分享使用 Deno 搭建第一个 Web 应用

发布于 1 年前21584
搭建 HTTP 服务
// server.ts
import { abc } from "https://deno.land/x/abc/mod.ts";
const app = abc();

app.start(":3000");

运行:

$ deno run -A server.ts

打开浏览器, 访问 http://127.0.0.1:3000, 你会看到页面显示 {"statusCode":404,"error":"Not Found"}.

加载静态资源

assets 目录下创建 index.html, 在里面随便写点内容, 然后更改 server.ts

// server.ts
import { abc } from "https://deno.land/x/abc/mod.ts";
const app = abc();

app.static("/static", "assets");

app.start(":3000");

访问 http://127.0.0.1:3000/static/index.html, 之前在 index.html 中键入的文件就会在页面中显示.

request & response
import { abc } from "https://deno.land/x/abc/mod.ts";
const app = abc();

const users = [
  {
    id: "1",
    username: "Li Lei"
  }
];

app
  .static("/static", "assets")
  .get("/user/", _ => users)
  .get("/user/:id", c => {
    const { id } = c.params;
        return `<h3>Hello, ${users.find(u => u.id === id).username}</h3>`;
  });

app.start(":3000");

访问 /user/ 显示 [{"id":"1","username":"Li Lei"}].

访问 /user/1 显示 Hello, Li Lei.

访问 /user/2 显示 {"statusCode":500,"error":"Internal Server Error","message":"Cannot read property 'username' of undefined"}, 这是因为并没有 id 为 2 的用户.

使用装饰器

很多时候我们需要对请求的内容做一些简单的处理, 这里提供一种简单的数据绑定的方法:

// tsconfig.json
{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}
import { abc, Binder } from "https://deno.land/x/abc/mod.ts";
const app = abc();

@Binder()
class UserDTO {
  constructor(
    public username: string,
    public password: string,
    public age: number
  ) {}
}

app
  .post("/user", async c => {
    const user = await c.bind(UserDTO);
    return user;
  })
  .start(":3000");

运行:

$ deno run -A -c tsconfig.json server.ts

这时, 当接收到的数据中的数据类型与 DTO 中定义类型不同时, 会返回错误的信息, 而多余的数据则会被过滤掉.

异常
import { Status } from "https://deno.land/std/http/http_status.ts";
import { abc } from "https://deno.land/x/abc/mod.ts";
import { HttpException } from "https://deno.land/x/abc/http_exception.ts";

const app = abc();

app
  .get("/admin", c => {
        throw new HttpException("Forbidden", Status.Forbidden);
  })
  .start(":3000");

当访问 /admin 时, 则会返回 {"statusCode":403,"message":"Forbidden"}. http_exception.ts 中内置了很多异常可以直接使用:

  • BadGatewayException
  • BadRequestException
  • ConflictException
  • ForbiddenException
  • GatewayTimeoutException
  • GoneException
  • TeapotException
  • MethodNotAllowedException
  • NotAcceptableException
  • NotFoundException
  • NotImplementedException
  • RequestEntityTooLargeException
  • RequestTimeoutException
  • ServiceUnavailableException
  • UnauthorizedException
  • UnprocessableEntityException
  • InternalServerErrorException
  • UnsupportedMediaTypeException
中间件

中间件是一个围绕路由处理程序调用的函数. 中间件函数可以访问 requestresponse 对象.

让我们从实现一个简单的中间件功能开始.

function track(next: HandlerFunc) {
  return function(c: Context) {
    console.log(`request to ${c.path}`);
    next(c);
  };
}

中间件分为以下级别:

  • 根级: 在路由处理请求之前执行根级中间件. 它可以通过 abc().pre() 注册.

  • 组级: 创建新组时, 你可以仅为该组注册中间件.

  • 路由级: 定义新路由时, 你可以选择仅为其注册中间件.

有些情况下, 你希望基于某些条件跳过中间件, 对此每个中间件都有一个选项来定义函数 skipper(c:Context):boolean.

abc().use(
  logger({
    skipper: c => {
      return c.path.startsWith("/skipper");
    }
  })
);
共有 2 条回复
登录后发表评论!
通过Github登录