CORS(跨源资源共享) 是一种允许一个域上的 Web 应用程序访问另一个域上的资源的机制。这在开发前端和后端分离并通过 API 进行通信的应用程序时至关重要。
这里有一篇文章解释了在 Node.js 和 Express 中不使用外部库的 CORS 实现:
"use strict"; /*jshint node:true */ var simpleMethods, simpleRequestHeaders, simpleResponseHeaders, toLowerCase, checkOriginMatch, origin; Object.defineProperty(exports, "simpleMethods", { get: function () { return [ "GET", "HEAD", "POST", "PUT", "DELETE" ]; } }); simpleMethods = exports.simpleMethods; Object.defineProperty(exports, "origin", { get: function () { return ["http://localhost:3000"]; } }); origin = exports.origin;
Export simpleMethods:定义 CORS 请求允许的 HTTP 方法(例如 GET、POST、PUT 等)。
导出来源:指定允许访问的来源列表。在此示例中,允许使用 http://localhost:3000。
Object.defineProperty(exports, "simpleRequestHeaders", { get: function () { return ["accept", "accept-language", "content-language", "content-type", "authorization", "token"]; } }); simpleRequestHeaders = exports.simpleRequestHeaders; Object.defineProperty(exports, "simpleResponseHeaders", { get: function () { return ["cache-control", "content-language", "content-type", "expires", "last-modified", "pragma"]; } }); simpleResponseHeaders = exports.simpleResponseHeaders;
导出 simpleRequestHeaders:定义跨域请求中客户端允许的请求标头。
导出 simpleResponseHeaders:定义允许从服务器到客户端的响应标头。
checkOriginMatch = function (originHeader, origins, callback) { if (typeof origins === "function") { origins(originHeader, function (err, allow) { callback(err, allow); }); } else if (origins.length > 0) { callback(null, origins.some(function (origin) { return origin === originHeader; })); } else { callback(null, true); } };
函数 checkOriginMatch:检查请求来源是否与允许的来源列表匹配。如果匹配,则请求被允许。
exports.create = function (options) { options = options || {}; options.origins = options.origins || origin; options.methods = options.methods || simpleMethods;
来源和方法选项的初始化,如果没有提供,则使用来自 origin 和 simpleMethods 的默认值。
设置请求和响应标头
if (options.hasOwnProperty("requestHeaders") === true) { options.requestHeaders = toLowerCase(options.requestHeaders); } else { options.requestHeaders = simpleRequestHeaders; } if (options.hasOwnProperty("responseHeaders") === true) { options.responseHeaders = toLowerCase(options.responseHeaders); } else { options.responseHeaders = simpleResponseHeaders; }
设置允许的请求(requestHeaders)和响应(responseHeaders)标头。将任何给定的请求或响应标头转换为小写。
附加中间件配置
options.maxAge = options.maxAge || null; options.supportsCredentials = options.supportsCredentials || false; if (options.hasOwnProperty("endPreflightRequests") === false) { options.endPreflightRequests = true; }
maxAge:指定 CORS 预检的最大缓存期限。 supportCredentials:确定服务器是否支持跨域请求中的凭据(cookie 或令牌)。 endPreflightRequests:决定服务器是否应终止预检请求(选项)或继续执行下一个中间件。
return function (req, res, next) { if (!req.headers.hasOwnProperty("origin")) { next(); } else { checkOriginMatch(req.headers.origin, options.origins, function (err, originMatches) { if (err !== null) { next(err); } else { var endPreflight = function () { if (options.endPreflightRequests === true) { res.writeHead(204); res.end(); } else { next(); } };
函数 endPreflight:如果 endPreflightRequests 设置为 true,则结束预检(OPTIONS)请求。来源检查:使用 checkOriginMatch 来验证请求来源是否与允许的来源匹配。
处理预检请求(选项)
if (req.method === "OPTIONS") { if (!req.headers.hasOwnProperty("access-control-request-method")) { endPreflight(); } else { requestMethod = req.headers["access-control-request-method"]; if (req.headers.hasOwnProperty("access-control-request-headers")) { requestHeaders = toLowerCase(req.headers["access-control-request-headers"].split(/,\s*/)); } else { requestHeaders = []; } methodMatches = options.methods.indexOf(requestMethod) !== -1; if (!methodMatches) { endPreflight(); } else { headersMatch = requestHeaders.every(function (requestHeader) { return options.requestHeaders.includes(requestHeader); }); if (!headersMatch) { endPreflight(); } else { if (options.supportsCredentials) { res.setHeader("Access-Control-Allow-Origin", req.headers.origin); res.setHeader("Access-Control-Allow-Credentials", "true"); } else { res.setHeader("Access-Control-Allow-Origin", "*"); } if (options.maxAge !== null) { res.setHeader("Access-Control-Max-Age", options.maxAge); } res.setHeader("Access-Control-Allow-Methods", options.methods.join(",")); res.setHeader("Access-Control-Allow-Headers", options.requestHeaders.join(",")); endPreflight(); } } } }
请求方法和标头匹配:检查请求方法和标头是否与允许的匹配。 CORS 响应标头:设置 CORS 标头,例如 Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Allow-Methods 等
Exposing Headers in the Response
} else {
if (options.supportsCredentials) {
res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
} else {
res.setHeader("Access-Control-Allow-Origin", "*");
}
exposedHeaders = options.responseHeaders.filter(function (header) { return !simpleResponseHeaders.includes(header); }); if (exposedHeaders.length > 0) { res.setHeader("Access-Control-Expose-Headers", exposedHeaders.join(",")); } next(); } } }); } };
} else { if (options.supportsCredentials) { res.setHeader("Access-Control-Allow-Origin", req.headers.origin); res.setHeader("Access-Control-Allow-Credentials", "true"); } else { res.setHeader("Access-Control-Allow-Origin", "*"); } exposedHeaders = options.responseHeaders.filter(function (header) { return !simpleResponseHeaders.includes(header); }); if (exposedHeaders.length > 0) { res.setHeader("Access-Control-Expose-Headers", exposedHeaders.join(",")); } next(); } } }); } };
Access-Control-Expose-Headers: Sets response headers that are accessible to the client if there are custom headers not included in simpleResponseHeaders.
This is how you can implement custom CORS in Node.js without using any library. For the complete script, you can refer to this example.
以上是Nodejs 自定义 CORS的详细内容。更多信息请关注PHP中文网其他相关文章!