DenoでHTTP Webサーバーを作る
Fetch API
DenoはFetch APIのインターフェースを使ってHTTP Webサーバーを作ることができます。
Fetch APIとは、JavaScriptでブラウザからサーバーにリクエストを投げるときに使うこんなやつです。
let response: Response = await fetch(new Request('http://localhost:8000'))
Requestオブジェクトを投げるとResponseオブジェクトが返ってきます。
(普通、Requestオブジェクトは省略しますが、あえて明記しています。)
DenoでHTTPサーバーを作るときは逆にRequestオブジェクトを受け取ってResponseオブジェクトを返すだけです。
std/http
ライブラリ
serve()関数
serve()
関数を使うと、HTTPサーバーを簡単に実現できます。
例えばリクエストボディをオウム返しするHTTPサーバーはこれだけでできてしまいます(@の後ろは最新バージョンを使ってください)。
import { serve } from 'https://deno.land/std@0.178.0/http/server.ts'
function handler(request: Request) {
return new Response(request.body)
}
serve(handler)
serve()
関数に対して、Requestオブジェクトを受け取ってResponseオブジェクトを返す関数を渡しています。
あとは、--allow-net
オプションを付けて実行するだけです。
deno run --allow-net app.ts
RequestオブジェクトからリクエストメソッドやURL等を取得できますので、後はswitch
文でもルーティングライブラリでも使って分岐して、リクエストに応じたレスポンスを返すようにすればOKです。
IPアドレス
RequestオブジェクトにはIPアドレスの情報がありませんので、Webサーバー側でリクエストしてきた相手のIPアドレスを確認したいときは、第二引数のConnInfoを使います。
import { serve, ConnInfo } from 'https://deno.land/std@0.178.0/http/server.ts'
function handler(request: Request, connInfo: ConnInfo) {
return new Response(connInfo.remoteAddr.hostname)
}
serve(handler)
ポート指定
serve()
関数は第二引数にオプションを受け取ることができます。もし受信するポート番号を変更する場合はport
オプションを指定します。
serve(handler, {
port: 3000,
})
httpsサーバー
httpsでサーバーを立てる場合はserveTls()
関数を使います。
import { serveTls } from 'https://deno.land/std@0.178.0/http/server.ts'
function handler(request: Request) {
return new Response(request.body)
}
serveTls(handler, {
certFile: '証明書ファイルパス',
keyFile: '秘密鍵ファイルパス',
})
標準API
標準APIでHTTPサーバーを作る場合は、Deno.listen()
とDeno.serveHttp()
を組み合わせて使います。
ひと工夫してRequestオブジェクトを受け取ってResponseオブジェクトを返す関数を作るだけにしてしまうと楽です。
例えばリクエストボディをオウム返しするHTTPサーバーは次のように作ります。
function handler(request: Request) {
return new Response(request.body)
}
let listener = Deno.listen({ port: 80 })
for await (const conn of listener) {
for await (const { request, respondWith } of Deno.serveHttp(conn)) {
respondWith(handler(request))
}
}
実行するときは、--allow-net
オプションを付けます。
deno run --allow-net app.ts
handler()
内でRequestオブジェクトからリクエストメソッドやURL等を取得できますので、後はswitch
文でもルーティングライブラリでも使って分岐して、リクエストに応じたレスポンスを返すようにすればOKです。
非同期処理
respondWith()
がPromiseLike<Response>
を受け取るので、handler()
は非同期関数にしても問題ありません。
async function handler(request: Request) {
return new Response(request.body)
}
function handler(request: Request) {
return Promise.resolve(new Response(request.body))
}
IPアドレス
リクエストしてきた相手のIPアドレスの情報はconn
に入っていますので、必要な場合はconn
もhandler()
に渡します。
function handler(request: Request, conn: Deno.Conn) {
return new Response(conn.remoteAddr.hostname)
}
let listener = Deno.listen({ port: 80 })
for await (const conn of listener) {
for await (const { request, respondWith } of Deno.serveHttp(conn)) {
respondWith(handler(request, conn))
}
}
httpsサーバー
httpsでサーバーを立てる場合はDeno.listenTls()
関数を使います。
let listener = Deno.listenTls({
port: 80,
certFile: '証明書ファイルパス',
keyFile: '秘密鍵ファイルパス',
})
ファイルを返す
ファイルのReadableStreamをResponseオブジェクトに渡してやればファイルの中身を返すことができます。
async function handler(request: Request) {
let file = await Deno.open('hello.txt')
return new Response(file.readable)
}
Discussion