オリジン•CORS•プリフライトリクエストを理解する【Nuxt.js×Netlify Functions】
  • 2022.02.17に公開
  • ブログ構築TS
  • 8. NetlifyFunctionsを使った検索機能
  • No.4 / 9

今回達成すること

前回作成したNuxtのFunctionsリクエストボタンの通信を行えるように、FunctionsにCORS設定を行います。

2022-02-17 11-18-25

実装の中で

  • オリジンとは?
  • CORSとは?
  • プリフライトリクエストとは?

について解説します。

CORSエラーを確認する

Functionsのサーバーを起動して、

% yarn functions:dev

トップページのボタンをクリックしてみましょう。

2022-02-17 11-18-25-1

ネットワークエラーが発生しました。

2022-02-15 19-52-00

これは、異なるオリジン間の通信を拒否するブラウザの仕様です。

オリジンとは?

オリジンとは、

  • プロトコル ... http
  • ドメイン ... localhost
  • ポート ... 3000

を組み合わせたものを言います。

Document: Origin (オリジン) | MDN

クロスオリジンリソースシェアリング(CORS)とは?

通常、同じオリジン間の通信は、自分のアプリから自分のアプリへのリクエストなので許容されます。

今回は http:localhost:3000 => http:localhost:8888への異なるオリジン間の通信となります。

このような通信を何でも許容すると、外部からの悪意のあるリクエストにより、サーバーのデータが抜き取られてしまいます。

そこでブラウザには、異なるオリジン間の通信にアクセス権を与えるようにブラウザに指示する仕組みが用意されています。

その仕組みを「クロスオリジンリソースシェアリング(CORS)」と呼びます。

  • クロスオリジンリソースシェアリング => 異なるオリジン間でリソースを共有する仕組み

Document: オリジン間リソース共有 (CORS) - HTTP | MDN

異なるオリジンのリクエストを許可するには?

特定のオリジンのリクエストを許可するには、レスポンスヘッダにAccess-Control-Allow-Originを追加し、許容するオリジンを指定します。

netlify/functions/form-validator/form-validator.ts
import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'

/* eslint require-await: 'off' */
// eslint Doc: https://eslint.org/docs/user-guide/configuring/rules
export const handler: Handler = async (_event: HandlerEvent, _context: HandlerContext) => {
  const name: string = 'World'
  // 追加
  const headers: { [key: string]: string } = {
    'Access-Control-Allow-Origin': 'http://localhost:3000'
  }

  return {
    statusCode: 200,
    // 追加
    headers,
    body: JSON.stringify({
      message: `Hello, ${name}!`
    })
  }
}

もう一度ボタンをクリックしてみましょう。

FunctionsからHello Worldが返されます。

2022-02-15 22-25-49

これで特定のオリジンからのリクエストを許可する設定ができました。

プリフライトリクエストに対応する

上記Nuxtからのリクエストは、シンプルリクエストという種類のリクエストに該当します。

シンプルリクエストの要件: Simple requests - HTTP | MDN

Nuxtからのリクエストにパラメーターを持たせた場合、

pages/index.vue
@Component
export default class IndexPage extends Vue {
  async requestFunctions (): Promise<void> {
    // $postに書き換え
    await this.$axios.$post(
      '/.netlify/functions/form-validator',
      // パラメーターの追加
      { data: 'Nuxt request parameter' }
    )
      .then((response: { message: string }) => {
        console.log(response)
      })
  }
}

リクエストヘッダには、Content-Type: application/jsonが付与されます。

2022-02-15 22-54-21

リクエストヘッダにContent-Type: application/jsonが付与された場合、シンプルリクエストの要件から外れます。

この時ブラウザは、プリフライトリクエストを実行します。

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

リクエスト送信前に「該当のリクエストは、安全に送信できるリクエストであるか?」を判断するためのHTTPリクエストです。

シンプルなリクエストに該当しない場合、ブラウザが自動で送信します。

リクエストメソッドは、OPTIONSとなります。

参考: Preflighted requests - HTTP | MDN

httpMethodを確認する

event.httpMethod のログをとって確認しましょう。

netlify/functions/form-validator/form-validator.ts
...
export const handler: Handler = async (event: HandlerEvent, _context: HandlerContext) => {
  // 追加
  console.log(event.httpMethod)
  ...
}

