index.js 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /**
  2. * CORS middleware for koa2
  3. *
  4. * @param {Object} [options]
  5. * - {String|Function(ctx)} origin `Access-Control-Allow-Origin`, default is request Origin header
  6. * - {Array} exposeHeaders `Access-Control-Expose-Headers`
  7. * - {String|Number} maxAge `Access-Control-Max-Age` in seconds
  8. * - {Boolean} credentials `Access-Control-Allow-Credentials`
  9. * - {Array} allowMethods `Access-Control-Allow-Methods`,
  10. * default is ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
  11. * - {Array} allowHeaders `Access-Control-Allow-Headers`
  12. * @return {Function}
  13. * @api public
  14. */
  15. module.exports = function crossOrigin(options = {}) {
  16. const defaultOptions = {
  17. allowMethods: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
  18. };
  19. // set defaultOptions to options
  20. options = Object.assign({}, defaultOptions, options); // eslint-disable-line no-param-reassign
  21. // eslint-disable-next-line consistent-return
  22. return async function cors(ctx, next) {
  23. // always set vary Origin Header
  24. // https://github.com/rs/cors/issues/10
  25. ctx.vary('Origin');
  26. let origin;
  27. if (typeof options.origin === 'function') {
  28. origin = options.origin(ctx);
  29. } else {
  30. origin = options.origin || ctx.get('Origin') || '*';
  31. }
  32. if (!origin) {
  33. return await next();
  34. }
  35. // Access-Control-Allow-Origin
  36. ctx.set('Access-Control-Allow-Origin', origin);
  37. if (ctx.method === 'OPTIONS') {
  38. // Preflight Request
  39. if (!ctx.get('Access-Control-Request-Method')) {
  40. return await next();
  41. }
  42. // Access-Control-Max-Age
  43. if (options.maxAge) {
  44. ctx.set('Access-Control-Max-Age', String(options.maxAge));
  45. }
  46. // Access-Control-Allow-Credentials
  47. if (options.credentials === true) {
  48. // When used as part of a response to a preflight request,
  49. // this indicates whether or not the actual request can be made using credentials.
  50. ctx.set('Access-Control-Allow-Credentials', 'true');
  51. }
  52. // Access-Control-Allow-Methods
  53. if (options.allowMethods) {
  54. ctx.set('Access-Control-Allow-Methods', options.allowMethods.join(','));
  55. }
  56. // Access-Control-Allow-Headers
  57. if (options.allowHeaders) {
  58. ctx.set('Access-Control-Allow-Headers', options.allowHeaders.join(','));
  59. } else {
  60. ctx.set('Access-Control-Allow-Headers', ctx.get('Access-Control-Request-Headers'));
  61. }
  62. ctx.status = 204; // No Content
  63. } else {
  64. // Request
  65. // Access-Control-Allow-Credentials
  66. if (options.credentials === true) {
  67. if (origin === '*') {
  68. // `credentials` can't be true when the `origin` is set to `*`
  69. ctx.remove('Access-Control-Allow-Credentials');
  70. } else {
  71. ctx.set('Access-Control-Allow-Credentials', 'true');
  72. }
  73. }
  74. // Access-Control-Expose-Headers
  75. if (options.exposeHeaders) {
  76. ctx.set('Access-Control-Expose-Headers', options.exposeHeaders.join(','));
  77. }
  78. try {
  79. await next();
  80. } catch (err) {
  81. throw err;
  82. }
  83. }
  84. };
  85. };