1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HonoのマージされたPRを読んでいく#51 - #55

Last updated at Posted at 2025-12-10

この記事何?

エンジニア歴1年半。業務で利用しているHonoが大好きだが、TSもプログラミング知識も弱々すぎて上手く使いこなせない。速いはずが多分生かせてない。
ちゃんと理解するためにソースコードリーディングしたい。

でも目的なくソースコード眺めても意味ないし……ねむい……
そうだ!PRを全部読めば、変遷やなぜ変更されているかストーリー的に分かるのでは。
と思ったのでチャレンジしてみる。PR2000以上あるので、全部できるかは知らん。
https://github.com/honojs/hono

#51 feat: default route with wildcard

おや、#52も同じ内容だ。

Revertされて PR #51の変更が取り消された(commit ab0783b)
PR #52: 修正版として再実装され、マージされた(commit b6c3eef6)ぽい。

ので、#52を見ていく。

#52 feat: default route with wildcard

ワイルドカードルートの優先順位を改善し、デフォルトルート機能を追加。

デフォルトルートってなんだっけ。

ルーティングテーブルに特定の宛先が見つからない場合に使用されるルート情報のこと

fmfm

  // 変更前
  if (curNode.children['*']) {
    const astNode = curNode.children['*'] // 常にワイルドカードにマッチ
  }

  // 変更後
  // '*' => match any path
  // /api/* => default wildcard match
  if (curNode.children['*'] && !curNode.children[p]) {
    //                         ↑ 具体的なルートが存在しない場合のみ
    const astNode = curNode.children['*']
    if (Object.keys(astNode.children).length === 0) {
      curNode = astNode
    }
  }
  • 具体的なルートが存在する場合 → そちらを優先
  • 具体的なルートが存在しない場合 → ワイルドカードにフォールバック

#53 feat: CORS middleware

CORS(Cross-Origin Resource Sharing)ミドルウェアを追加。

CORS とは?

Cross-Origin Resource Sharing(クロスオリジンリソース共有)
異なるドメインからのHTTPリクエストを許可するための仕組み。

例:
フロントエンド: https://example.com
API: https://api.example.com

↓ 異なるオリジン(クロスオリジン)

CORS がないと、ブラウザがリクエストをブロック

これ、ミドルウェアやライブラリの機能として使うことはあっても中身どうやって実装してるんだろう。ワクワク。

src/middleware/cors/cors.ts

  export const cors = (options?: CORSOptions) => {
    const defaults: CORSOptions = {
      origin: '*',
      allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
      allowHeaders: [],
      exposeHeaders: [],
    }
    const opts = { ...defaults, ...options }

    return async (c: Context, next: Function) => {
      await next()

      function set(key: string, value: string) {
        c.res.headers.append(key, value)
      }

      // オリジンを設定
      set('Access-Control-Allow-Origin', opts.origin)

      // オリジンが具体的に指定されている場合、Vary ヘッダーを追加
      if (opts.origin !== '*') {
        set('Vary', 'Origin')
      }

      // 認証情報を許可
      if (opts.credentials) {
        set('Access-Control-Allow-Credentials', 'true')
      }

      // 公開するヘッダーを設定
      if (opts.exposeHeaders.length) {
        set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','))
      }

      // プリフライトリクエスト(OPTIONS)の処理
      if (c.req.method === 'OPTIONS') {
        if (opts.maxAge != null) {
          set('Access-Control-Max-Age', opts.maxAge.toString())
        }

        if (opts.allowMethods.length) {
          set('Access-Control-Allow-Methods', opts.allowMethods.join(','))
        }

        let headers = opts.allowHeaders
        if (!headers.length) {
          // クライアントが要求したヘッダーをそのまま許可
          const requestHeaders = c.req.headers.get('Access-Control-Request-Headers')
          if (requestHeaders) {
            headers = requestHeaders.split(/\s*,\s*/)
          }
        }
        if (headers.length) {
          set('Access-Control-Allow-Headers', headers.join(','))
          set('Vary', 'Access-Control-Request-Headers')
        }

        // 204 No Content を返す
        c.res = new Response(null, {
          headers: c.res.headers,
          status: 204,
          statusText: c.res.statusText,
        })
      }
    }
  }

プリフライトリクエストとは?

ブラウザが本番リクエストの前に送る「確認リクエスト」。

処理の流れ:

  1. ブラウザ(クライアント)
    ↓ OPTIONS リクエスト(プリフライト)
    ↓ Access-Control-Request-Method: POST
    ↓ Access-Control-Request-Headers: Content-Type

  2. サーバー
    ↓ 204 No Content
    ↓ Access-Control-Allow-Methods: POST, GET, OPTIONS
    ↓ Access-Control-Allow-Headers: Content-Type

  3. ブラウザ
    「許可されているので本番リクエストを送る」
    ↓ POST リクエスト

  4. サーバー
    ↓ 200 OK + データ

使用方法

  1. デフォルト設定(すべて許可)
  import { Hono, Middleware } from 'hono'

  const app = new Hono()

  app.use('/api/*', Middleware.cors())

  app.get('/api/data', (c) => {
    return c.json({ data: 'Hello' })
  })

  // レスポンスヘッダー:
  // Access-Control-Allow-Origin: *
  1. カスタム設定
  app.use('/api/*', Middleware.cors({
    origin: 'http://example.com',              // 許可するオリジン
    allowMethods: ['POST', 'GET', 'OPTIONS'],  // 許可するHTTPメソッド
    allowHeaders: ['X-Custom-Header'],         // 許可するヘッダー
    exposeHeaders: ['Content-Length'],         // 公開するヘッダー
    maxAge: 600,                               // プリフライトのキャッシュ時間(秒)
    credentials: true,                         // 認証情報を含むリクエストを許可
  }))

  // レスポンスヘッダー:
  // Access-Control-Allow-Origin: http://example.com
  // Access-Control-Allow-Methods: POST, GET, OPTIONS
  // Access-Control-Allow-Headers: X-Custom-Header
  // Access-Control-Expose-Headers: Content-Length
  // Access-Control-Max-Age: 600
 // Access-Control-Allow-Credentials: true
 // Vary: Origin

ほえー

#54 なし

#55 fix: Content-Length middleware not as default

Content-Length ミドルウェアをデフォルトから削除(コメントアウト)

  • Content-Length ヘッダーを追加すると、かなり遅くなる
  • これはデフォルトミドルウェアにすべきではない

とのこと。

セキュリティの基礎知識しっかりないとダメだなぁ。すごい。勉強になった。今日はここまで。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?