Почему добавление заголовков CORS к маршруту OPTIONS не позволяет браузерам получать доступ к моему API?

Я пытаюсь поддерживать CORS в моем приложении Node.js, которое использует веб-фреймворк Express.js. Я прочитал обсуждение в группе Google о том, как с этим справиться, и прочитайте несколько статей о том, как работает CORS. Во-первых, я сделал это (код написан в синтаксисе CoffeeScript):

app.options "*", (req, res) ->
  res.header 'Access-Control-Allow-Origin', '*'
  res.header 'Access-Control-Allow-Credentials', true
  # try: 'POST, GET, PUT, DELETE, OPTIONS'
  res.header 'Access-Control-Allow-Methods', 'GET, OPTIONS'
  # try: 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'
  res.header 'Access-Control-Allow-Headers', 'Content-Type'
  # ...

Кажется, он не работает. Кажется, что мой браузер (Chrome) не отправляет первоначальный запрос OPTIONS. Когда я только что обновил блок для ресурса, мне нужно отправить запрос GET с кросс-началом:

app.get "/somethingelse", (req, res) ->
  # ...
  res.header 'Access-Control-Allow-Origin', '*'
  res.header 'Access-Control-Allow-Credentials', true
  res.header 'Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS'
  res.header 'Access-Control-Allow-Headers', 'Content-Type'
  # ...

Он работает (в Chrome). Это также работает в Safari.

Я читал, что...

В браузере, реализующем CORS, каждому запросу GET или POST для кросс-начала предшествует запрос OPTIONS, который проверяет, является ли GET или POST в порядке.

Итак, мой главный вопрос: почему это не происходит в моем случае? Почему не вызван мой блок app.options? Почему мне нужно установить заголовки в основном блоке app.get?

Ответ 1

Чтобы ответить на ваш главный вопрос, спецификация CORS требует, чтобы вызов OPTIONS предшествовал POST или GET, если в POST или GET есть непростой контент или заголовки.

Типы контента, для которых требуется предполетный запрос CORS (вызов OPTIONS), это любой тип контента, кроме следующих:

  1. application/x-www-form-urlencoded
  2. multipart/form-data
  3. text/plain

Любые другие типы контента, кроме перечисленных выше, вызовут предполетный запрос.

Что касается Заголовков, любые Заголовки Запроса кроме следующего вызовут предполетный запрос:

  1. Accept
  2. Accept-Language
  3. Content-Language
  4. Content-Type
  5. DPR
  6. Save-Data
  7. Viewport-Width
  8. Width

Любые другие заголовки запроса активируют предполетный запрос.

Таким образом, вы можете добавить пользовательский заголовок, такой как: x-Trigger: CORS, и это должно вызвать предполетный запрос и попасть в блок OPTIONS.

См. Справочник по MDN Web API - Предварительные запросы CORS.

Ответ 2

Я нашел самый простой способ использовать пакет node.js cors. Простейшее использование:

var cors = require('cors')

var app = express()
app.use(cors())

Есть, конечно, много способов настроить поведение к вашим потребностям; приведенная выше страница показывает ряд примеров.

Ответ 3

Попробуйте передать управление следующему соответствующему маршруту. Если Express сначала соответствует маршруту app.get, то он не будет продолжать путь опций, если вы этого не сделаете (обратите внимание на использование следующего):

app.get('somethingelse', function(req, res, next) {
    //..set headers etc.

    next();
});

Что касается организации материала CORS, я помещал его в промежуточное программное обеспечение, которое хорошо работает для меня:

//CORS middleware
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'example.com');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    next();
}

//...
app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.cookieParser());
    app.use(express.session({ secret: 'cool beans' }));
    app.use(express.methodOverride());
    app.use(allowCrossDomain);
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

Ответ 4

Остаться в той же идее маршрутизации. Я использую этот код:

app.all('/*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  next();
});

Аналогично http://enable-cors.org/server_expressjs.html example

Ответ 5

делать

npm install cors --save

