cors: 跨域资源共享(Cross-Origin Resource Sharing)是一种机制,用来允许不同源服务器上的指定资源可以被特定的Web应用访问。
在koa项目中使用cors中间件:
eg:
1 var koa = require('koa'); 2 var route = require('koa-route'); 3 var cors = require('koa-cors'); 4 var app = koa(); 5 6 app.use(cors()); 7 8 app.use(route.get('/', function() { 9 this.body = { msg: 'Hello World!' }; 10 })); 11 12 app.listen(3000);
koa-cors 源码解析:
1 'use strict'; 2 3 const vary = require('vary'); 4 5 /** 6 * CORS middleware 7 * 8 * @param {Object} [options] 9 * - {String|Function(ctx)} origin `Access-Control-Allow-Origin`, default is request Origin header 10 * - {String|Array} allowMethods `Access-Control-Allow-Methods`, default is 'GET,HEAD,PUT,POST,DELETE,PATCH' 11 * - {String|Array} exposeHeaders `Access-Control-Expose-Headers` 12 * - {String|Array} allowHeaders `Access-Control-Allow-Headers` 13 * - {String|Number} maxAge `Access-Control-Max-Age` in seconds 14 * - {Boolean|Function(ctx)} credentials `Access-Control-Allow-Credentials` 15 * - {Boolean} keepHeadersOnError Add set headers to `err.header` if an error is thrown 16 * - {Boolean} secureContext `Cross-Origin-Opener-Policy` & `Cross-Origin-Embedder-Policy` headers.', default is false 17 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer/Planned_changes 18 * - {Boolean} privateNetworkAccess handle `Access-Control-Request-Private-Network` request by return `Access-Control-Allow-Private-Network`, default to false 19 * @see https://wicg.github.io/private-network-access/ 20 * @return {Function} cors middleware 21 * @public 22 */ 23 module.exports = function (options) { 24 // 这是默认配置 25 const defaults = { 26 // Access-Control-Allow-Methods 必须字段,表示服务器支持的跨域HTTP方法、返回的是所有支持方法 27 allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', 28 secureContext: false, 29 }; 30 31 // 覆盖默认配置 合并options 32 options = { 33 ...defaults, 34 ...options, 35 }; 36 37 // 组装options 38 // CORS很多响应头value都是允许多个逗号分隔的字符串 39 if (Array.isArray(options.exposeHeaders)) { 40 options.exposeHeaders = options.exposeHeaders.join(','); 41 } 42 43 if (Array.isArray(options.allowMethods)) { 44 options.allowMethods = options.allowMethods.join(','); 45 } 46 47 if (Array.isArray(options.allowHeaders)) { 48 options.allowHeaders = options.allowHeaders.join(','); 49 } 50 // 设计预检请求的有效期 51 if (options.maxAge) { 52 options.maxAge = String(options.maxAge); 53 } 54 55 // keepHeadersOnError 默认设置为true 56 options.keepHeadersOnError = options.keepHeadersOnError === undefined || !!options.keepHeadersOnError; 57 58 // 返回中间件函数 59 return async function cors(ctx, next) { 60 // If the Origin header is not present terminate this set of steps. 61 // The request is outside the scope of this specification. 62 63 const requestOrigin = ctx.get('Origin'); 64 65 // Always set Vary header 66 // https://github.com/rs/cors/issues/10 67 ctx.vary('Origin'); 68 69 // 获取配置参数中的 origin,默认取 requestOrigin 70 let origin; 71 if (typeof options.origin === 'function') { 72 origin = await options.origin(ctx); 73 if (!origin) return await next(); 74 } else { 75 origin = options.origin || requestOrigin; 76 } 77 78 // 处理不同场景下的credentials 79 // Access-Control-Allow-Credentials:可选、布尔值、表示是否允许发送Cookie 80 // Cookie默认不包含在CORS请求中,设为true表示服务器许可、 81 // 如果不需要直接删除该字段即可 82 let credentials; 83 if (typeof options.credentials === 'function') { 84 credentials = await options.credentials(ctx); 85 } else { 86 credentials = !!options.credentials; 87 } 88 89 if (credentials && origin === '*') { 90 origin = requestOrigin; 91 } 92 93 const headersSet = {}; 94 95 function set(key, value) { 96 ctx.set(key, value); 97 headersSet[key] = value; 98 } 99 100 // 非预检请求 简单请求 101 if (ctx.method !== 'OPTIONS') { 102 // 考虑配置 Access-Control-Allow-Origin,Access-Control-Allow-Credentials,Access-Control-Expose-Headers 三个响应头 103 // ! 配置响应头 104 // CORS 之 简单请求、浏览器直接发出CORS请求,并且在浏览器请求头添加Origin字段 105 // 简单请求: 106 // 1、请求方法:HEAD、GET、POST 107 // 2、HTTP头信息字段限制,Accept、Accept-Language、Content-Language、Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 108 // 设置接收来自origin域名的请求 109 set('Access-Control-Allow-Origin', origin); 110 111 if (credentials === true) { 112 // 设置允许客户端发送Cookies 113 set('Access-Control-Allow-Credentials', 'true'); 114 } 115 116 // CORS请求,XHR对象的getResponseHeader方法只能获取6个基本字段 117 // Cache-Control Content-Language Content-Type Expires Last-Modified Pragma 118 // 因此如果你需要获取其它字段、则需要在Access-Control-Expose-Headers指定 119 if (options.exposeHeaders) { 120 set('Access-Control-Expose-Headers', options.exposeHeaders); 121 } 122 123 if (options.secureContext) { 124 set('Cross-Origin-Opener-Policy', 'same-origin'); 125 set('Cross-Origin-Embedder-Policy', 'require-corp'); 126 } 127 128 if (!options.keepHeadersOnError) { 129 return await next(); 130 } 131 // 异常情况处理 132 try { 133 return await next(); 134 } catch (err) { 135 const errHeadersSet = err.headers || {}; 136 const varyWithOrigin = vary.append(errHeadersSet.vary || errHeadersSet.Vary || '', 'Origin'); 137 delete errHeadersSet.Vary; 138 139 err.headers = { 140 ...errHeadersSet, 141 ...headersSet, 142 ...{ 143 vary: varyWithOrigin 144 }, 145 }; 146 throw err; 147 } 148 } else { 149 // 预检请求:非简单请求即为预检请求、比如PUT、DELETE方法或者Content-Type: application/json。 150 // 如果是一个非简单请求的CORS请求,在正式通信之前,会增加一次HTTP查询请求,称为预检请求 151 // 预检请求使用的HTTP方法是OPTION,所以你知道上面为什么用OPTION作为判断了 152 153 // 预检请求目的:浏览器发起请求,询问服务器,当前网页是否在服务器允许的origin名单内 154 // 以及想知道允许使用哪些HTTP Method、头信息字段等 155 // 只有得到正确答复、才会发出正式的XHR请求 156 157 // 预检请求一般包括的字段有 158 // Origin: http://api.bob.com 必须会带上 159 // Access-Control-Request-Method: PUT // 必须,这里PUT指的是浏览器预检请求后的XHR请求方法 160 // Access-Control-Request-Headers: X-Custom-Header // 可选 161 162 // 服务器接收预检请求后,会检测Origin、Access-Control-Allow-Methods、以及Access-Control-Request-Headers 163 // 如果请求头不存在Access-Control-Request-Method头、或者解析失败、流程直接终止 164 165 if (!ctx.get('Access-Control-Request-Method')) { 166 // this not preflight request, ignore it 167 return await next(); 168 } 169 170 // 检测正确,允许跨源请求,作出响应 171 // 设置返回请求域白名单 172 ctx.set('Access-Control-Allow-Origin', origin); 173 174 // 设置允许浏览器CORS请求发送Cookies 175 if (credentials === true) { 176 ctx.set('Access-Control-Allow-Credentials', 'true'); 177 } 178 179 if (options.maxAge) { 180 ctx.set('Access-Control-Max-Age', options.maxAge); 181 } 182 // 是否是私有网络访问 只能 访问内网 或 局域网 183 if (options.privateNetworkAccess && ctx.get('Access-Control-Request-Private-Network')) { 184 ctx.set('Access-Control-Allow-Private-Network', 'true'); 185 } 186 187 // 设置服务器支持的所有跨域方法 188 if (options.allowMethods) { 189 ctx.set('Access-Control-Allow-Methods', options.allowMethods); 190 } 191 192 if (options.secureContext) { 193 set('Cross-Origin-Opener-Policy', 'same-origin'); 194 set('Cross-Origin-Embedder-Policy', 'require-corp'); 195 } 196 197 // 设置服务器支持的所有头信息字段 198 let allowHeaders = options.allowHeaders; 199 if (!allowHeaders) { 200 // 如果没有自定义、从请求中获取 201 allowHeaders = ctx.get('Access-Control-Request-Headers'); 202 } 203 // 自定义拓展请求头 204 if (allowHeaders) { 205 ctx.set('Access-Control-Allow-Headers', allowHeaders); 206 } 207 // 204 服务器处理请求、但是没有返回内容 208 ctx.status = 204; 209 } 210 }; 211 };
标签:Control,Origin,请求,koa,ctx,Access,源码,cors,options From: https://www.cnblogs.com/taue997/p/17365134.html