這篇文章主要介紹“如何讓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服務”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注創新互聯網站,小編會繼續努力為大家帶來更多實用的文章!
網頁名稱:如何讓deno支持HTTP服務
網頁URL:http://vcdvsql.cn/article30/gjspso.html
成都網站建設公司_創新互聯,為您提供網站內鏈、微信公眾號、動態網站、網站導航、建站公司、關鍵詞優化
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