murave

CORSに@nuxtjs/proxy

Nuxt.js でspaの開発やってたら普通は開発サーバーを使いますよね。
例えばyarnをパッケージマネージャーに採用している場合だと

$ yarn run dev
yarn run v1.21.1
$ nuxt

╭───────────────────────────────────────────────╮
│ │
│ Nuxt.js v2.12.2 │
│ Running in development mode (spa) │
│ │
│ Listening on: http://127.0.0.1:3000/ │
│ │
╰───────────────────────────────────────────────╯

ℹ Preparing project for development 13:54:32
ℹ Initial build may take a while 13:54:32
✔ Builder initialized 13:54:32
✔ Nuxt files generated 13:54:32

✔ Client
Compiled successfully in 25.76s

ℹ Waiting for file changes 13:55:00
ℹ Memory usage: 241 MB (RSS: 451 MB) 13:55:00
ℹ Listening on: http://127.0.0.1:3000/
といった感じ。通常だとポートは3000になります。

サーバー側APIは別のポートで提供することになるので別オリジンになり、そのままだと開発中にAPIアクセスができません。

セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、 XMLHttpRequestや Fetch API は同一オリジンポリシーsame-origin policyに従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンの場合は正しい CORS ヘッダーを含んでいることが必要です。

「オリジン間リソース共有 (CORS)」(https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)

からの引用です。

運用時には必要なくても開発時にはCORS対応が必要になる、ということが発生するわけです。
どうしたら?

「そうだね @nuxtjs/proxy だね」と公式さんは言いました(適当)

「クロスオリジンリソースを共有するには?」(https://ja.nuxtjs.org/faq/http-proxy/)

から導入や詳細については辿っていただくとして、自分の設定などをご紹介します。

前提条件として、cross-envで環境毎に設定を切り替えています。
この件についてはNuxt.js 悩み解決の小ネタ集をご参照ください。

nuxt.config.js から関係箇所抜粋

const environment = process.env.NODE_ENV || 'development'
const envSet = require(`./.env.${environment}.js`)

export default {
// (略)
axios: {
proxy: true
},
proxy: {
// 開発中はAPIサーバーとポートが異なるのでその対処のため(CORS対策)
'/api/': {
target: envSet.server.domain,
pathRewrite: { '^/api/': envSet.server.apiDir }
}
},
// (略)
開発環境用の設定ファイル .env.development.js

module.exports = {
server: {
domain: 'http://127.0.0.1',
apiDir: '/api/'
}
}
てな設定で開発時は http://127.0.0.1/api/ の下にAPIアクセスしCORSについてはよろしくやってくれます。

使用方法は /api/all というパスのAPIへのアクセスだとこんな感じ

const res = await this.$axios.get('/api/all')
おまけ
前の例ではややこしくなるのが嫌でpathRewriteで変に書き換えが起きないようにしていますが、APIが実は /api/rev2/ の下で提供されていた場合には設定ファイルを以下のよう修正すれば対応できます。

module.exports = {
server: {
domain: 'http://127.0.0.1',
apiDir: '/api/rev2/'
}
}
いい感じですね。