и просто добавьте эти строки в ваш основной файл, куда направляется ваш запрос (сохраните его перед любым маршрутом).

const cors = require('cors');
const express = require('express');
let app = express();
app.use(cors());
app.options('*', cors());

Ответ 6

Я сделал более полное промежуточное программное обеспечение, подходящее для экспресс-связи или подключения. Он поддерживает запросы OPTIONS для проверки предполетного контроля. Обратите внимание, что это позволит CORS получить доступ ко всему, вы можете захотеть внести некоторые проверки, если хотите ограничить доступ.

app.use(function(req, res, next) {
    var oneof = false;
    if(req.headers.origin) {
        res.header('Access-Control-Allow-Origin', req.headers.origin);
        oneof = true;
    }
    if(req.headers['access-control-request-method']) {
        res.header('Access-Control-Allow-Methods', req.headers['access-control-request-method']);
        oneof = true;
    }
    if(req.headers['access-control-request-headers']) {
        res.header('Access-Control-Allow-Headers', req.headers['access-control-request-headers']);
        oneof = true;
    }
    if(oneof) {
        res.header('Access-Control-Max-Age', 60 * 60 * 24 * 365);
    }

    // intercept OPTIONS method
    if (oneof && req.method == 'OPTIONS') {
        res.send(200);
    }
    else {
        next();
    }
});

Ответ 7

установить cors модуль expressjs. вы можете выполнить следующие шаги >

Установка

npm install cors

Простое использование (разрешить все запросы CORS)

var express = require('express');
var cors = require('cors');
var app = express();
app.use(cors());

для более подробной информации перейдите к https://github.com/expressjs/cors

Ответ 8

Сделайте что-то вроде этого:

app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

Ответ 9

Тестирование выполняется с помощью экспресс + node + ионного запуска в разных портах.

Localhost:8100

Localhost:5000

// CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests

app.all('*', function(req, res, next) {
       res.header("Access-Control-Allow-Origin", "*");
       res.header("Access-Control-Allow-Headers", "X-Requested-With");
       res.header('Access-Control-Allow-Headers', 'Content-Type');
       next();
});

Ответ 10

сначала просто установите корс в свой проект. Возьмите терминал (командная строка) и cd в каталог проекта и выполните следующую команду:

npm install cors --save

Затем возьмите файл server.js и измените код, чтобы добавить в него следующее:

var cors = require('cors');


var app = express();

app.use(cors());

app.use(function(req, res, next) {
   res.header("Access-Control-Allow-Origin", "*");
   res.header('Access-Control-Allow-Methods', 'DELETE, PUT, GET, POST');
   res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
   next();
});

Это сработало для меня..

Ответ 11

Это работает для меня, поскольку это легкая реализация внутри маршрутов, im с использованием meanjs и его работоспособного режима, сафари, хром и т.д.

app.route('/footer-contact-form').post(emailer.sendFooterMail).options(function(req,res,next){ 
        res.header('Access-Control-Allow-Origin', '*'); 
        res.header('Access-Control-Allow-Methods', 'GET, POST');
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
        return res.send(200);

    });

Ответ 12

Некоторое время назад я столкнулся с этой проблемой, поэтому я сделал это, чтобы разрешить CORS в моем приложении nodejs:

Сначала вам нужно установить cors, используя следующую команду:

npm install cors --save

Теперь добавьте следующий код в ваше приложение, начиная файл (app.js or server.js)

var express = require('express');
var app = express();

var cors = require('cors');
var bodyParser = require('body-parser');

//enables cors
app.use(cors({
  'allowedHeaders': ['sessionId', 'Content-Type'],
  'exposedHeaders': ['sessionId'],
  'origin': '*',
  'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
  'preflightContinue': false
}));

require('./router/index')(app);

Ответ 13

Если вы хотите сделать его контроллером конкретным, вы можете использовать:

res.setHeader('X-Frame-Options', 'ALLOWALL');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');

Обратите внимание, что это также позволит использовать iframes.

Ответ 14

В моем index.js я добавил:

