一款强大的macS、iOS和tvOS Cocoa Web框架和HTTP服务器。
Criollo可以帮助您创建快速的单例或嵌入式Web应用,直接通过HTTP或FastCGI提供内容。您可以使用Swift或Objective-C编写代码,并且可以使用您已经熟悉的Cocoa技术。
就像这样简单
let server = CRHTTPServer()
server.get("/") { (req, res, next) in
res.send("Hello world!")
}
server.startListening()
... 以及在Objective-C中的代码
CRServer* server = [[CRHTTPServer alloc] init];
[server get:@"/" block:^(CRRequest *req, CRResponse *res, CRRouteCompletionBlock next) {
[res send:@"Hello world!"];
}];
[server startListening];
主要特性
Criollo的设计考虑到速度、安全和灵活性,因此它包含了一些非常实用的特性,这样您就可以专注于项目实际需要完成的任务,而不必为完成任务而绕很多弯路。
HTTPS
Criollo在所有平台上都完全支持HTTPS。您可以将凭据作为PKCS#12身份和密码,或者X509证书和私钥对传递,无论是PEM还是DER编码。
server.isSecure = true
// Credentials: PKCS#12 Identity and password
server.identityPath = Bundle.main.path(forResource: "identity", ofType: "p12")
server.password = "123456"
// Credentials: PEM-encoded certificate and public key
server.certificatePath = Bundle.main.path(forResource: "certificate", ofType: "pem")
server.privateKeyPath = Bundle.main.path(forResource: "key", ofType: "pem")
// Credentials: DER-encoded certificate and public key
server.certificatePath = Bundle.main.path(forResource: "certificate", ofType: "der")
server.privateKeyPath = Bundle.main.path(forResource: "key", ofType: "der")
... 以及在Objective-C中的代码
server.isSecure = YES;
// Credentials: PKCS#12 Identity and password
server.identityPath = [NSBundle.mainBundle pathForResource:@"identity" ofType:@"p12"];
server.password = @"password";
// Credentials: PEM-encoded certificate and public key
server.certificatePath = [NSBundle.mainBundle pathForResource:@"certificate" ofType:@"pem"];
server.privateKeyPath = [NSBundle.mainBundle pathForResource:@"key" ofType:@"pem"];
// Credentials: DER-encoded certificate and public key
server.certificatePath = [NSBundle.mainBundle pathForResource:@"certificate" ofType:@"der"];
server.privateKeyPath = [NSBundle.mainBundle pathForResource:@"key" ofType:@"der"];
路由
定义路由时,路径可以以三种方式指定:
- 固定字符串(例如
/api
)。这会精确匹配该字符串。 - 占位符(例如
/posts/:pid
)。紧接在/posts
之后的路径段将被匹配并添加到request.query
下的pid
键中。 - 正则表达式模式(例如
/[0-9]{4}/[0-9]{1,2}/[a-zA-Z0-9-]+
)。当这三个模式匹配时,它们将分别添加到request.query
下的0
、1
和2
键中。
server.add("/api") { (req, res, next) in
// /api/?pid=12345
res.send(req.query)
}
server.add("/posts/:pid") { (req, res, next) in
// /posts/12345
res.send(req.query)
}
server.add("/[0-9]{4}/[0-9]{1,2}/[a-zA-Z0-9-]+") { (req, res, next) in
// /2017/10/my-first-criollo-app
res.send(req.query)
}
... 以及在Objective-C中的代码
[server add:@"/api" block:^(CRRequest *req, CRResponse *res, CRRouteCompletionBlock next) {
// /api/?pid=12345
[res send:req];
}];
[server add:@"/posts/:pid" block:^(CRRequest *req, CRResponse *res, CRRouteCompletionBlock next) {
// /posts/12345
[res send:req];
}];
[server add:@"/[0-9]{4}/[0-9]{1,2}/[a-zA-Z0-9-]+" block:^(CRRequest *req, CRResponse *res, CRRouteCompletionBlock next) {
// /2017/10/my-first-criollo-app
[res send:req];
}];
控制器
控制器提供了一种非常简单地将功能分组到一个语义单元中的方法。它们作为路由器,允许您根据它们自身附加的路径定义基于路径的路由。
// The controller class
class APIController : CRRouteController {
override init(prefix: String) {
super.init(prefix: prefix)
self.add("/status") { (req, res, next) in
res.send(["status": true])
}
}
}
// Add the controller to the server
server.add("/api", controller:APIController.self)
... 以及在Objective-C中的代码
// The controller class
@interface APIController : CRRouteController
@end
@implementation APIController
- (instancetype)initWithPrefix:(NSString *)prefix {
self = [super initWithPrefix:prefix];
if ( self != nil ) {
[self add:@"/status" block:^(CRRequest *req, CRResponse *res, CRRouteCompletionBlock next) {
[res send:@{@"status": @YES}];
}];
}
}
@end
// Add the controller to the server
[server add:@"/api" controller:APIController.class];
视图和视图控制器
视图控制器通过调用视图的render
方法,使用HTML资源文件构建视图对象。这通过分别使用CRViewConroller
、CRView
和CRNib
API来实现。
视图控制器是功能强大的对象,让您可以轻松地标准化应用程序的外观并将功能分组到逻辑单元中。
HTML模板文件
<!-- Define some placeholders -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
<p>{{content}}</p>
</body>
</html>
源代码
// The view controller class
class HelloWorldViewController: CRViewController {
override func present(with request: CRRequest, response: CRResponse) -> String {
self.vars["title"] = String(describing: type(of: self))
self.vars["content"] = "Hello from the view controller."
return super.present(with: request, response: response)
}
}
// Add the view controller to server
server.add("/controller", viewController: HelloWorldViewController.self, withNibName: "HelloWorldViewController", bundle: nil)
... 以及在Objective-C中的代码
// The view controller class
@interface HelloWorldViewController : CRViewController
@end
@implementation HelloWorldViewController
- (NSString *)presentViewControllerWithRequest:(CRRequest *)request response:(CRResponse *)response {
self.vars[@"title"] = NSStringFromClass(self.class);
self.vars[@"content"] = @"Hello from the view controller.";
return [super presentViewControllerWithRequest:request response:response];
}
@end
// Add the view controller to server
[server add:@"/controller" viewController:HelloWorldViewController.class withNibName:@"HelloWorldViewController" bundle:nil];
静态文件/目录服务
Criollo内置了对同时通过HTTP公开目录和单个文件的支持。通过使用CRStaticFileManager
和CRStaticDirectoryManager
API,您可以实现这一点。
// Expose the home directory (with auto-indexing)
server.mount("/pub", directoryAtPath: "~", options: [.autoIndex])
// Serve a single static file at a path
server.mount("/info.plist", fileAtPath: Bundle.main.bundlePath.appending("/Info.plist"))
... 以及在Objective-C中的代码
// Expose the home directory (with auto-indexing)
[server mount:@"/pub" directoryAtPath:@"~" options:CRStaticDirectoryServingOptionsAutoIndex];
// Serve a single static file at a path
[server mount:@"/info.plist" fileAtPath:[NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"/Contents/Info.plist"]];
多部分文件上传
Criollo内置了对处理multipart/form-data
POST请求的支持,因此您可以轻松地处理HTML文件上传。上传的文件以request.files
的形式提供,作为CRUploadedFile
对象数组。
// Serve the first uploaded file back to the client
self.server.post("/image") { (req, res, next) in
do {
let data = try Data.init(contentsOf: (req.files!["0"]?.temporaryFileURL)!)
res.setValue(req.env["HTTP_CONTENT_TYPE"]!, forHTTPHeaderField: "Content-type")
res.setValue("\(data.count)", forHTTPHeaderField: "Content-Length")
res.send(data)
} catch {
res.setValue("text/plain", forHTTPHeaderField: "Content-type")
res.setValue("\(error.localizedDescription.count)", forHTTPHeaderField: "Content-length")
res.send(error.localizedDescription)
}
}
... 以及在Objective-C中的代码
// Serve the first uploaded file back to the client
[server post:@"/image" block:^(CRRequest *req, CRResponse *res, CRRouteCompletionBlock next) {
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:req[0].temporaryFileURL options:0 error:&error];
if ( error ) {
[res setValue:@"text/plain" forHTTPHeaderField:@"Content-type"];
[res setValue:@(error.description.length).stringValue forHTTPHeaderField:@"Content-length"];
[res sendString:error.description];
} else {
[res setValue:request.env[@"HTTP_CONTENT_TYPE"] forHTTPHeaderField:@"Content-type"];
[res setValue:@(data.length).stringValue forHTTPHeaderField:@"Content-length"];
[res sendData:data];
}
}];
为什么?
Criollo是为了利用苹果堆栈提供的真正出色的工具和API而创建的,以便通过网页提供由它们生成的内容。
它包含一个HTTP web服务器和一个FastCGI应用程序服务器,用于提供内容。该服务器基于Grand Central Dispatch构建,并设计用于速度。
如何使用
Criollo可以轻松地嵌入到您的macOS、iOS或tvOS应用程序中作为web服务器,如果您需要这样功能,然而,它被设计为创建独立的长寿命守护进程式应用程序。它与launchd
完全兼容,并复制了NSApplication
的生命周期和行为,使得学习曲线尽可能地平滑。
为了更实际的示例,请查看使用Criollo制作的criollo.io网站,可在https://github.com/thecatalinstan/Criollo-Web处克隆。
查看“Hello World Multi Target”示例,了解两种使用模式。
入门
- 阅读“入门”指南,并继续阅读“做更多事情”指南
- 查看示例
- 查看API参考以查看可用的API
- 学习如何在“部署”指南中部署您的Criollo应用程序
- 查看Criollo博客以获取有关Criollo的最新新闻、想法和更新。
安装
推荐通过 CocoaPods 来安装 Criollo。然而,您也可以手动将框架嵌入到项目中。
使用CocoaPods安装
- 如在项目中尚未创建,请创建
Podfile
。您可以在项目文件夹中运行pod init
来完成此操作。 - 将 Criollo 添加到您的
Podfile
中。pod 'Criollo', '~> 0.5'
- 运行
pod install
请注意,Criollo 将下载 CocoaAsyncSocket 作为依赖项。
克隆仓库
Criollo 使用 CocoaAsyncSocket,它作为 git 子模块包含其中。
git clone --recursive https://github.com/thecatalinstan/Criollo.git
联系信息
如果您对项目有任何疑问,或需要用其完成任何事情,请随时通过 Twitter @criolloio 或普通电子邮件 [email protected] 联系。
我真的非常鼓励您 提交问题,因为您的反馈真的非常感谢。
查看Criollo博客以获取有关Criollo的最新新闻、想法和更新。