Skip to main content
On this page

@std/http

概览 Jump to heading

在 Deno 原生 HTTP 服务器之上提供用户友好的 serve,以及其他用于创建 HTTP 服务器和客户端的实用工具。

文件服务器

一个用于通过 HTTP 提供本地文件的小程序。

deno run --allow-net --allow-read jsr:@std/http/file-server
监听于:
- 本地: http://localhost:8000

当提供 --allow-sys=networkInterfaces 权限时,文件服务器还会显示可用于访问服务器的局域网地址。

HTTP 状态码和状态文本

用于处理状态码和状态文本的辅助工具。

HTTP 错误

提供针对每个 HTTP 错误状态码的错误类,以及以结构化方式处理 HTTP 错误的实用函数。

方法

提供辅助函数和类型,用于安全地处理 HTTP 方法字符串。

协商

一组函数,可用于在响应请求时协商内容类型、编码和语言。

注意:某些库通过分析 Accept-Charset 头包含字符集接受功能。该头是一个遗留头,客户端会省略,服务器应忽略,因此本库不提供该功能。

用户代理处理

UserAgent 类提供用户代理字符串解析,允许用户代理标志被语义化理解。

例如,集成 HTTP 请求头中的 User-Agent,如下示例:

import { UserAgent } from "@std/http/user-agent";

Deno.serve((req) => {
  const userAgent = new UserAgent(req.headers.get("user-agent") ?? "");
  return new Response(`Hello, ${userAgent.browser.name}
    on ${userAgent.os.name} ${userAgent.os.version}!`);
});

路由

route 提供了一种简便方式,根据请求路径和方法将请求路由至不同的处理器。

import { route, type Route } from "@std/http/unstable-route";
import { serveDir } from "@std/http/file-server";

const routes: Route[] = [
  {
    pattern: new URLPattern({ pathname: "/about" }),
    handler: () => new Response("关于页面"),
  },
  {
    pattern: new URLPattern({ pathname: "/users/:id" }),
    handler: (_req, _info, params) => new Response(params?.pathname.groups.id),
  },
  {
    pattern: new URLPattern({ pathname: "/static/*" }),
    handler: (req: Request) => serveDir(req)
  },
  {
    method: ["GET", "HEAD"],
    pattern: new URLPattern({ pathname: "/api" }),
    handler: (req: Request) => new Response(req.method === 'HEAD' ? null : 'ok'),
  },
];

function defaultHandler(_req: Request) {
  return new Response("未找到", { status: 404 });
}

Deno.serve(route(routes, defaultHandler));

添加到你的项目 Jump to heading

deno add jsr:@std/http

查看 @std/http 中的所有符号

为什么使用 @std/http? Jump to heading

非常适合使用 Web Fetch API 风格构建小型、快速的 HTTP 服务器和处理器。

示例 Jump to heading

一个极简服务器

import { serve } from "@std/http";

serve((_req) => new Response("Hello"), { port: 8000 });

条件 GET 与 ETag(304 未修改)

import { serve } from "@std/http";
import { eTag, ifNoneMatch } from "@std/http/etag";

const body = JSON.stringify({ message: "Hello, cached world" });
const etag = eTag(body);

serve((req) => {
  const inm = req.headers.get("if-none-match");
  // ifNoneMatch 传回 false 说明标签匹配 -> 响应 304
  if (!ifNoneMatch(inm, etag)) {
    return new Response(null, { status: 304, headers: { ETag: etag } });
  }
  return new Response(body, {
    headers: { "content-type": "application/json; charset=utf-8", ETag: etag },
  });
});

内容协商(HTML 与 JSON)

import { serve } from "@std/http";
import { accepts } from "@std/http/negotiation";

serve((req) => {
  const preferred = accepts(req) ?? ["*/*"];
  if (preferred.includes("application/json") || preferred.includes("*/*")) {
    return Response.json({ ok: true });
  }
  return new Response("<h1>ok</h1>", {
    headers: { "content-type": "text/html; charset=utf-8" },
  });
});

Cookie:设置、读取与删除

import { serve } from "@std/http";
import { deleteCookie, getCookies, setCookie } from "@std/http/cookie";

serve(async (req) => {
  const url = new URL(req.url);
  const headers = new Headers();

  if (url.pathname === "/login" && req.method === "POST") {
    // 实际场景请先验证凭证
    setCookie(headers, {
      name: "sid",
      value: crypto.randomUUID(),
      httpOnly: true,
      secure: true,
      sameSite: "Lax",
      path: "/",
      maxAge: 60 * 60, // 1 小时
    });
    return new Response("ok", { headers });
  }

  if (url.pathname === "/me") {
    const cookies = getCookies(req.headers);
    const sid = cookies["sid"] ?? "(无)";
    return Response.json({ sid });
  }

  if (url.pathname === "/logout") {
    deleteCookie(headers, "sid", { path: "/" });
    return new Response("bye", { headers });
  }

  return new Response("未找到", { status: 404 });
});

使用 serveDir 提供静态文件和跨域资源共享(CORS)

import { serve } from "@std/http";
import { serveDir } from "@std/http/file-server";

// 需要为你的公共目录授予 --allow-read 权限
serve((req) =>
  serveDir(req, {
    fsRoot: "public",
    showDirListing: true,
    enableCors: true, // 添加基本的 Access-Control-* 头
    urlRoot: "/static",
  })
);

使用服务端发送事件 (SSE) 流式更新

import { serve } from "@std/http";
import { ServerSentEventStream } from "@std/http/server-sent-event-stream";

serve((_req) => {
  const { readable, writable } = new TransformStream();
  const sse = new ServerSentEventStream(writable);

  let i = 0;
  const timer = setInterval(() => {
    sse.dispatchMessage({
      event: "tick",
      id: String(i),
      data: new Date().toISOString(),
    });
    i++;
    if (i === 5) { // 发送 5 条消息后停止
      clearInterval(timer);
      sse.close();
    }
  }, 1000);

  return new Response(readable, {
    headers: {
      "content-type": "text/event-stream",
      "cache-control": "no-cache",
      "connection": "keep-alive",
    },
  });
});

提供单个文件服务(支持 Range 请求)

import { serve } from "@std/http";
import { serveFile } from "@std/http/file-server";

serve((req) => {
  const url = new URL(req.url);
  if (url.pathname === "/video") {
    return serveFile(req, "static/video.mp4");
  }
  return new Response("未找到", { status: 404 });
});

小贴士 Jump to heading

  • 处理器签名为 (req: Request) => Response | Promise<Response>
  • 与标准的 URLHeadersRequest 结合使用,方便解析与响应。

你找到了你需要的东西吗?

编辑此页面
隐私政策