app.use((req, res, next) => {
   res.header("Access-Control-Allow-Origin", "*");
   res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
   res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
   next();
}) 

Ответ 15

Мое самое простое решение с Express 4.2.0 (EDIT: похоже, не работает в 4.3.0):

function supportCrossOriginScript(req, res, next) {
    res.status(200);
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Content-Type");

    // res.header("Access-Control-Allow-Headers", "Origin");
    // res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    // res.header("Access-Control-Allow-Methods","POST, OPTIONS");
    // res.header("Access-Control-Allow-Methods","POST, GET, OPTIONS, DELETE, PUT, HEAD");
    // res.header("Access-Control-Max-Age","1728000");
    next();
}

// Support CORS
app.options('/result', supportCrossOriginScript);

app.post('/result', supportCrossOriginScript, function(req, res) {
    res.send('received');
    // do stuff with req
});

Я полагаю, что выполнение app.all('/result', ...) тоже будет работать...

Ответ 16

Можете сослаться на код ниже для того же. Источник: Academind/node-restful-api

const express = require('express');
const app = express();

//acts as a middleware
//to handle CORS Errors
app.use((req, res, next) => { //doesn't send response just adjusts it
    res.header("Access-Control-Allow-Origin", "*") //* to give access to any origin
    res.header(
        "Access-Control-Allow-Headers",
        "Origin, X-Requested-With, Content-Type, Accept, Authorization" //to give access to all the headers provided
    );
    if(req.method === 'OPTIONS'){
        res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET'); //to give access to all the methods provided
        return res.status(200).json({});
    }
    next(); //so that other routes can take over
})

Ответ 17

В дополнение к тому, что говорили другие, не забывайте, что, если вы не используете nodemon, вам потребуется перезагрузить сервер node, чтобы изменения вступили в силу!

Я лично обновлял свой браузер по привычке, забыв, что это код на стороне сервера.

Ответ 18

Использование Express Middleware отлично подходит для меня. Если вы уже используете Express, просто добавьте следующие правила промежуточного слоя. Он должен начать работать.

app.all("/api/*", function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With");
  res.header("Access-Control-Allow-Methods", "GET, PUT, POST");
  return next();
});

app.all("/api/*", function(req, res, next) {
  if (req.method.toLowerCase() !== "options") {
    return next();
  }
  return res.send(204);
});

Справка

Ответ 19

