解析node-cors模块
(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模块的更多相关文章
- Node.js-Events 模块总结与源码解析
Events 描述 大多数 Node.js API 采用异步事件驱动架构,这些对象都是EventEmitter类的实例(Emitter),通过触发命名事件(eventName or type)来调用函 ...
- 深入浅出node(2) 模块机制
这部分主要总结深入浅出Node.js的第二章 一)CommonJs 1.1CommonJs模块定义 二)Node的模块实现 2.1模块分类 2.2 路径分析和文件定位 2.2.1 路径分析 2.2.2 ...
- Node.js模块
每一个Node.js都是一个Node.js模块,包括JavaScript文件(.js).JSON文本文件(.json)和二进制模块文件(.node). mymodul.js function Hell ...
- python 解析XML python模块xml.dom解析xml实例代码
分享下python中使用模块xml.dom解析xml文件的实例代码,学习下python解析xml文件的方法. 原文转自:http://www.jbxue.com/article/16587.html ...
- Node.js 模块
稳定性: 5 - 锁定 Node 有简单的模块加载系统.在 Node 里,文件和模块是一一对应的.下面例子里,foo.js 加载同一个文件夹里的 circle.js 模块. foo.js 内容: va ...
- node基础—模块系统
模块的概念 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块加载系统. 在 Node.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块),换言之,一个 Node ...
- 10、Node.js模块系统
##################################################################################介绍Node.js模块系统为了让No ...
- 如何发布一个自定义Node.js模块到NPM(详细步骤)
咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...
- 编写原生Node.js模块
导语:当Javascript的性能需要优化,或者需要增强Javascript能力的时候,就需要依赖native模块来实现了. 应用场景 日常工作中,我们经常需要将原生的Node.js模块做为依赖并在项 ...
- 编写原生的Node.js模块
导语:当Javascript的性能遭遇瓶颈,或者需要增强Javascript能力的时候,就需要依赖native模块来实现了. 应用场景 日常工作中,我们经常需要将原生的Node.js模块做为依赖并在项 ...
随机推荐
- Web应急:搜索引擎劫持
当你直接打开网址访问网站,是正常的,可是当你在搜索引擎结果页中打开网站时,会跳转到一些其他网站,比如博彩,虚假广告,淘宝搜索页面等.是的,你可能了遇到搜索引擎劫持. 现象描述 从搜索引擎来的流量自动跳 ...
- golang--生成某区间的随机数
import ( "crypto/rand" "fmt" "math" "math/big" ) func main() ...
- BindRequired and Required
[https://www.cnblogs.com/tdfblog/p/required-and-bindrequired-in-asp-net-core-mvc.html] Required:对值进行 ...
- 【python+selenium学习】常见错误: 'gbk' codec can't decode byte 0xb0 in position 30
最近编写的自动化脚本,数据部分使用到了从配置文件中取,即自定义config.ini,但是在读取配置文件的时候却报错了'gbk' codec can't decode byte 0xb0 in posi ...
- css层叠规则(层叠样式表)
CSS层叠规则: 1.找出所有相关的规则,这些规则都包含与一个给定元素匹配的选择器. 2.按权重(!important)和来源对应用到给定元素的所有声明进行排序. 3.按特殊性对应用到给定元素的所有声 ...
- JS 树形结构与数组结构相互转换、在树形结构中查找对象
总是有很多需求是关于处理树形结构的,所以不得不总结几个常见操作的写法.¯\_(ツ)_/¯ 首先假设有一个树形结构数据如下 var tree=[ { 'id': '1', 'name': '教学素材管理 ...
- JavaWeb 文件的上传、下载
文件上传 表单 <form action="HandlerServlet" method="post" enctype="multipart/f ...
- 4 Linux文件与目录管理
Linux的目录文件结构为树状结构,最顶级目录为根目录 / 其他目录通过挂载可以将他们添加到树中,通过解除挂载可以移除他们 绝对路径: 根目录开始 eg: /usr/share/doc 相对路径: 不 ...
- qtp安装和使用
QTP许可证密钥的破解步骤: 以前使用QTP9.2 使用此方法成功破解,现在本人使用的HP QuickTest Professional 11 英文版,也成功适用. 一.准备工作: 1. 由于注册码文 ...
- Lua 字符串查找函数 string.find(s, pattern [, init [, plain]] )【转】
函数原型 string.find(s, pattern [, init [, plain]] ) s: 源字符串 pattern: 待搜索模式串 init: 可选, 起始位置 plain: 我没用过 ...