🤢

Next.js appルートでのAPI RoutesのCORS設定

2023/07/22に公開

結論

Access-Control-Allowの追加

  • "Access-Control-Allow-Origin" は、リクエストオリジンの許可を設定します
    • "*" 全オリジンを許可
    • "example.app" example.appからのリクエストを許可
  • "Access-Control-Allow-Methods" は、リクエストメソッドの許可を設定します
    • "GET" GETメソッドを許可
    • "GET, POST" GETメソッドとPOSTメソッドを許可
  • "Access-Control-Allow-Headers" は、リクエストヘッダーの許可を設定します
    • "Content-Type" Content-Typeを許可
    • "Content-Type, Authorization" Content-TypeとAuthorizationを許可
export const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
};

corsHeadersを設定

export async function GET(request: Request, response: Response) {
  return NextResponse.json(
    { data: 'データ' },
    { status: 200, headers: corsHeaders })
}

全体のコード

app/api/sample/route.ts
export const corsHeaders = {
  'Access-Control-Allow-Origin': 'http://localhost:3001', // 許可するオリジン
  'Access-Control-Allow-Methods': 'POST, OPTIONS', // 許可するメソッド
  'Access-Control-Allow-Headers': 'Content-Type', // 許可するリクエストヘッダー
}

// リクエストヘッダーにContent-Type: "application/json"があると、preflightによりOPTIONSが必要
export async function OPTIONS() {
  return NextResponse.json({}, { headers: corsHeaders })
}

export async function GET(request: Request, response: Response) {
  // new Responseの書き方
  return new Response(JSON.stringify({ data: 'レスポンス' }), {
    status: 200,
    headers: corsHeaders 
  })

  // NextResponseの書き方
  return NextResponse.json(
    { foo: 'bar' },
    { status: 200, headers: corsHeaders })
}

export async function POST(request: Request, response: Response) {
  const body = await request.json()

  return NextResponse.json(
    { data: "データ", body },
    { status: 200, headers: corsHeaders },
  )
}

詰まった点

これでリクエストするとエラーになりました

fetch('http://localhost:3000/api/sample', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
})
  • 探っていくと、 Content-Type: 'application/json' の設定が問題みたいでした
    • POSTで、headers, bodyなし OK
    • POSTで、bodyあり OK
    • POSTで、bodyあり、空header OK
    • POSTで、bodyあり、headerに、'Content-Type': 'application/json', ⚠️エラー
    • POSTで、bodyあり、headerに、'Content-Type': 'ddddd', OK

エラー内容

Access to fetch at 'http://localhost:3000/api/scoring' from origin 'http://localhost:3001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

どうやら preflight request でエラーが出ているので、 preflight request について調べました

  • リクエストヘッダーに'Content-Type': 'application/json'を付けると、preflightというものが送られます。
  • preflight request リクエストは始めに [OPTIONS](https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/OPTIONS) メソッドによる HTTP リクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうかを確かめます
  • 今回のAPIはメソッド毎にexportしていて、OPTIONSが存在しないのでエラーが出ていたということです。

preflightとは

解決

  • Access-Control-Allow-Methodsに、OPTIONSを追加
  • Access-Control-Allow-Headersに、Content-Typeを追加
  • OPTIONSを追加
export const corsHeaders = {
  'Access-Control-Allow-Origin': 'http://localhost:3001',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', // OPTONSを追加
  'Access-Control-Allow-Headers': 'Content-Type', // 追加
}

export async function POST(request: Request, response: Response) {
  const body = await request.json()

  return NextResponse.json(
    { method: 'POSTです', body },
    { headers: corsHeaders },
  )
}

// 追加
export async function OPTIONS() {
  return NextResponse.json({}, { headers: corsHeaders })
}

参考

https://github.com/vercel/next.js/discussions/47933

Discussion