Мне было очень легко сделать это с пакетом запросов npm (https://www.npmjs.com/package/request)

Затем я основал свое решение на этом посту http://blog.javascripting.com/2015/01/17/dont-hassle-with-cors/

'use strict'

const express = require('express');
const request = require('request');

let proxyConfig = {
    url : {
        base: 'http://servertoreach.com?id=',
    }
}

/* setting up and configuring node express server for the application */
let server = express();
server.set('port', 3000);


/* methods forwarded to the servertoreach proxy  */
server.use('/somethingElse', function(req, res)
{
    let url = proxyConfig.url.base + req.query.id;
    req.pipe(request(url)).pipe(res);
});


/* start the server */
server.listen(server.get('port'), function() {
    console.log('express server with a proxy listening on port ' + server.get('port'));
});

Ответ 20

В машинописном тексте, если вы хотите использовать пакет node.js cors

/**
* app.ts
* If you use the cors library
*/

import * as express from "express";
[...]
import * as cors from 'cors';

class App {
   public express: express.Application;

   constructor() {
       this.express = express();
       [..]
       this.handleCORSErrors();
   }

   private handleCORSErrors(): any {
       const corsOptions: cors.CorsOptions = {
           origin: 'http://example.com',
           optionsSuccessStatus: 200
       };
       this.express.use(cors(corsOptions));
   }
}

export default new App().express;

Если вы не хотите использовать библиотеки сторонних компонентов для обработки ошибок cors, вам необходимо изменить метод handleCORSErrors().

/**
* app.ts
* If you do not use the cors library
*/

import * as express from "express";
[...]

class App {
   public express: express.Application;

   constructor() {
       this.express = express();
       [..]
       this.handleCORSErrors();
   }

   private handleCORSErrors(): any {
       this.express.use((req, res, next) => {
           res.header("Access-Control-Allow-Origin", "*");
           res.header(
               "Access-Control-ALlow-Headers",
               "Origin, X-Requested-With, Content-Type, Accept, Authorization"
           );
           if (req.method === "OPTIONS") {
               res.header(
                   "Access-Control-Allow-Methods",
                   "PUT, POST, PATCH, GET, DELETE"
               );
               return res.status(200).json({});
           } 
           next(); // send the request to the next middleware
       });
    }
}

export default new App().express;

Для использования файла app.ts

/**
* server.ts
*/
import * as http from "http";
import app from "./app";

const server: http.Server = http.createServer(app);

const PORT: any = process.env.PORT || 3000;
server.listen(PORT);

Ответ 21

Это похоже на ответ Пэта с той разницей, что я заканчиваю с res.sendStatus(200); вместо следующего();

Код будет перехватывать все запросы типа метода OPTIONS и отправлять обратно заголовки контроля доступа.

app.options('/*', (req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
    res.sendStatus(200);
});

Код принимает CORS из всех источников в соответствии с запросом в вопросе. Тем не менее, было бы лучше заменить * конкретным источником, то есть http://localhost: 8080, чтобы предотвратить неправильное использование.

Поскольку мы используем метод app.options вместо метода app.use, нам не нужно выполнять эту проверку:

req.method === 'OPTIONS'

который мы можем увидеть в некоторых других ответах.

Я нашел ответ здесь: http://johnzhang.io/options-request-in-express.

Ответ 23

Вы можете использовать промежуточное программное обеспечение Express, заблокировать домен и методы.

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", process.env.DOMAIN); // update to match the domain you will make the request from
  res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  next();
});

Ответ 24

Мы можем избежать CORS и перенаправлять запросы на другой сервер:

// config:
var public_folder = __dirname + '/public'
var apiServerHost = 'http://other.server'

// code:
console.log("starting server...");

var express = require('express');
var app = express();
var request = require('request');

// serve static files
app.use(express.static(public_folder));

// if not found, serve from another server
app.use(function(req, res) {
    var url = apiServerHost + req.url;
    req.pipe(request(url)).pipe(res);
});

app.listen(80, function(){
    console.log("server ready");
});

Ответ 25

Я использовал следующие шаги для своего веб-приложения, и у меня был успех:

Добавьте пакет cors в экспресс:

npm install cors --save

Добавьте следующие строки после конфигурации bodyParser. У меня были некоторые неприятности, добавленные перед bodyParser:

 // enable cors to the server
const corsOpt = {
    origin: process.env.CORS_ALLOW_ORIGIN || '*', // this work well to configure origin url in the server
    methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS'], // to works well with web app, OPTIONS is required
    allowedHeaders: ['Content-Type', 'Authorization'] // allow json and token in the headers
};
app.use(cors(corsOpt)); // cors for all the routes of the application
app.options('*', cors(corsOpt)); // automatic cors gen for HTTP verbs in all routes, This can be redundant but I kept to be sure that will always work.

Ответ 26

const express = require("express");
const router = express.Router();
const app = express();
const cors = require("cors"); 



const options = {
  origin: true,
  "Access-Control-Allow-Credentials": true,

  "Access-Control-Allow-Origin": true,
  "Access-Control-Allow-Headers": true,
  "Access-Control-Expose-Headers": true
};


app.use(cors(options));
router.get("/", cors(options), (req, res) => {
  res.render("index", { title: "Express" });
});

module.exports = router;

Для GOOGLE CHROME необходимо добавить расширение "Allow-Control-Allow-Origin"

Заголовок Access-Control-Allow-Origin (заголовок ответа) указывает, можно ли разделить ресурс на основе возврата значения заголовка запроса источника. Заголовок Access-Control-Allow-Origin открывает дверь для перекрестного доступа

