解析node-cors模塊
阿新 • • 發佈:2017-09-24
oba 閉包 custom 中間件 tom evel smi clas web
(function () { ‘use strict‘; var assign = require(‘object-assign‘); var vary = require(‘vary‘); var defaults = { origin: ‘*‘, methods: ‘GET,HEAD,PUT,PATCH,POST,DELETE‘, preflightContinue: false, optionsSuccessStatus: 204 }; function isString(s) { return typeof s === ‘string‘ || s instanceof String; } function isOriginAllowed(origin, allowedOrigin) { // 多次遞歸返回一個地址 if (Array.isArray(allowedOrigin)) { for (var i = 0; i < allowedOrigin.length; ++i) { if (isOriginAllowed(origin, allowedOrigin[i])) { return true; } } return false; // 在白名單裏面就通過 } else if (isString(allowedOrigin)) { return origin === allowedOrigin; // 正則匹配 } else if (allowedOrigin instanceof RegExp) { return allowedOrigin.test(origin); } else { return !!allowedOrigin; } } function configureOrigin(options, req) { var requestOrigin = req.headers.origin, headers = [], isAllowed; // 默認所有域名都可以使用 if (!options.origin || options.origin === ‘*‘) { // allow any origin headers.push([{ key: ‘Access-Control-Allow-Origin‘, value: ‘*‘ }]); // 固定的一個域名 } else if (isString(options.origin)) { // fixed origin headers.push([{ key: ‘Access-Control-Allow-Origin‘, value: options.origin }]); headers.push([{ key: ‘Vary‘, value: ‘Origin‘ }]); } else { // 這裏通過白名單列表 isAllowed = isOriginAllowed(requestOrigin, options.origin); // reflect origin headers.push([{ key: ‘Access-Control-Allow-Origin‘, value: isAllowed ? requestOrigin : false }]); headers.push([{ key: ‘Vary‘, value: ‘Origin‘ }]); } return headers; } // 允許的方法 function configureMethods(options) { var methods = options.methods; if (methods.join) { methods = options.methods.join(‘,‘); // .methods is an array, so turn it into a string } return { key: ‘Access-Control-Allow-Methods‘, value: methods }; } function configureCredentials(options) { // 是否帶cookies if (options.credentials === true) { return { key: ‘Access-Control-Allow-Credentials‘, value: ‘true‘ }; } return null; } function configureAllowedHeaders(options, req) { var allowedHeaders = options.allowedHeaders || options.headers; var headers = []; // 如果沒有請求頭, 那麽指定請求頭 if (!allowedHeaders) { allowedHeaders = req.headers[‘access-control-request-headers‘]; // .headers wasn‘t specified, so reflect the request headers headers.push([{ key: ‘Vary‘, value: ‘Access-Control-Request-Headers‘ }]); } else if (allowedHeaders.join) { allowedHeaders = allowedHeaders.join(‘,‘); // .headers is an array, so turn it into a string } if (allowedHeaders && allowedHeaders.length) { headers.push([{ key: ‘Access-Control-Allow-Headers‘, value: allowedHeaders }]); } return headers; } // 該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。 // 如果想拿到其他字段,就必須在Access-Control-Expose-Headers裏面指定。上面的例子指定,getResponseHeader(‘FooBar‘)可以返回FooBar字段的值。 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers // https://github.com/expressjs/cors#configuration-options function configureExposedHeaders(options) { var headers = options.exposedHeaders; if (!headers) { return null; } else if (headers.join) { headers = headers.join(‘,‘); // .headers is an array, so turn it into a string } if (headers && headers.length) { return { key: ‘Access-Control-Expose-Headers‘, value: headers }; } return null; } // 在此時間內不需要發出預檢查請求 function configureMaxAge(options) { var maxAge = options.maxAge && options.maxAge.toString(); if (maxAge && maxAge.length) { return { key: ‘Access-Control-Max-Age‘, value: maxAge }; } return null; } // 發送響應頭 function applyHeaders(headers, res) { for (var i = 0, n = headers.length; i < n; i++) { var header = headers[i]; if (header) { if (Array.isArray(header)) { applyHeaders(header, res); } else if (header.key === ‘Vary‘ && header.value) { vary(res, header.value); } else if (header.value) { res.setHeader(header.key, header.value); } } } } function cors(options, req, res, next) { var headers = [], method = req.method && req.method.toUpperCase && req.method.toUpperCase(); // 嗅探請求 如果是非簡單請求,那麽會先發送這個 // 關於簡單請求和非簡單請求 // 這裏寫的很詳細 http://www.lcode.cc/2016/12/06/cors-explain.html if (method === ‘OPTIONS‘) { // preflight headers.push(configureOrigin(options, req)); headers.push(configureCredentials(options, req)); headers.push(configureMethods(options, req)); headers.push(configureAllowedHeaders(options, req)); headers.push(configureMaxAge(options, req)); headers.push(configureExposedHeaders(options, req)); applyHeaders(headers, res); // 兼容一波zz瀏覽器 if (options.preflightContinue ) { next(); } else { // Safari (and potentially other browsers) need content-length 0, // for 204 or they just hang waiting for a body res.statusCode = options.optionsSuccessStatus || defaults.optionsSuccessStatus; res.setHeader(‘Content-Length‘, ‘0‘); res.end(); } } else { // 返回正常的結果 // actual response headers.push(configureOrigin(options, req)); headers.push(configureCredentials(options, req)); headers.push(configureExposedHeaders(options, req)); applyHeaders(headers, res); next(); } } function middlewareWrapper(o) { // 設置回調函數 // if options are static (either via defaults or custom options passed in), wrap in a function var optionsCallback = null; if (typeof o === ‘function‘) { optionsCallback = o; } else { optionsCallback = function (req, cb) { cb(null, o); }; } // 使用到了閉包 return function corsMiddleware(req, res, next) { optionsCallback(req, function (err, options) { if (err) { // 出錯了直接進入下一個中間件 next(err); } else { var corsOptions = assign({}, defaults, options); var originCallback = null; // 通過傳參數進來可以動態使用cors。 // 見文檔 https://github.com/expressjs/cors#configuring-cors-w-dynamic-origin if (corsOptions.origin && typeof corsOptions.origin === ‘function‘) { originCallback = corsOptions.origin; } else if (corsOptions.origin) { originCallback = function (origin, cb) { cb(null, corsOptions.origin); }; } if (originCallback) { // 用來設置白名單 originCallback(req.headers.origin, function (err2, origin) { if (err2 || !origin) { next(err2); } else { corsOptions.origin = origin; cors(corsOptions, req, res, next); } }); } else { next(); } } }); }; } // can pass either an options hash, an options delegate, or nothing module.exports = middlewareWrapper; }());
解析node-cors模塊