Criollo 1.1.0

Criollo 1.1.0

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
发布最新发布2022年9月

Cătălin Stan维护。



Criollo 1.1.0

Criollo

一款强大的macS、iOS和tvOS Cocoa Web框架和HTTP服务器。

Build Status Version Status Platform Carthage compatible MIT License Twitter Gitter

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下的012键中。
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资源文件构建视图对象。这通过分别使用CRViewConrollerCRViewCRNib 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公开目录和单个文件的支持。通过使用CRStaticFileManagerCRStaticDirectoryManager 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”示例,了解两种使用模式。

入门

安装

推荐通过 CocoaPods 来安装 Criollo。然而,您也可以手动将框架嵌入到项目中。

使用CocoaPods安装

  1. 如在项目中尚未创建,请创建 Podfile。您可以在项目文件夹中运行 pod init 来完成此操作。
  2. 将 Criollo 添加到您的 Podfile 中。 pod 'Criollo', '~> 0.5'
  3. 运行 pod install

请注意,Criollo 将下载 CocoaAsyncSocket 作为依赖项。

克隆仓库

Criollo 使用 CocoaAsyncSocket,它作为 git 子模块包含其中。

git clone --recursive https://github.com/thecatalinstan/Criollo.git

联系信息

如果您对项目有任何疑问,或需要用其完成任何事情,请随时通过 Twitter @criolloio 或普通电子邮件 [email protected] 联系。

我真的非常鼓励您 提交问题,因为您的反馈真的非常感谢。


查看Criollo博客以获取有关Criollo的最新新闻、想法和更新。