处理器函数是用于处理 HTTP 请求的一个函数,它的签名如下:
type RequestHandler<CT extends RequestContext = RequestContext> = (
context: CT
) => Promise<void>;
可以看出它是一个 ES2017 规范里的 async function。
每一个 HTTP 请求都必须对应一个处理器函数,如果不想处理某个请求,也必须在路由器里面 指定一个 NOT FOUND 规则来处理这些不想处理的请求。否则服务器将不知道如何处理这个请求。
从签名可以看到,这个处理器函数有一个参数,叫 context,它是一个 http.RequestContext 类型的对象。整个处理器就是通过控制 context 对象来操作这个请求,并输出数据给请求的。
每个请求中都有一个 context
对象,它贯穿一个请求的整个生命周期。因为它包含的内容就是
请求的完整上下文,因此也称为请求上下文对象。它的结构如下:
export interface RequestContext {
/**
* 这个字段是一个请求控制对象,内含请求的基本信息。
*/
"request": ServerRequest;
/**
* 这个字段是一个响应控制对象,用于控制和输出响应的数据和 HTTP 头部数据。
*/
"response": ServerResponse;
/**
* 这是一个数据储存空间,用于储存请求过程中,需要在中间件和处理器函数间共享的
* 数据。
*
* 这里面的数据会在请求结束后被释放。
*/
"data": IDictionary<any>;
}
下面将通过几个例子来介绍处理器函数的基本用法。
通过 context.request.getContent 方法,即可读取 HTTP 请求的 Body 数据。
router.post("/users/{id:uint}", async function(ctx) {
try {
await ctx.request.getContent({"type": "raw"});
ctx.response.send("OK");
}
catch (e) {
ctx.response.writeHead(
http.HTTPStatus.BAD_REQUEST
);
ctx.response.send(e.message);
}
});
- HTTP.ServerRequest.getContent
通过 context.response.setHeader 即可设置响应头。也可以通过 writeHead 方法设置 并发送响应头。
router.get("/users/{id:uint}", async function(ctx) {
ctx.response.statusCode = http.HTTPStatus.OK;
ctx.response.statusMessage = "OK";
ctx.response.setHeader("Content-Type", "text/plain");
ctx.response.send("OK");
});
- HTTP.ServerResponse.setHeader
- HTTP.ServerResponse.writeHead
主要是用过 context.request 对象的各个字段获取请求的信息:
字段 | 用途 |
---|---|
context.request.host |
请求的域名(与端口) |
context.request.https |
是否 HTTPS 请求 |
context.request.url |
请求的 URL(不包含 origin) |
context.request.time |
服务器收到请求的时间(时间戳) |
context.request.path |
请求的路径(不包含 QueryString) |
context.request.query |
QueryString 解码后的对象,是个哈希表 |
context.request.queryString |
请求的 QueryString |
context.request.server |
处理当前请求的服务器对象 |
context.request.aborted |
链接是否已经被中断 |
context.request.closed |
链接是否已经被关闭 |
context.request.ip |
获取客户端 IP 地址 |
router.get("/client", async function(ctx) {
ctx.response.statusCode = http.HTTPStatus.OKOK;
ctx.response.statusMessage = "OK";
ctx.response.setHeader("Content-Type", "text/plain");
let content = `
IP Address: ${ctx.request.ip}
HTTPS: ${ctx.request.https ? "Yes" : "No"}
Request Host: ${ctx.request.host}
Request URL: ${ctx.request.url}
Request Path: ${ctx.request.path}
Request Search: ${ctx.request.queryString}
Request Time: ${new Date(ctx.request.time)}
Request Query: ${JSON.stringify(ctx.request.query)}
`;
ctx.response.send(content);
});
- HTTP.ServerResponse
在 JavaScript 中,可以通过 throw 抛出异常。而在 async function 中。如果你想往上抛出 异常,交由中间件去处理,可以写作如下:
router.get("/error", async function(ctx) {
// 在 async function 中,返回一个 Rejected Promise 就是向上抛出异常。
return Promise.reject(new Error("Test"));
});