FunctionsサーバーのログにはPOSTリクエストに到達する前のOPTIONS が表示されます。

localhost:8888サーバーログ
Request from ::1: OPTIONS /.netlify/functions/form-validator
OPTIONS

これがプリフライトリクエストの実態です。

プリフライトリクエストに対応するには?

OPTIONSのリクエストに対して、通信が安全であることを伝えます。

リクエストヘッダにContent-Type: application/jsonが付与された場合、

レスポンスヘッダは'Access-Control-Allow-Headers'を返します。

また異なるオリジンなので、Access-Control-Allow-Originのヘッダも返す必要があります。

リクエストヘッダ 対応するレスポンスヘッダ レスポンスの値
Content-Type: application/json Access-Control-Allow-Headers Content-Type
Origin: 異なるオリジン Access-Control-Allow-Origin 許容するオリジン

プリフライトリクエストまとめ

  • axios$post()メソッドでパラメーターを持たせた場合、リクエストヘッダにはContent-Type: application/jsonが付与される。
  • Content-Type: application/jsonが付与されたリクエストは、プリフライトリクエストが実行される。
  • プリフライトリクエストとは、安全に送信できるリクエストであるか?を判断するリクエスト。ブラウザによって自動送信される。
  • つまり、シンプルリクエストでは無いリクエストにはOPTIONSと該当のリクエスト、2つのリクエストが送信される。
  • OPTIONSのリクエストに対して「安全なリクエストである」事を通知するには、
    • 'Access-Control-Allow-Headers': 'Content-Type'
    • 'Access-Control-Allow-Origin': '許容するオリジン' を返す。
  • 上記の「安全なリクエストである事を通知する設定」をCORS設定という。

FunctionsプロジェクトにCORS設定を行う

ここまでの知識を踏まえたform-validator.tsのコードは以下のようになります。

netlify/functions/form-validator/form-validator.ts
import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'

/* eslint require-await: 'off' */
// eslint Doc: https://eslint.org/docs/user-guide/configuring/rules
export const handler: Handler = async (event: HandlerEvent, _context: HandlerContext) => {
  // 許容するオリジンリスト
  const allowedOrigins: string[] = [
    'http://localhost:3000'
    /* 本番環境のカスタムドメインはここに追加する */
  ]

  // リクエストオリジン
  const requestOrigin: string | undefined = event.headers.origin

  // レスポンスヘッダ
  let headers: { [key: string]: string } | undefined

  // リクエストオリジンが存在し、許容オリジンリストに含まれている場合
  if (requestOrigin && allowedOrigins.includes(requestOrigin)) {
    // レスポンスヘッダの値を用意
    headers = {
      'Access-Control-Allow-Headers': 'Content-Type',
      'Access-Control-Allow-Origin': requestOrigin
    }
  }

  // プリフライトリクエストの場合
  if (event.httpMethod === 'OPTIONS') {
    // 安全なリクエストであることを伝える
    if (headers) {
      return {
        statusCode: 200,
        headers
      }
    }

    // headersが存在しない場合のレスポンス
    return {
      statusCode: 500
    }
  // リクエストがPOSTではない場合
  } else if (event.httpMethod !== 'POST') {
    // 405エラーの発生
    return {
      statusCode: 405,
      body: 'Method Not Allowed'
    }
  }

  /*
    プリフライトリクエスト通過 && POSTリクエストの場合
  */
  const name: string = 'World'

  return {
    statusCode: 200,
    headers,
    body: JSON.stringify({
      message: `Hello, ${name}!`
    })
  }
}
  • const allowedOrigins ... 許容するオリジンを配列で指定。

  • allowedOrigins.includes(requestOrigin) ... リクエストオリジンが許容オリジン配列に含まれていればtrueを返す。

  • if (event.httpMethod === 'OPTIONS') ... プリフライトリクエストの場合は、headersの値を確認し、存在すればCORS設定を行なったheadersを返す。これにより、安全な通信であることを保証している。

  • else if (event.httpMethod !== 'POST') ... リクエストがPOST ではない場合はエラーを返し、処理を終了している。

CORS設定を確認しよう

Functionsサーバーを起動して、ブラウザで確認してみましょう。

yarn functions:dev

