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オブジェクトを返すだけです。
Deno.serve()
標準API シンプルにHTTPサーバーを作る場合は、Deno.serve()
を使います。
function handler(request: Request): Response {
return new Response(request.body)
}
Deno.serve(handler)
Deno.serve()
関数に対して、Requestオブジェクトを受け取ってResponseオブジェクトを返す関数を渡しています。
あとは、--allow-net
オプションを付けて実行するだけです。
deno run --allow-net app.ts
RequestオブジェクトからリクエストメソッドやURL等を取得できますので、後はswitch
文でもルーティングライブラリでも使って分岐して、リクエストに応じたレスポンスを返すようにすればOKです。
リクエストからよく使うデータを取得する
リクエストからは次のようなデータを取得できます。他に必要なデータはRequestオブジェクトを参照します。
async function handler(request: Request): Promise<Response> {
let method = request.method
let url = new URL(request.url)
let pathname = url.pathname
let query = Object.fromEntries(url.searchParams)
let data = await request.json()
let authorization = request.headers.get('authorization')
return new Response('Hello')
}
Cookieを利用する
Cookieを簡単に扱える標準ライブラリが用意されています。
import {
getCookies,
setCookie,
deleteCookie,
} from 'https://deno.land/std@0.224.0/http/cookie.ts'
function handler(request: Request): Response {
const cookies = getCookies(request.headers)
const res = new Response()
setCookie(res.headers, { name: 'name', value: 'value' })
// deleteCookie(res.headers, 'name')
return res
}
IPアドレスを取得する
RequestオブジェクトにはIPアドレスの情報がありませんので、Webサーバー側でリクエストしてきた相手のIPアドレスを確認したいときは、第二引数のConnInfoを使います。
function handler(request: Request, info: Deno.ServeHandlerInfo): Response {
return new Response((info.remoteAddr as Deno.NetAddr).hostname)
}
Deno.serve(handler)
ポート指定
Deno.serve()
関数は第一引数にオプションを渡すことができます。もし受信するポート番号を変更する場合はport
オプションを指定します。
Deno.serve({
port: 3000,
}, handler)
ファイルを返す
ファイルのReadableStreamをResponseオブジェクトに渡してやればファイルの中身を返すことができます。
async function handler(request: Request): Promise<Response> {
let file = await Deno.open('hello.txt')
return new Response(file.readable)
}
実行オプションに--allow-read
が必要です。
deno run --allow-net --allow-read app.ts
リダイレクト
別のURLにリダイレクトする場合はResponse.redirect()
を使えます。ブラウザではパスだけで利用できますが、Denoの場合はフルURLが必要です。
function handler(request: Request): Response {
let url = new URL(request.url)
return Response.redirect(`${url.protocol}//${url.host}/path`, 302)
}
location
ヘッダーを使うこともできます。この場合はパスだけで問題ありません。
function handler(request: Request): Response {
return new Response(null, {
status: 302,
headers: {
location: `/path`
}
})
}
Deno.listen()
標準API 標準APIで高度なHTTPサーバーを作る場合は、Deno.listen()
とDeno.serveHttp()
を組み合わせて使います。
ひと工夫してRequestオブジェクトを受け取ってResponseオブジェクトを返す関数を作るだけにしてしまうと楽です。
例えばリクエストボディをオウム返しするHTTPサーバーは次のように作ります。
function handler(request: Request): Response {
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): Response {
return new Response(request.body)
}
function handler(request: Request): Response {
return Promise.resolve(new Response(request.body))
}
IPアドレス
リクエストしてきた相手のIPアドレスの情報はconn
に入っていますので、必要な場合はconn
もhandler()
に渡します。
function handler(request: Request, conn: Deno.Conn): Response {
return new Response(conn.remoteAddr.hostname)
}
let listener = Deno.listen({ port: 80 })
for await (const conn of listener) {
(async () => {
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: '秘密鍵ファイルパス',
})
std/http
ライブラリ
std/http`ライブラリ
serve()関数
serve()
関数を使うと、HTTPサーバーを簡単に実現できます。
例えばリクエストボディをオウム返しするHTTPサーバーはこれだけでできてしまいます(@の後ろは最新バージョンを使ってください)。
import { serve } from 'https://deno.land/std@0.178.0/http/server.ts'
function handler(request: Request): Response {
return new Response(request.body)
}
serve(handler)
Cookieを利用する
Cookieを利用するには、標準ライブラリのCookieMap
オブジェクトが便利です。
import { serve } from 'https://deno.land/std@0.178.0/http/server.ts'
import { CookieMap, mergeHeaders } from "https://deno.land/std@0.181.0/http/mod.ts"
function handler(request: Request): Response {
let cookies = new CookieMap(request)
cookies.set('name', 'value')
return new Response('Hello', {
headers: mergeHeaders(cookies)
})
}
serve(handler)
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): Response {
return new Response((connInfo.remoteAddr as Deno.NetAddr).hostname)
}
serve(handler)
ファイルを返す
ファイルの内容をそのまま返す場合は、serveFile()
を使います。
import { serve } from 'https://deno.land/std@0.178.0/http/server.ts'
import { serveFile } from 'https://deno.land/std@0.178.0/http/file_server.ts'
function handler(request: Request): Response {
return serveFile(request, 'hello.txt')
}
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): Response {
return new Response(request.body)
}
serveTls(handler, {
certFile: '証明書ファイルパス',
keyFile: '秘密鍵ファイルパス',
})
Discussion