Я делаю запросы с помощью приложения Angular2+ к API NodeJS.
Пока я не добавляю пользовательские заголовки, звонки выполняются, и я получаю ответ в соответствии с моим запросом. Но если у меня был заголовок вручную с использованием interceptor
, это приводит к ошибке.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at [api_url]. (Reason: CORS request did not succeed)
Вот результат метода входа в систему, когда я сначала вызываю сервер без заголовков, а затем добавляю заголовки и снова вызываю сервер.
Заголовки первого метода POST
И один из второго метода POST
Два пользовательских заголовка добавляются перехватчиком в Angular следующим образом.
...
const cloned = req.clone({
headers: req.headers.set("Authorization", "Bearer " + idToken)
.set("X-QWEMP-data", encrypted)
});
req = cloned
...
И когда я их удаляю, маршрут достигается, но, естественно, выдает ошибку из-за отсутствия данных.
Что касается моего API, я перепробовал множество конфигураций, позволяющих использовать настраиваемые заголовки. Сначала нужно было добавить заголовки и вернуть ОПЦИИ предварительной проверки вручную, как показано ниже (не помню точно, как я это сделал, но что-то вроде этого)
app.use((req,res,next) => {
set.headers('Access-Control-Allow-Origin','*')
set.headers('Access-Control-Allow-Headers','Authorization,X-QWEMP-data')
if (req.method === 'OPTIONS') {
res.send()
return;
}else{
next()
return;
}
})
Затем я использовал библиотеку cors
и конфигурацию по умолчанию, просто добавив app.use(cors())
И, наконец, я попытался добавить пользовательскую конфигурацию в cors, например, следовать (весь файл app.js
)
require('dotenv').config()
const express = require('express');
const indexRouter = require('../routes/index');
const bodyParser = require('body-parser')
const cors = require('cors')
const morgan = require('morgan');
const app = express();
app.use(cors({
methods:'GET,POST,PUT,DELETE,OPTIONS',
allowedHeaders:'Authorization,X-QWEMP-data,Content-Type,[and all the already accepted headers in case]',
exposedHeaders:'[same as allowedHeaders]',
credentials:true,
preflightContinue:true
})
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(morgan('tiny'));
app.use('/', indexRouter)
app.listen(3000, () => {
console.info('Server listening on port 3000.')
})
РЕДАКТИРОВАТЬ
Экраны запросов во вкладке сети
Первый ВАРИАНТ
Первый пост
Второй вариант
Второй пост
@alexis, вы заметите в документе cors, что app.use(cors())
по умолчанию не обрабатывает запросы OPTIONS, поскольку методы по умолчанию: "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
. См. документ здесь.
@sideshowbarker, я тестировал app.options('*', cors())
перед другими маршрутами, но не помогает. @ jfriend00, я добавил метод «OPTIONS» в конфигурацию cors, как вы можете видеть в вопросе.
Я смущен здесь. То, что вы пометили как First OPTION и First POST, успешно выполнено. Эти запросы выполняются успешно. First OPTION — это статус 204 (как и ожидалось), а First POST — это 200. Похоже, это работает. Я не знаю, достиг ли он желаемого кода маршрута сервера, но оба запроса возвращают успешные HTTP-ответы с сервера.
То, что вы пометили как второй вариант, похоже, тоже работает (статус 204). Кажется, что второй POST никогда не отправляет ответ с сервера, или вы что-то упускаете из снимка экрана (без статуса и без заголовков ответа).
В этом проблема, как я сказал в начале своего вопроса, пока я не добавляю пользовательские заголовки, все в порядке, но если я добавлю пользовательский заголовок в запрос с помощью перехватчика или чего-либо еще, я просто не могу получить результат из-за CORS. Итак, здесь в моем методе я делаю 2 вызова на сервер, первый простой и не требует никаких пользовательских заголовков, поэтому все в порядке, а второй требует пользовательских заголовков и выдает ошибку.
Добавление настраиваемого заголовка приводит к тому, что браузеру требуется предварительная проверка для запроса между источниками. Это означает, что браузер отправит запрос OPTIONS на ваш сервер по тому же пути и должен вернуть ответ 2xx с соответствующими заголовками из этого запроса OPTIONS, иначе он не отправит фактический последующий запрос.
Из того, что я могу сказать в документе для модуля CORS (и проверено небольшим экспериментом), когда вы устанавливаете:
preflightContinue: true
Вы говорите промежуточному программному обеспечению COR пропустить этот запрос OPTIONS и отправить его другим обработчикам запросов. В документе конкретно сказано: «Передать предварительный ответ CORS следующему обработчику». Поскольку у вас нет других обработчиков запросов для этого предварительного запроса, ваш сервер сгенерирует 404, и браузер увидит, что предварительная проверка не удалась, поэтому ваш запрос не будет выполнен.
Итак, первое, что нужно попробовать, это изменить на:
preflightContinue: false
Если это все еще не работает, покажите точный запрос, как показано на вкладке сети отладчика Chrome. Он точно покажет, что браузер отправляет для предварительной проверки, и точно покажет, что ваш сервер возвращает в ответ на это.
Спасибо, но это не делает его лучше. На самом деле я уже пробовал с false
, но я написал пример с true
. Я отредактировал вопрос с нужными вам скринами.
В конце концов, проблема была не в CORS, даже если он выдавал ошибку.
На самом деле JWT был слишком длинным по неизвестной причине. Я использовал «Burp Suite» для перехвата запросов и добавил запрос в повторитель, чтобы повторно отправить их и протестировать с пользовательскими значениями.
Чтобы понять это, мне нужно было обновить ключ, используемый библиотекой jsonwebtoken
, чтобы подписать JWT, на меньший ключ, и таким образом JWT был меньше.
См. stackoverflow.com/a/48133489/441757 и npmjs.com/package/cors#enbling-cors-pre-flight. Вам нужно добавить некоторую форму
app.options('*', cors())
в код сервера.