ボタンをクリックすると、messageが返ってきました。

2022-02-17 11-18-25

Functionsサーバーのログを確認すると、OPTIONSPOST双方に200のステータスコードが返ってきています。

2022-02-17 11-21-41

許容しないオリジンからのリクエストを行う

許容しないオリジンからのリクエストにエラーを返しているか確認します。

Nuxtサーバーを停止し、ポート8080で起動します。

% yarn dev --port 8080

ポート8080の場合では、ネットワークエラーが返ってきており、ログを見るとCORSエラーが発生しています。

2022-02-17 11-27-47

FunctionsサーバーはOPTIONSの時点で500が返ってきていますね。

2022-02-17 11-29-48

OK!!これで想定通りの挙動となりました。

確認が取れたので、Nuxtサーバーは3000番で再起動しておいてください。

% yarn dev

今回の作業は以上です。

% git commit -am "Add Netlify Functions cors settings"

まとめと次回

今回は、Nuxtからのリクエストに応えられるようFunctionsにCORS設定を行いました。

この知識を理解していれば、モジュール無しで異なるオリジン間の通信設定を行うことができますね。

次回は、Functions内で使用する自作ライブラリを作成し、CORS設定の処理メソッドを作成します。

そしてフォームのバリデーションを行うFunctionsプロジェクトを完成させましょう。

あなたの力になれること
私自身が独学でプログラミングを勉強してきたので、一人で学び続ける苦しみは痛いほど分かります。そこで、当時の私がこんなのあったら良いのにな、と思っていたサービスを立ち上げました。周りに質問できる人がいない、答えの調べ方が分からない、ここを聞きたいだけなのにスクールは高額すぎる。そんな方に向けた単発・短期間メンターサービスを行っています。
独学プログラマのサービス
ブログ構築TSの投稿
1
  • Nuxt.js×TypeScript開発環境構築
  • /
  • #01
Nuxt.jsをローカルPCに立ち上げよう
2
  • Nuxt.js×TypeScript開発環境構築
  • /
  • #02
Nuxt.jsプロジェクトをGitHubにPushしよう
3
  • Nuxt.js×TypeScript開発環境構築
  • /
  • #03
nuxt-property-decoratorのインストールとTypeScriptのセットアップ
1
  • Vuetifyセットアップ
  • /
  • #01
TypeScript環境のNuxt.jsにVuetifyを導入しよう
2
  • Vuetifyセットアップ
  • /
  • #02
VuetifyにカスタムCSSを追加してSASS変数を理解しよう
3
  • Vuetifyセットアップ
  • /
  • #03
VuetifyにカスタムSVGアイコンを追加しよう
1
  • NetlifyCLIを使ったNuxtデプロイ
  • /
  • #01
Netlify CLIをインストールして本番環境のサイトを作成しよう
2
  • NetlifyCLIを使ったNuxtデプロイ
  • /
  • #02
netlify.tomlを使ってNuxt.jsをNetlifyに手動デプロイしよう
1
  • Contentfulモデル構築
  • /
  • #01
Contentfulの料金とCommunityプランの無料枠を理解する
2
  • Contentfulモデル構築
  • /
  • #02
Contentfulへ新規会員登録、ロケールの変更、API Keyの発行を行う
3
  • Contentfulモデル構築
  • /
  • #03
Contentful ブログカテゴリーモデルを作成しよう
4
  • Contentfulモデル構築
  • /
  • #04
Contentful カテゴリーモデルに1対1で関連づくblogPostモデルを作成しよう
5
  • Contentfulモデル構築
  • /
  • #05
Contentful ブログ記事に1対多で関連づくplogTagモデルを作成しよう
6
  • Contentfulモデル構築
  • /
  • #06
Contentful カテゴリー・ブログ記事・タグコンテンツを作成しよう
1
  • Nuxt.js×Contentfulセットアップ
  • /
  • #01
Nuxt.js×Contentfulセットアップ。モジュールのインストールからAPI Keyの登録まで
2
  • Nuxt.js×Contentfulセットアップ
  • /
  • #02
Contentful APIリクエストの実行 Nuxt.jsにブログコンテンツを表示しよう
3
  • Nuxt.js×Contentfulセットアップ
  • /
  • #03
