API的通用形式
路由
一个API可以包含多个“路由”,每个“路由”看起来就像文件系统中的一个目录或文件一样。每个路由节点都可以执行不同的操作。比如一个“查看用户清单”的路由与一个“创建新用户”的路由肯定是不一样的。
在一个API中,这些路由往往不是在真实的目录和文件下存在的,而是指向了一些函数。这些“目录结构”暗示了这些函数的逻辑分组。比如:
/api/v1/users/list
/api/v1/users/detail
/api/v1/users/create
/api/v1/users/modify
/api/v1/users/delete
/api/v1/companies/list
/api/v1/companies/detail
/api/v1/companies/create
/api/v1/companies/modify
/api/v1/companies/delete
这个例子描述了一个典型的“CRUD”系统——即Create创建、Read读取、Update更新和Delete删除。两组内容函数的区别貌似仅仅是在users
用户和companies
公司着两个实体上,但是其实它们指向的是完全不同的函数。
HTTP动作
HTTP动作是一个网页浏览器或移动应用端向路由发出请求的额外信息。这些动作能够向API服务器提供数据接收时需要的“上下文”信息。
常见HTTP动作包括
GET
GET方法向特定路由发出请求,其中从客户端发向服务器的所有参数包含在URL字符串中。使用GET方法发出的请求只能接收数据,不能由其它作用。使用GET请求来删除数据库的一条记录是可行的,但是不推荐这么做。
POST
POST方法通常用于向数据库内发送创建一条新记录的表单,比如在一个网站上填写好一个表单后提交给服务器。每个表单中都有成对出现的字段名和字段值,便于API服务器读取这些变量信息。
PATCH
PATCH方法整体上来说和POST差不多,但是用于更新记录,而不是新建记录。
PUT
PUT方法主要用于上传文件,一个典型的PUT请求通常会在其消息体内部包含一个即将发给服务器的文件。
DELETE
DELETE请求用于通知API服务器删除特定资源。通常在其URL中会包含一个唯一的标识信息。
使用HTTP动作和路由来简化一个API
当综合使用时,每一个HTTP请求的这两个组成部分能够有效降低API的复杂性。
查看以下包含了HTTP动作的URL结构,您会发现其内容和形式都要简单多了、也更明确了:
GET /api/v1/users
GET /api/v1/users/5d096846-a000-43db-b6c5-a5883135d71d
POST /api/v1/users
PATCH /api/v1/users/5d096846-a000-43db-b6c5-a5883135d71d
DELETE /api/v1/users/5d096846-a000-43db-b6c5-a5883135d71d
上述例子给人第一感觉就是都指向了同一个路由:/api/v1/users。但是每一个路由都执行一个不同的操作。类似的,头两个GET路由的区别是第二个GET的后缀带了一个用户id编码作为参数,与之前的例子有所差别。这种命令通常会被一个在路由设置中的“通配符”获取。
HTTP路由
HTTP请求/响应路由是用于决定在当前请求下,哪一个句柄去接收和响应。句柄可以是一个函数、过程或者方法,只要能够接收特定类型的请求并做出反应即可。路由主要依据请求的方法“HTTP request method”和请求内容包括的路径信息来决定的。一个路由就是“HTTP method”方法、路径和句柄的组合。路由需要在服务器启动并开始监听请求或调度信号之前注册到服务器上。
GET
router.get("vapor") { req in
return "vapor release";
}
router.get("hello", "vapor") { (req) -> String in
return "hello vapor";
}
router.get("hello", String.parameter) { (req) -> String in
let name = try req.parameters.next(String.self)
return "hello, \(name)";
}
/// Creates a `Route` at the provided path using the `GET` method.
///
/// router.get("hello", "world") { req in
/// return "Hello, world!"
/// }
///
/// The above route closure would return `"Hello, world"` to requests to `GET /hello/world`.
///
/// You can use anything `PathComponentsRepresentable` to create the path, including dynamic parameters.
///
/// router.get("users", Int.parameter) { req in
/// let id = try req.parameters.next(Int.self)
/// return "User #\(id)"
/// }
///
/// See `ParametersContainer` for more information on using dynamic parameters.
///
/// - parameters:
/// - path: Variadic `PathComponentsRepresentable` items.
/// - closure: Creates a `Response` for the incoming `Request`.
/// - returns: Discardable `Route` that was just created.
@discardableResult
public func get<T>(_ path: PathComponentsRepresentable..., use closure: @escaping (Request) throws -> T) -> Route<Responder>
where T: ResponseEncodable
{
return _on(.GET, at: path.convertToPathComponents(), use: closure)
}
POST
//POST返回字符串
router.post(InfoData.self, at: "info") { (req, data) -> String in
return "hello, \(data.name)";
}
//POST返回JSON
router.post(InfoData.self, at: "infodata") { (req, data) -> InfoResponse in
return InfoResponse(request: data);
}
struct InfoData: Content {
let name: String
}
struct InfoResponse: Content {
let request: InfoData
}
/// Creates a `Route` that automatically decodes `Content` at the provided path using the `POST` method.
///
/// router.post(User.self, at: "users") { req, user in
/// print(user) // User
/// // create user and return response...
/// }
///
/// The above route closure would automatically decode a `User` to requests to `POST /users`.
///
/// See `ParametersContainer` for more information on using dynamic parameters.
///
/// - parameters:
/// - content: `Content` type to automatically decode.
/// - path: Variadic `PathComponentsRepresentable` items.
/// - closure: Creates a `Response` for the incoming `Request`.
/// - returns: Discardable `Route` that was just created.
@discardableResult
public func post<C, T>(_ content: C.Type, at path: PathComponentsRepresentable..., use closure: @escaping (Request, C) throws -> T) -> Route<Responder>
where C: RequestDecodable, T: ResponseEncodable
{
return _on(.POST, at: path.convertToPathComponents(), use: closure)
}
网友评论