corsモジュール

 

corsモジュール

サーバーサイドでCORS(オリジン間リソース共有)対応をおこなうために使われる便利なモジュール。express.jsのミドルウェアとして利用する。

CORSとは、あるサイトを表示するときに異なるオリジン(ドメイン)からデータを取得する場合、それを許可する処理を必要とする仕組み。
あるリクエストに対し、サーバーはリソースにアクセスできるドメインをレスポンスのAccess-Control-Allow-Originヘッダにて応答する必要がある。 また、特定のHTTPメソッドでは本命のリクエストを送る前にプリフライトリクエストを送り、サーバのCORS対応(=Access-Control-Allow-Originヘッダの値)を確認することがある。

ブラウザのコンソールにAccess-Control-Allow-Originと書かれたエラーが発生している場合、その原因はCORS対応ができていないことにある。 CORSについて詳しくはオリジン間リソース共有 (CORS) - HTTP | MDNを参照。

ついでにこちらも:[Access-Control-Allow-Origin - HTTP MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)

基本的にCORSの仕様について把握していれば、分かる内容だとおもう。このモジュールについて調べながら少し勉強することになった。

middlewareWrapper([o])

  • o Object CORSの設定情報。省略時のデフォルト値は下記参照。

引数oのプロパティは以下の表を参照。

プロパティ名 説明
origin boolean, string, regexp, array, function レスポンスヘッダAccess-Control-Allow-Originを設定する、つまりどのドメインからのアクセスを許可するかを設定する。デフォルトでは全てのドメインを許可する。
boolean: trueの場合、リクエストのドメインをそのまま返す。falseの場合、CORS対応をおこなわない。
string: 特定のドメイン名を記載する(例: http://example.com)。
regexp: 正規表現で一致するドメインを許可する(例: /example\.com$/)。
array: 許可するドメインを配列で記載する。配列の要素は文字列でも正規表現でも構わない(例: ["http://example1.com", /\.example2\.com$/])。
function: 独自の関数で値を決定する。例えばDBに登録されている値を呼び出すなど。実装例を後述した。
methods string, array レスポンスヘッダAccess-Control-Allow-Methodsを設定する、つまりどのメソッドを許可するかを設定する。
string型の場合はカンマ区切りでメソッドを指定する(例:'GET,PUT,POST')。配列での指定も可能(例:['GET', 'PUT', 'POST'])。
デフォルトでは全てのメソッドを許可する。
allowedHeaders string, array レスポンスヘッダAccess-Control-Allow-Headersを設定する、つまり許可するリクエストヘッダを設定する(正確にはプリフライトリクエストに対して許可されているリクエストヘッダとして示す値を設定する)。
デフォルトでは、プリフライトリクエストのヘッダAccess-Control-Request-Headersの値をそのまま返す。
string型の場合はカンマ区切りでヘッダ名を指定する(例:'Content-Type,Authorization')。配列での指定も可能(例:['Content-Type', 'Authorization'])。
exposedHeaders string, array レスポンスヘッダAccess-Control-Expose-Headersを設定する、つまりレスポンスで返却するヘッダを設定する。CORSセーフリストレスポンスヘッダなるものがあるらしく、デフォルトではこれ以外のカスタムヘッダは許可しない。
credentials true レスポンスヘッダAccess-Control-Allow-Credentialsの値を設定する、つまり認証情報(Cookie、認証ヘッダ、またはTLSクライアント証明書)を使ったリクエストを許可するかどうかを設定する。
許可する場合はtrueを設定し、許可しない場合はこのプロパティを省略する。
maxAge number(integer) レスポンスヘッダAccess-Control-Max-Ageを設定する、つまりプリフライトリクエストの結果をキャッシュできる時間(単位は秒)を設定する。
デフォルトではキャッシュは無効と思われる。指定しない場合はプロパティを省略する。
preflightContinue funtion プリフライトリクエストをこのモジュールで処理した後、引数の関数に渡す。
デフォルトではfalse
optionSuccessStatus number HTTPメソッドOPTIONSによるプリフライトリクエストに対し、成功したときのステータスコードを設定する。
デフォルトでは204となっている。

よって、デフォルトでは以下のようになる。

{
  "origin": "*",
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
  "preflightContinue": false,
  "optionsSuccessStatus": 204
}

expressjsのミドルウェアとして利用されるため、エクスポートされているメソッドはmiddlewareWrapper(o)だけである。
ファイルも非常にシンプルで実装自体はlib/index.jsだけであった。

今回実際に動かすのは少々手間なので、モジュール作者がサンプルとして用意した環境を触りながら確認するとよさそう。

以下は公式サイトのソースコードを丸コピもしくは一部改変している。

全経路にCORS対応をおこなう

一般的なexpress.jsのミドルウェアらしく、useメソッドにcorsモジュールを指定する。これで全てのパスでcorsモジュールが呼び出される。

var express = require('express')
var cors = require('cors')
var app = express()

app.use(cors())

app.get('/products/:id', function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for all origins!'})
})

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

特定の経路にCORS対応を入れる

特定の経路にだけ設定する場合、その経路のメソッドの第2引数以降に設定する。

下記の場合は、/product/123のようなパスではCORS対応があるが、/animalsというパスではCORS対応がないことになる。

var express = require('express')
var cors = require('cors')
var app = express()

app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
})

app.get('/animals', function (req, res, next) {
  res.json({msg: 'This path is CORS-disabled'})
})

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

CORS対応の設定をおこなう

corsモジュールmiddlewareWrapperメソッドの引数に値を設定し、CORS対応の設定をおこなう。

下記の場合は、許可するドメインはhttp://example.comで、プリフライトリクエストに対するレスポンスのステータスコードは200に設定している。

var express = require('express')
var cors = require('cors')
var app = express()

var corsOptions = {
  origin: 'http://example.com',
  optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.get('/products/:id', cors(corsOptions), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for only example.com.'})
})

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

独自関数でドメインを設定する

middlewareWrapperメソッドの引数のoriginプロパティで関数を指定したときの挙動および関数の例を示す。

アクセス許可するオリジンをデータベースに登録している場合などに有用な設定である。

originプロパティに設定する関数は、function(origin, callback)のような形式とする。引数originにはリクエストを送ってきたドメインが入る。引数callbackはこの独自の関数の処理が終わったときに呼び出す関数で、callback(error, origins)のように使う。このとき、引数errorは独自関数でエラーが発生したときに値を設定する。引数originsはアクセスを許可するオリジン(ドメイン)を設定する。この値はmiddlewareWrapperメソッドの引数のoriginプロパティで指定できる値である必要がある。

db.loadOriginsは、データベースに問い合わせをおこない、アクセスを許可するオリジン(ドメイン)の情報を取得する呼び出しである。
db.loadOriginsはコールバック関数を引数に持ち、エラーが発生した場合には第1引数errorにその情報を格納する。値を取得した場合、第2引数originsにその値を格納する。この呼び出しがエラーとなっても正常終了しても、今度はcallbackという関数が呼び出される。DB問い合わせがあれば、エラーを関数callbackの第1引数にセットし、値を取得できればそれを第2引数にセットする。

var express = require('express')
var cors = require('cors')
var app = express()

var corsOptions = {
  origin: function (origin, callback) {
    // db.loadOrigins is an example call to load
    // a list of origins from a backing database
    db.loadOrigins(function (error, origins) {
      callback(error, origins)
    })
  }
}

app.get('/products/:id', cors(corsOptions), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for an allowed domain.'})
})

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})