千家信息网

如何让deno支持HTTP服务

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,这篇文章主要介绍"如何让deno支持HTTP服务",在日常操作中,相信很多人在如何让deno支持HTTP服务问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何让deno
千家信息网最后更新 2025年11月07日如何让deno支持HTTP服务

这篇文章主要介绍"如何让deno支持HTTP服务",在日常操作中,相信很多人在如何让deno支持HTTP服务问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何让deno支持HTTP服务"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

现在的 deno 只支持很少的几个功能, 并不支持搭建 HTTP 服务, 如果想要用 deno 搭建 HTTP 服务要怎么办呢?

只能自己进行开发支持

代码如下:

// helloServer.ts

import { Request, Response, createHttpServer } from "deno";

const server = createHttpServer((req: Request, res: Response) => {

res.write(——[${req.method}] ${req.path} Hello world!——);

res.end();

});

server.listen(3000);

上面是我们期望创建服务器的代码, 接下来我们根据这段代码一点点实现

Request, Response, createHttpServer

上面说过, deno 现在并没有这些类和方法, 我们要构建这些对象和方法。

注: 这里并不是要写一个功能完善的模块, 有很多东西我都会省略掉

// http.ts

import { main as pb } from "./msg.pb";

import { pubInternal, sub } from "./dispatch";

const enc = new TextEncoder();

const servers: {[key: number]: HttpServer} = {};

export class Request {

method: string;

path: string;

constructor(msg: pb.Msg) {

this.path = msg.httpReqPath;

this.method = msg.httpReqMethod;

}

}

export class Response{

requestChannel: string;

constructor(msg: pb.Msg) {

this.requestChannel = ——http/${msg.httpReqId}——;

}

}

let serverId = 0;

export class HttpServer {

port: number;

private id: number;

private requestListener: (req: Request, res: Response) => void;

constructor(requestListener: (req: Request, res: Response) => void) {

this.requestListener = requestListener;

this.id = serverId ++;

servers[this.id] = this;

}

}

export function createHttpServer(

requestListener: (req: Request, res: Response) => void

): HttpServer {

const server = new HttpServer(requestListener);

return server;

}

在根目录创建 http.ts , 在其中进行定义。

Request 中有 method、path 两个属性, 简单起见, 浏览器请求中还有 body、header 等等其他实际中会用到的属性我都忽略了。

Response 中 requestChannel 是用于通过 deno 订阅/发布模式返回结果的, 后面能看到具体什么用。

HttpServer 中包括绑定的端口 port, 在构造函数中, 生成对 HttpServer 生成实例进行标识的 id, 及绑定对请求进行处理的函数 requestListener。

方法 createHttpServer 则是用 requestListener 创建 server 实例

server.listen

在有了 HttpServer 也绑定了 requestListenner 之后, 要监听端口

// http.ts

...

export class HttpServer {

...

listen(port: number) {

this.port = port;

pubInternal("http", {

command: pb.Msg.Command.HTTP_SERVER_LISTEN,

httpListenPort: port,

httpListenId: this.id

});

}

}

...

其中, pubInternal 方法需要两个参数 channel 和 msgObj, 上面的代码就是将监听端口命令及所需的配置发布到 Golang 代码中 http 这个频道。

// msg.proto

...

message Msg {

enum Command {

...

HTTP_RES_WRITE = 14;

HTTP_RES_END = 15;

HTTP_SERVER_LISTEN = 16;

}

...

// HTTP

int32 http_listen_port = 140;

int32 http_listen_id = 141;

bytes http_res_write_data = 142;

int32 http_server_id = 143;

string http_req_path = 144;

string http_req_method = 145;

int32 http_req_id = 146;

}

...

在 msg.proto 文件(protobuf 的定义文件)中对需要用到的 Command 以及 Msg 的属性进行定义, 需要注意的是, 属性值需要使用下划线命名, 在编译 deno 时会会根据这个文件生成对应的 msg.pb.d.ts、msg.pb.js 及 msg.pb.go 分别让 ts 及 Golang 代码使用, 这里对后续需要用到的定义都展示了, 后面不再赘述。

// http.go

package deno

import (

"fmt"

"net/http"

"github.com/golang/protobuf/proto"

)

var servers = make(map[int32]*http.Server)

func InitHTTP() {

Sub("http", func(buf []byte) []byte {

msg := &Msg{}

check(proto.Unmarshal(buf, msg))

switch msg.Command {

case Msg_HTTP_SERVER_LISTEN:

httpListen(msg.HttpListenId, msg.HttpListenPort)

default:

panic("[http] unsupport message " + string(buf))

}

return nil

})

}