ContentfulAPIをNetlifyにデプロイしよう【Nuxt FullStaticのasyncDataとfetch】
1
  • Vuex×TypeScriptセットアップ
  • /
  • #01
Vuexの型付け vuex-module-decoratorsとnuxt-typed-vuexどちらを使用するか
2
  • Vuex×TypeScriptセットアップ
  • /
  • #02
nuxt-typed-vuexのインストールとセットアップ。Vuexの型定義と呼び出し方
3
  • Vuex×TypeScriptセットアップ
  • /
  • #03
VuexにContentfulの型定義ファイルとnuxtServerInitを追加しよう
4
  • Vuex×TypeScriptセットアップ
  • /
  • #04
VuexにContentfulAPIレスポンスを保存してVueファイルに表示しよう
1
  • コンテンツページ構築
  • /
  • #01
ブログアプリのページ設計とNuxt.jsの動的ルーティングについて理解しよう
2
  • コンテンツページ構築
  • /
  • #02
カテゴリーのコンテンツページを作成しよう【Nuxt.js×Contentful】
3
  • コンテンツページ構築
  • /
  • #03
カテゴリーに関連付くブログ記事一覧を表示しよう【Nuxt.js×Contentful】
4
  • コンテンツページ構築
  • /
  • #04
injectを使用して共通エラー処理メソッドを作成しよう【Nuxt×TypeScript】
5
  • コンテンツページ構築
  • /
  • #05
NuxtChildを使用してブログ記事ページを作成しよう【Nuxt.js×TypeScript】
6
  • コンテンツページ構築
  • /
  • #06
タグ一覧ページとタグ関連づく記事一覧を表示しよう【Nuxt.js×TypeScript】
7
  • コンテンツページ構築
  • /
  • #07
プライバシーポリシーページを作成しよう【Nuxt.js×TypeScript】
8
  • コンテンツページ構築
  • /
  • #08
@nuxtjs/i18nのインストールとセットアップ。ページタイトルの翻訳化【TypeScript】
1
  • NetlifyFunctionsを使った検索機能
  • /
  • #01
Netlify Functionsを使ってクエリを返す関数を作成しよう【Nuxt.js×TypeScript】
2
  • NetlifyFunctionsを使った検索機能
  • /
  • #02
Netlify Functionsプロジェクトをデプロイしよう【Nuxt.js×TypeScript】
3
  • NetlifyFunctionsを使った検索機能
  • /
  • #03
Nuxt.js × axiosセットアップ Netlify Functionsにリクエストを行う準備をしよう
4
  • NetlifyFunctionsを使った検索機能
  • /
  • #04
オリジン•CORS•プリフライトリクエストを理解する【Nuxt.js×Netlify Functions】
5
  • NetlifyFunctionsを使った検索機能
  • /
  • #05
Netlify Functionsを使ってフォームバリデーション機能を構築しよう【Nuxt.js】
6
  • NetlifyFunctionsを使った検索機能
  • /
  • #06
ツールバーに表示する検索フォームを作成しよう【Nuxt.js×TypeScript】
7
  • NetlifyFunctionsを使った検索機能
  • /
  • #07
検索ページを作成しよう【Vue propsとTypeScriptの書き方 解説】
8
  • NetlifyFunctionsを使った検索機能
  • /
  • #08
Netlify FunctionsからContentfulAPIリクエストを送ろう【Nuxt.js】
9
  • NetlifyFunctionsを使った検索機能
  • /
  • #09
検索ページに「もっと見る」ボタンを実装しよう【Nuxt.js×TypeScript】
1
  • ブログMarkdown対応
  • /
  • #01
@nuxtjs/markdownitのインストールとセットアップ【Nuxt.js×TypeScript】
2
  • ブログMarkdown対応
  • /
  • #02
Nuxt.js×markdown-it 外部リンクを別タブで開くプラグインを追加しよう
3
  • ブログMarkdown対応
  • /
  • #03
Nuxt.js×markdown-it 内部リンクをVueRouterで高速にページ遷移しよう
4
  • ブログMarkdown対応
  • /
  • #04
Nuxt.js×markdown-it アンカーリンクとブログ目次を自動生成しよう
独学プログラマ
独学でも、ここまでできるってよ。
CONTACT
Nuxt.js制作のご依頼は下記メールアドレスまでお送りください。