今天介绍好朋友写的一个基于装饰器&元数据反射实现的 http server 框架——http-yyds。
下面的文章转载自他的知乎「善良超哥哥」
,可以点击文末的阅读原文直接跳转。
知乎主页地址:https://www.zhihu.com/people/shimachao
一个注解风格的 TypeScript HTTP Server。适用于封装 API 或个人小项目。目前还未到 1.0.0 版本,切勿用在生产环境。
安装
npm install http-yyds
项目配置
因为使用了装饰器和元数据反射,需要在 tsconfig.json
中加入以下开启以下配置项:
{
"compilerOptions": {
"target": "es2021",
"module": "esnext",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node"
}
}
示例
import { App } from "http-yyds";
let app = new App("127.0.0.1", 8080);
class Test {
@app.get("/hello")
hello(name: string) {
return `Hello ${name}!`;
}
@app.get("/person/{name}/sex")
getCurTime(name: string) {
if (name == "xiaoming") {
return `man`;
} else if (name == "xiaohong") {
return "woman";
} else {
return "unknown";
}
}
@app.post("/cal/sum")
sum(a: number, b: number) {
return `${a}+${b}=${a + b}`;
}
@app.get("/api/person/{name}/info")
getPersion(name: string) {
if (name == "xiaoming") {
return { sex: "man", name: "xiaoming", age: 18 };
} else if (name == "xiaohong") {
return { sex: "woman", name: "xiaohong", age: 19 };
} else {
throw new Error(`${name} 的信息不存在`);
}
}
}
app.listen();
功能
1.用注解映射请求路径和响应方法
http-yyds 提供的注解 API 可以根据请求路径和请求方法将请求映射到响应方法,方法可以是同步方法,也可以是异步方法。
例如,下面将路径 "/hello" 的 GET 请求映射到方法 hello(name:string)
。
@app.get("/hello")
hello(name: string) {
return `Hello ${name}!`;
}
还可以将多个请求路径映射到同一个响应方法,只要叠加注解就行:
@app.get("/")
@app.get("/index")
getIndex() {
// 省略。。。
}
2.支持 HTTP GET、POST、PUT、DELETE
App 类内置了以下方法注解,用于支持常用的 HTTP 请求方法。
interface App {
get(path: string);
post(path: string);
put(path: string);
delete(path: string);
}
参数 path
表示请求的路径。
对于没有内置支持的 HTTP 请求方法,未来会提供一个扩展方法 mapping(path: string, method: string)
让用户自定定义方法注解。
3.自动提取和填充参数
http-yyds 会自动从请求中提取参数,并将参数填充为响应方法中的同名实参。
(1)URL 中的查询参数
例如前面的示例中的代码片段:
@app.get("/hello")
hello(name: string) {
return `Hello ${name}!`;
}
收到请求时会自动提取请求 URL 中的查询参数:
$ curl http://127.0.0.1:8080/hello?name=xiaoming
hello xiaoming!
(2)路径参数
例如前面的示例中的代码片段:
@app.get("/person/{name}/sex")
getCurTime(name: string) {
if (name == "xiaoming") {
return `man`;
} else if (name == "xiaohong") {
return "woman";
} else {
return "unknown";
}
}
收到请求后会自动将路径中的 name
参数:
$ curl http://127.0.0.1:8080/person/xiaoming/sex
man
$ curl http://127.0.0.1:8080/person/xiaohong/sex
woman
$ curl http://127.0.0.1:8080/person/xiaohong/sex
woman
$ curl http://127.0.0.1:8080/person/unknown/sex
unknown
(3)POST 请求体中的参数
例如前面的示例中的代码片段:
@app.post("/cal/sum")
sum(a: number, b: number) {
return `${a}+${b}=${a + b}`;
}
(4)application/x-www-form-urlencoded
格式的参数
$ curl -d 'a=1&b=2' http://127.0.0.1:8080/cal/sum
1+2=3
(5)application/json
格式的参数
$ curl -H 'Content-Type: application/json' -d '{"a":1,"b":2}' http://127.0.0.1:8080/cal/sum
1+2=3
4.自动处理响应函数的返回
http-yyds 会自动将响应方法的返回封装成 HTTP 响应。
将字符串类型的返回转成 text/plain
类型的 HTTP 响应:
@app.get("/person/{name}/sex")
getCurTime(name: string) {
if (name == "xiaoming") {
return `man`;
} else if (name == "xiaohong") {
return "woman";
} else {
return "unknown";
}
}
$ curl -i http://127.0.0.1:8080/person/xiaohong/sex HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Date: Fri, 07 Jan 2022 12:22:44 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
woman
将对象类型的响应转成 application/json
类型的 HTTP 响应:
@app.get("/api/person/{name}/info")
getPersion(name: string) {
if (name == "xiaoming") {
return { sex: "man", name: "xiaoming", age: 18 };
} else if (name == "xiaohong") {
return { sex: "woman", name: "xiaohong", age: 19 };
} else {
throw new Error(`${name} 的信息不存在`);
}
}
$ curl -i http://127.0.0.1:8080/api/person/xiaoming/info
HTTP/1.1 200 OK
Content-Type: application/json
Date: Fri, 07 Jan 2022 12:40:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
{"sex":"man","name":"xiaoming","age":18}
如果响应函数抛出异常,也能将其转成 500 响应返回:
$ curl -i http://127.0.0.1:8080/api/person/xxx/info
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Date: Fri, 07 Jan 2022 12:40:51 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
{"message":"服务内部出错:Error: xxx 的信息不存在"}
如果自动返回不满足需求,http-yyds 还提供了一个 Response
类用于自定义返回:
例如,下面返回一个 html 文件:
import { App, Response } from "http-yyds";
@app.get("/")
@app.get("/index")
getIndex() {
return new Response(
200,
"OK",
{ "Content-Type": contentType },
"<h1>Hello World!</h1>"
);
}
5.自动处理错误请求
如果请求路径不存在对应的响应方法,则自动返回 404:
$ curl -i http://127.0.0.1:8080/api/xx
HTTP/1.1 404 Not Found
Content-Type: application/json
Date: Fri, 07 Jan 2022 12:45:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
{"message":"未找到/api/xx对应的资源"}
如果请求对应的路径存在响应方法,但是不特定 HTTP 请求方法,则自动返回 405 响应:
$ curl -i http://127.0.0.1:8080/cal/sum
HTTP/1.1 405 Method Not Allowed
Allow: POST
Content-Type: application/json
Date: Fri, 07 Jan 2022 12:47:23 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
{"message":"/cal/sum对应的资源不支持GET"}
未来会支持更多类型的自动响应。
todo
完成面向切面的拦截器
提供映射基础路径和处理器类的注解
支持用户自己实例化处理器类
注意
在未发布 1.0.0 版本前,http-yyds 的接口会随着作者的经验和品味变化。
http-yyds 基于的装饰器和元数据反射都处于”建议征集的第二阶段“,不知道什么时候出现在正式标准中。
毕竟是个人作品,在发布 1.0.0 之前不要用于生产环境。
本文所有赞赏归文章原作者所有。