Access-Control-Allow-Credentials (заголовок ответа): true используется, чтобы указать, что клиенту разрешено использовать учетные данные. По умолчанию ответы на запросы с учетными данными не могут быть прочитаны клиентским скриптом. Таким образом, вы можете получить доступ к ответу. Кроме того, по умолчанию CORS не включает файлы cookie в запросах с несколькими источниками, и это может привести к классу уязвимостей, который называется подделкой запросов между сайтами или CSRF. Чтобы снизить вероятность появления уязвимостей CSRF в CORS, CORS требует, чтобы и сервер, и клиент подтвердили, что файлы cookie можно включать в запросы.

Access-Control-Expose-Headers (заголовок ответа) указывает, какие заголовки безопасны для показа. Список заголовков ответа можно прочитать. По умолчанию отображаются только 6 простых заголовков ответов: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma.

Access-Control-Request-Headers (заголовок запроса) указывает, какие заголовки HTTP можно использовать во время фактического запроса.

origin = true (заголовок запроса) отражает происхождение запроса. Если это неверно, Cors будет отключен.

Ответ 27

Если бы я был тобой @OP, я бы изменил свою парадигму программирования.

при условии, что вы блокируете эти CORS, потому что вы делаете запросы на localhost или что-то подобное.

В конце концов, если вы собираетесь развернуть производственные оптоины, такие как Google Cloud Platform или Heroku, или вам не нужно будет беспокоиться о CORS, например, разрешить источник или что-то еще в процессе производства.

поэтому при тестировании сервера просто используйте postman, и вы не заблокируете CORS, после этого разверните свой сервер и затем работайте на своем клиенте.

Ответ 28

Ниже приведен код, но сначала установите cors:

npm установить --save корс

Затем:

module.exports = function(app) { 
var express = require("express");
var cors = require('cors');
var router = express.Router();
app.use(cors());

app.post("/movies",cors(), function(req, res) { 
res.send("test");
});

Ответ 29

используя nodejs без экспресс/внешних библиотек Я использовал метод ниже в моем файле server.js. Ключевыми частями здесь является получение источника из заголовка запроса, а затем разрешение его в ответе сервера, после чего мы можем установить заголовок, который будет возвращен, включая разрешенный источник, если совпадение найдено.

    **const origin = req.headers.origin;**

      let decoder = new StringDecoder('utf-8');
      let buffer = '';
      req.on('data', function (data) {
        buffer += decoder.write(data);
      });
      req.on('end', function () {
        buffer += decoder.end();

        let chosenHandler = typeof (server.router[trimmedPath]) !== 'undefined' ? server.router[trimmedPath] : handlers.notFound;

const data = { ....data object vars}

// should be wrapped in try catch block
      chosenHandler(data, function (statusCode, payload, contentType) {
        server.processHandlerResponse(res, method, trimmedPath, statusCode, payload, contentType, **origin**);


server.processHandlerResponse = function (res, method, trimmedPath, statusCode, payload, contentType, origin) {
  contentType = typeof (contentType) == 'string' ? contentType : 'json';

  statusCode = typeof (statusCode) == 'number' ? statusCode : 200;

  let payloadString = '';
  if (contentType == 'json') {
    res.setHeader('Content-Type', 'application/json');

    const allowedOrigins = ['https://www.domain1.com', 'https://someotherdomain','https://yetanotherdomain',
    ...// as many as you need
  ];
    **if (allowedOrigins.indexOf(origin) > -1) {
        res.setHeader('Access-Control-Allow-Origin', origin);
    }**
    payload = typeof (payload) == 'object' ? payload : {};
    payloadString = JSON.stringify(payload);
  }

... //  if (other content type) ...rinse and repeat..

Ответ 30

Самый простой подход - установить модуль Cors в ваш проект, используя:

npm i --save cors

Затем в файл вашего сервера импортируйте его, используя следующее:

import cors from 'cors';

Затем просто используйте его в качестве промежуточного ПО, например:

app.use(cors());

Надеюсь это поможет!