func httpListen(serverID int32, port int32) {

handler := buildHTTPHandler(serverID)

server := &http.Server{

Addr: fmt.Sprintf(":%d", port),

Handler: http.HandlerFunc(handler),

}

servers[serverID] = server

wg.Add(1)

go func() {

server.ListenAndServe()

wg.Done()

}()

}

同样在根目录创建 http.go文件。

InitHTTP 中订阅 http channel, 在传入的 msg.command 为 Msg_HTTP_SERVER_LISTEN 时调用 httpListen 进行端口监听(还记得之前 msg.proto 中定义的枚举 Command 么, 在生成的 msg.proto.go 中会加上 Msg 前缀)。

httpListen 中用模块 net/http 新建了一个 httpServer, 对端口进行监听, 其中 Handler 后面再说。

wg 是个 sync.WaitGroup, 在 dispatch.go 中保证调度任务完成.

请求到来

在上面的代码中已经成功创建了 httpServer, 接下来浏览器发送 HTTP请求来到 http.go 中新建的 server 时, 需要将请求转交给 ts 代码中定义的 requestListener 进行响应。

// http.go

...

var requestID int32 = 0

func buildHTTPHandler(serverID int32) func(writer http.ResponseWriter, req *http.Request) {

return func(writer http.ResponseWriter, req *http.Request) {

requestID++

id, requestChan := requestID, fmt.Sprintf("http/%d", requestID)

done := make(chan bool)

Sub(requestChan, func(buf []byte) []byte {

msg := &Msg{}

proto.Unmarshal(buf, msg)

switch msg.Command {

case Msg_HTTP_RES_WRITE:

writer.Write(msg.HttpResWriteData)

case Msg_HTTP_RES_END:

done <- true

}

return nil

})

msg := &Msg{

HttpReqId: id,

HttpServerId: serverID,

HttpReqPath: req.URL.Path,

HttpReqMethod: req.Method,

}

go PubMsg("http", msg)

<-done

}

}

buildHTTPHandler 会生成个 Handler 接收请求, 对每个请求生成 requestChan 及 id。

订阅 requestChan 接收 ts 代码中 requestListener 处理请求后返回的结果, 在 msg.Command 为 Msg_HTTP_RES_WRITE 写入返回的 body, 而 Msg_HTTP_RES_END 返回结果给浏览器。

通过 PubMsg 可以将构造出的 msg 传递给 ts 代码, 这里需要 ts 代码对 http 进行订阅, 接收 msg。

// http.ts

...

const servers: {[key: number]: HttpServer} = {};

export function initHttp() {

sub("http", (payload: Uint8Array) => {

const msg = pb.Msg.decode(payload);

const id = msg.httpServerId;

const server = servers[id];

server.onMsg(msg);

});

}

...

export class HttpServer {

...

onMsg(msg: pb.Msg) {

const req = new Request(msg);

const res = new Response(msg);

this.requestListener(req, res);

}

}

...

这里在初始化 initHttp 中, 订阅了http, 得到之前 Golang 代码传递过来的 msg, 获取对应的 server, 触发对应 onMsg。

onMsg 中根据 msg 构建 Request 和 Response 的实例, 传递给 createHttpServer 时的处理函数 requestListener。

在处理函数中调用了 res.write 和 res.end, 同样需要在 type.ts 里进行定义。

// http.ts

...

export class Response{

...

write(data: string) {

pubInternal(this.requestChannel, {

command: pb.Msg.Command.HTTP_RES_WRITE,

httpResWriteData: enc.encode(data)

});

}

end() {

pubInternal(this.requestChannel, {

command: pb.Msg.Command.HTTP_RES_END

});

}

}

...

而之前 Response 的构造方法中赋值的 requestChannel 作用就在于调用 res.write 和 res.end 时, 能将 command 和 httpResWriteDate 传递给 Golang 中相应的 handler, 所以这个值需要和 Golang 代码中 Handler 中订阅的 requestChan 相一致。

最后

到这里, 整个流程就已经走通了, 接下来就是要在 ts 和 Golang 代码中执行模块初始化

// main.go

...

func Init() {

...

InitHTTP()

...

}

...

// main.ts

...

import { initHttp } from "./http";

(window as any)["denoMain"] = () => {

...

initHttp()

...

}

...

然后在 deno.ts 中抛出 Request、Response 和 createHttpServer, 以供调用。

// deno.ts

...

export { createHttpServer, Response, Request } from "./http";

另外需要在 deno.d.ts 进行类型定义, 这个不详细说明了。

通过 make 进行编译即可, 在每次编译之前最好都要 make clean 清理之前的编译结果。

通过命令 ./deno helloServer.ts启动服务器, 就可以在浏览器访问了。

Hello world!

到此,关于"如何让deno支持HTTP服务"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0