一个基于装饰器&元数据反射实现的 http server 框架

Kotlin实战Android

共 5472字,需浏览 11分钟

 · 2022-01-11

今天介绍好朋友写的一个基于装饰器&元数据反射实现的 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 },
       "

Hello World!

"

  );
}
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 之前不要用于生产环境。



本文所有赞赏归文章原作者所有。


浏览 19
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报