Невозможно сделать POST-запрос с JSON от реакции/аксиоса для восстановления сервера

У меня есть restify, настроенный следующим образом:

var restify = require('restify');

const server = restify.createServer();

//server.use(restify.plugins.acceptParser(server.acceptable)); // [1]
server.use(restify.plugins.queryParser());
server.use(restify.plugins.bodyParser());

server.use(function(req, res, next) {
  console.log(req); // <-- Never see the POST from React here
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Headers', '*');
  res.setHeader('Access-Control-Allow-Methods', '*');
  next();
});

Я определяю кучу маршрутов GET и POST, и пока все работает отлично. Я вызывал сервер из Android-приложения, скриптов Python и для тестирования просто использовал curl. Никаких проблем. Аккуратный!

Однако теперь я реализовал веб-приложение с помощью React и хочу сделать запрос к API восстановления с помощью пакета axios. GET запросы в порядке, поэтому я исключаю любые опечатки в URL или подобные вещи.

Но запрос POST, подобный следующему, не будет работать:

var data = {"test": "hello"};
axios.post("http://.../api/v1/message/question/public/add", data)
  .then(function (response) {
    console.log("Test question sent!");
  })
  .catch(function (error) {
    console.log(error);
  });

Когда я проверяю инструменты разработчика браузера, я вижу, что браузер пытается сделать запрос OPTIONS (а не POST) к этому URL-адресу. Я предполагаю, из того, что я прочитал, это потому, что браузер делает «предварительный запрос». Проблема в том, что я получаю ошибку 405 Method Not Allowed:

Request URL: http://.../api/v1/message/question/public/add
Request method: OPTIONS
Remote address: ...
Status code: 405 Method Not Allowed

Response headers (178 B)    
  Server: restify
  Allow: POST
  Content-Type: application/json
  Content-Length: 62
  Date: Sat, 09 Sep 2017 08:16:32 GMT
  Connection: keep-alive
Request headers (485 B) 
  Host: ...
  User-Agent: Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/55.0
  Accept: text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8
  Accept-Language: en-ZA,en-GB;q=0.8,en-US;q=0.5,en;q=0.3
  Accept-Encoding: gzip, deflate
  Access-Control-Request-Method: POST
  Access-Control-Request-Headers: content-type
  Origin: http://...
  DNT: 1
  Connection: keep-alive

Но почему? Я разрешаю все Access-Control-Allow-Methods в restify. Все работает, кроме POST запросов и только тогда, когда они приходят из браузера (с веб-приложением React). Я думаю, это из-за запроса OPTIONS, но я понятия не имею, как с этим справиться.

Кстати с JSON.stringify(data) запросы POST проходят, но API ожидает Json а не строку. А так как со всеми другими средствами он работает отлично, я не хочу менять код переустановки только для решения этой проблемы.

[1] Если я использую эту строку, я получаю следующую ошибку: AssertionError [ERR_ASSERTION]: acceptable ([string]) is required at Object.acceptParser (/home/bob/node_modules/restify/lib/plugins/accept.js:30:12)


person Christian    schedule 09.09.2017    source источник


Ответы (2)


Еще через пару часов я наконец нашел решение. Я изменил свой код сервера restify следующим образом:

var restify = require("restify");
var corsMiddleware = require('restify-cors-middleware')

var cors = corsMiddleware({
  preflightMaxAge: 5, //Optional
  origins: ['*'],
  allowHeaders: ['API-Token'],
  exposeHeaders: ['API-Token-Expiry']
});


// WITHOUT HTTPS
const server = restify.createServer();

server.pre(cors.preflight);
server.use(cors.actual);
...
(rest is the same as above)

Честно говоря, я понятия не имею, что он на самом деле делает. Но это работает и уже сегодня стоило мне слишком много энергии. Я надеюсь, что это поможет другим в какой-то момент. Все кажется довольно недавними изменениями от restify.

person Christian    schedule 09.09.2017

Если ваш сервер не разрешает OPTIONS по умолчанию, вы можете добавить явный обработчик:

server.opts(/\.*/, function (req, res, next) {
    res.send(200);
    next();
});

Другая возможная проблема заключается в том, что у вас не будет эффекта, предназначенного для следующих заголовков:

res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader('Access-Control-Allow-Methods', '*');

Подстановочные знаки * разрешены спецификацией, но браузеры их пока не поддерживают. Вместо этого вы должны явно указать в этих значениях разрешенные методы и заголовки:

res.setHeader('Access-Control-Allow-Headers', 'content-type');
res.setHeader('Access-Control-Allow-Methods', 'POST');

Это связано с тем, что заголовки запросов, показанные для запроса предварительной проверки CORS OPTIONS, имеют следующее:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://...

Эти заголовки запросов указывают на то, что браузер запрашивает сервер: Некоторый код, работающий в этом источнике, хочет сделать POST запрос к вашему серверу, который добавляет к запросу определенное Content-Type. Вы согласны с получением POST запросов, которые добавляют собственные Content-Type?

Таким образом, в ответ на этот предварительный запрос OPTIONS браузер ожидает получить заголовок ответа Access-Control-Allow-Methods, который явно разрешает POST, а также заголовок ответа Access-Control-Allow-Headers, который явно разрешает Content-Type.

person sideshowbarker    schedule 09.09.2017
comment
Я нашел решение, пожалуйста, смотрите мой ответ. На самом деле я пробовал явные настройки вместо подстановочных знаков, но ничего не сработало. Был ли OPTIONS в Access-Control-Allow-Methods или нет, не имело никакого значения. Я быстро попробовал ваше решение. Запрос OPTIONS действительно возвращает 200 сейчас, но POST продолжения нет. Опять же, независимо от того, использую ли я подстановочные знаки или нет. Думаю, я все еще что-то упускаю. - person Christian; 09.09.2017
comment
Ваш ответ, безусловно, пошел в правильном направлении, и я нахожу ваше непрофессиональное объяснение в конце очень полезным - оно далеко от моего прошлого. Отсюда мой плюс. Очень признателен! - person Christian; 09.09.2017