The issue is that your server is not configured to respond to OPTIONS requests with the correct response status, 2xx success status.
The GET is working because it is not making a preflight request, as it meets the criteria to be a simple request as defined by the CORS documentation
On the other hand, the POST request meets the criteria to be a Preflighted request, meaning a preflight OPTIONS request should be made first.
In short, you have correctly setup the CORS response headers, but the server is not configured to respond with a 2xx response for OPTIONS method requests(commonly 200 status).
The server must respond to OPTIONS requests with a 2xx success status—typically 200 or 204.
If the server doesn’t do that, it makes no difference what Access-Control-* headers you have it configured to send. And the answer to configuring the server to handle OPTIONS requests in the right way—to send a 200 or 204 success message—depends on what server software it’s running
Borrowing the solution from this answer, do this on your backend, .NET WEB API:
In your BaseApiController.cs:
We do this to allow the OPTIONS http verb
public class BaseApiController : ApiController
{
public HttpResponseMessage Options()
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
}
}
References
Preflighted requests
response for preflight 403 forbidden
Note
Running a nodejs server on domainA.com is irrelevent. The «axios» library can be used either to a) make XMLHttpRequests from the browser or b) make http requests from node.js. In this case it is the first option, the «axios.post» to domainB is done through a XMLHttpRequest from the browser, that `s why you get a preflighted request at domainB.com.
Когда я только начинал работать с Express и пытался разобраться с тем, как обрабатывать ошибки, мне пришлось нелегко. Возникало такое ощущение, будто никто не писал о том, что мне было нужно. В итоге мне пришлось самому искать ответы на мои вопросы. Сегодня я хочу рассказать всё, что знаю об обработке ошибок в Express-приложениях. Начнём с синхронных ошибок.
Обработка синхронных ошибок
Если вам нужно обработать синхронную ошибку, то вы можете, для начала, с помощью инструкции throw
, выдать такую ошибку в обработчике запроса Express. Обратите внимание на то, что обработчики запросов ещё называют «контроллерами», но я предпочитаю использовать термин «обработчик запросов» так как он кажется мне понятнее.
Вот как это выглядит:
app.post('/testing', (req, res) => {
throw new Error('Something broke! ')
})
Такие ошибки можно перехватить с помощью обработчика ошибок Express. Если вы не написали собственный обработчик ошибок (подробнее об этом мы поговорим ниже), то Express обработает ошибку с помощью обработчика, используемого по умолчанию.
Вот что делает стандартный обработчик ошибок Express:
- Устанавливает код состояния HTTP-ответа в значение 500.
- Отправляет сущности, выполнившей запрос, текстовый ответ.
- Логирует текстовый ответ в консоль.
Сообщение об ошибке, выведенное в консоль
Обработка асинхронных ошибок
Для обработки асинхронных ошибок нужно отправить ошибку обработчику ошибок Express через аргумент next
:
app.post('/testing', async (req, res, next) => {
return next(new Error('Something broke again! '))
})
Вот что попадёт в консоль при логировании этой ошибки.
Сообщение об ошибке, выведенное в консоль
Если вы пользуетесь в Express-приложении конструкцией async/await, то вам понадобится использовать функцию-обёртку, наподобие express-async-handler. Это позволяет писать асинхронный код без блоков try/catch. Подробнее об async/await в Express можно почитать здесь.
const asyncHandler = require('express-async-handler')
app.post('/testing', asyncHandler(async (req, res, next) => {
// Сделать что-нибудь
}))
После того, как обработчик запроса обёрнут в express-async-handler
, то можно, так же, как было описано выше, выбросить ошибку с использованием инструкции throw
. Эта ошибка попадёт к обработчику ошибок Express.
app.post('/testing', asyncHandler(async (req, res, next) => {
throw new Error('Something broke yet again! ')
}))
Сообщение об ошибке, выведенное в консоль
Написание собственного обработчика ошибок
Обработчики ошибок Express принимают 4 аргумента:
- error
- req
- res
- next
Размещать их нужно после промежуточных обработчиков и маршрутов.
app.use(/*...*/)
app.get(/*...*/)
app.post(/*...*/)
app.put(/*...*/)
app.delete(/*...*/)
// Собственный обработчик ошибок нужно поместить после всех остальных промежуточных обработчиков
app.use((error, req, res, next) => { /* ... */ })
Если создать собственный обработчик ошибок, то Express прекратит использование стандартного обработчика. Для того чтобы обработать ошибку, нужно сформировать ответ для фронтенд-приложения, которое обратилось к конечной точке, в которой возникла ошибка. Это означает, что нужно выполнить следующие действия:
- Сформировать и отправить подходящий код состояния ответа.
- Сформировать и отправить подходящий ответ.
То, какой именно код состояния подойдёт в каждом конкретном случае, зависит от того, что именно произошло. Вот список типичных ошибок, к обработке которых вы должны быть готовы:
- Ошибка
400 Bad Request
. Используется в двух ситуациях. Во-первых — тогда, когда пользователь не включил в запрос необходимое поле (например — в отправленной платёжной форме не заполнено поле со сведениями о кредитной карте). Во-вторых — тогда, когда в запросе содержатся некорректные данные (например — ввод в поле пароля и в поле подтверждения пароля разных паролей). - Ошибка
401 Unauthorized
. Этот код состояния ответа применяется в том случае, если пользователь ввёл неправильные учётные данные (вроде имени пользователя, адреса электронной почты или пароля). - Ошибка
403 Forbidden
. Используется в тех случаях, когда пользователю не разрешён доступ к конечной точке. - Ошибка
404 Not Found
. Применяется в тех случаях, когда конечную точку невозможно обнаружить. - Ошибка
500 Internal Server Error
. Применяется тогда, когда запрос, отправленный фронтендом, сформирован правильно, но на бэкенде при этом возникла какая-то ошибка.
После того, как определён подходящий код состояния ответа, его нужно установить с помощью res.status
:
app.use((error, req, res, next) => {
// Ошибка, выдаваемая в ответ на неправильно сформированный запрос
res.status(400)
res.json(/* ... */)
})
Код состояния ответа должен соответствовать сообщению об ошибке. Для этого нужно отправлять код состояния вместе с ошибкой.
Легче всего это сделать с помощью пакета http-errors. Он позволяет отправлять в ошибке три фрагмента информации:
- Код состояния ответа.
- Сообщение, сопутствующее ошибке.
- Любые данные, которые нужно отправить (это необязательно).
Вот как установить пакет http-errors
:
npm install http-errors --save
Вот как этим пакетом пользоваться:
const createError = require('http-errors')
// Создание ошибки
throw createError(status, message, properties)
Рассмотрим пример, который позволит как следует в этом всём разобраться.
Представим, что мы пытаемся обнаружить пользователя по адресу его электронной почты. Но этого пользователя найти не удаётся. В результате мы решаем отправить в ответ на соответствующий запрос ошибку User not found
, сообщающую вызывающей стороне о том, что пользователь не найден.
Вот что нам нужно будет сделать при создании ошибки:
- Установить код состояния ответа как
400 Bad Request
(ведь пользователь ввёл неправильные данные). Это будет наш первый параметр. - Отправить вызывающей стороне сообщение наподобие
User not found
. Это будет второй параметр.
app.put('/testing', asyncHandler(async (req, res) => {
const { email } = req.body
const user = await User.findOne({ email })
// Если пользователь не найден - выбросим ошибку
if (!user) throw createError(400, `User '${email}' not found`)
}))
Получить код состояния можно с помощью конструкции error.status
, а сообщение ошибки — с помощью error.message
:
// Логирование ошибки
app.use((error, req, res, next) => {
console.log('Error status: ', error.status)
console.log('Message: ', error.message)
})
Результат логирования ошибки в консоли
Затем состояние ответа устанавливают с помощью res.status
, а сообщение записывают в res.json
:
app.use((error, req, res, next) => {
// Установка кода состояния ответа
res.status(error.status)
// Отправка ответа
res.json({ message: error.message })
})
Лично я предпочитаю отправлять в подобных ответах код состояния, сообщение и результат трассировки стека. Это облегчает отладку.
app.use((error, req, res, next) => {
// Установка кода состояния ответа
res.status(error.status)
// Отправка ответа
res.json({
status: error.status,
message: error.message,
stack: error.stack
})
})
▍Код состояния ответа, используемый по умолчанию
Если источником ошибки не является createError
, то у неё не будет свойства status
. Вот пример, в котором сделана попытка прочесть несуществующий файл с помощью fs.readFile
:
const fs = require('fs')
const util = require('util')
// Преобразуем readFile из функции, использующей коллбэки, в async/await-функцию.
// Подробности об этом смотрите здесь: https://zellwk.com/blog/callbacks-to-promises
const readFilePromise = util.promisify(fs.readFile)
app.get('/testing', asyncHandler(async (req, res, next) => {
const data = await readFilePromise('some-file')
})
У такого объекта ошибки не будет свойства status
:
app.use((error, req, res, next) => {
console.log('Error status: ', error.status)
console.log('Message: ', error.message)
})
Результат логирования ошибки в консоли
В подобных случаях можно задать код ошибки, используемый по умолчанию. А именно, речь идёт об ошибке 500 Internal Server Error
:
app.use((error, req, res, next) => {
res.status(error.status || 500)
res.json({
status: error.status,
message: error.message,
stack: error.stack
})
})
▍Изменение кода состояния ошибки
Предположим, мы собираемся прочитать некий файл, воспользовавшись данными, предоставленными пользователем. Если такого файла не существует, это значит, что нам нужно выдать ошибку 400 Bad Request
. Ведь в том, что файл найти не удаётся, нет вины сервера.
В подобном случае нужно воспользоваться конструкцией try/catch для перехвата исходной ошибки. Затем нужно воссоздать объект ошибки с помощью createError
:
app.get('/testing', asyncHandler(async (req, res, next) => {
try {
const { file } = req.body
const contents = await readFilePromise(path.join(__dirname, file))
} catch (error) {
throw createError(400, `File ${file} does not exist`)
}
})
▍Обработка ошибок 404
Если запрос прошёл через все промежуточные обработчики и маршруты, но так и не был обработан, это означает, что конечная точка, соответствующая такому запросу, не была найдена.
Для обработки ошибок 404 Not Found
нужно добавить, между маршрутами и обработчиком ошибок, дополнительный обработчик. Вот как выглядит создание объекта ошибки 404:
// Промежуточные обработчики...
// Маршруты...
app.use((req, res, next) => {
next(createError(404))
})
// Обработчик ошибок...
Сведения об ошибке
▍Замечания об ошибке ERR_HTTP_HEADERS_SENT
Не впадайте в панику если видите сообщение об ошибке ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client
. Она возникает из-за того, что в одном и том же обработчике многократно вызывается метод, устанавливающий заголовки ответа. Вот методы, вызов которых приводит к автоматической установке заголовков ответа:
- res.send
- res.json
- res.render
- res.sendFile
- res.sendStatus
- res.end
- res.redirect
Так, например, если вы вызовете методы res.render
и res.json
в одном и том же обработчике ответа, то вы получите ошибку ERR_HTTP_HEADERS_SENT
:
app.get('/testing', (req, res) => {
res.render('new-page')
res.json({ message: '¯_(ツ)_/¯' })
})
В результате, в том случае, если вы сталкиваетесь с этой ошибкой, тщательно проверьте код обработчиков ответа и убедитесь в том, что в нём нет ситуаций, в которых вызывается несколько вышеописанных методов.
▍Обработка ошибок и потоковая передача данных
Если что-то идёт не так при потоковой передаче ответа фронтенду, то можно столкнуться с той же самой ошибкой ERR_HTTP_HEADERS_SENT
.
В подобном случае обработку ошибок нужно передать стандартным обработчикам. Такой обработчик отправит ошибку и автоматически закроет соединение.
app.use((error, req, res, next) => {
// Сделать это нужно только в том случае, если ответ передаётся в потоковом режиме
if (res.headersSent) {
return next(error)
}
// Остальной код обработки ошибок
})
Итоги
Сегодня я рассказал вам всё, что знаю об обработке ошибок в Express. Надеюсь, это поможет вам писать более надёжные Express-приложения.
Уважаемые читатели! Как вы обрабатываете ошибки в своих Node.js-проектах?
I was trying to install some modules in Nodejs, using NPM, but in some cases the install dont go on, and a error is returned:
Error: 403 status code downloading tarball
I got this again when I trying to install node-canvas:
D:servernode_modulescanvasnode_modulescanvas>node "C:Program Filesnodejsnode_modulesnpmbinnode-gyp-bin\....node_modulesnode-gypbinnode-gyp.js" rebuild
gyp http GET http://nodejs.org/dist/v0.8.3/node-v0.8.3.tar.gz
gyp http 403 http://nodejs.org/dist/v0.8.3/node-v0.8.3.tar.gz
gyp WARN install got an error, rolling back install
gyp ERR! rebuild error Error: 403 status code downloading tarball
gyp ERR! rebuild error at Request.async (C:Program Filesnodejsnode_modulesnpmnode_modulesnode-gyplibinstall.js:215:14)
gyp ERR! rebuild error at Request.EventEmitter.emit (events.js:115:20)
gyp ERR! rebuild error at ClientRequest.<anonymous> (C:Program Filesnodejsnode_modulesnpmnode_modulesrequestmain.js:521:12)
gyp ERR! rebuild error at ClientRequest.g (events.js:185:14)
gyp ERR! rebuild error at ClientRequest.EventEmitter.emit (events.js:88:17)
gyp ERR! rebuild error at HTTPParser.parserOnIncomingClient (http.js:1445:7)
gyp ERR! rebuild error at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:111:23)
gyp ERR! rebuild error at Socket.socketOnData (http.js:1356:20)
gyp ERR! rebuild error at TCP.onread (net.js:404:27)
gyp ERR! not ok
canvas@1.0.2 node_modulescanvas
I’m using Windows 7 64x
Nodejs 0.8.3
what could be this error?
What is ‘tarball’?
- Remove From My Forums
-
Question
-
I’ve created a React Web Starter Kit project which runs successfully on my local Windows 10 box. I have followed
directions to publish my web app to Azure. I also followed directions to have
Azure’s Kudu run a custom script for installing and building my app.
The custom deployment works correctly but I cannot access my site. I receive a 403 when I try to browse http://axiscode.azurewebsites.com. Could it be that my site was correctly built, but Azure/Kudu did not bother to execute «node server.js»?
The custom deployment output looks like this:
remote: Preparing deployment for commit id '058581114c'. remote: Running custom deployment command... remote: Running deployment command... remote: remote: D:homesiterepository>call npm install remote: ............................... remote: D:homesiterepository remote: npm WARN optional Skipping failed optional dependency /chokidar/fsevents: remote: +-- babel@5.8.34 remote: npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.6 remote: ¦ +-- glob@5.0.15 remote: npm WARN optional Skipping failed optional dependency /browser-sync/chokidar/fsevents: remote: +-- babel-core@5.8.34 remote: npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.6 remote: ¦ +-- regenerator@0.8.40 remote: ¦ +-- commoner@0.10.4 remote: ¦ +-- glob@5.0.15 remote: +-- eslint@1.10.3 remote: ¦ +-- glob@5.0.15 remote: +-- jest-cli@0.8.2 remote: ¦ +-- istanbul@0.3.22 remote: ¦ +-- fileset@0.2.1 remote: ¦ +-- glob@5.0.15 remote: +-- jscs@2.8.0 remote: ¦ +-- glob@5.0.15 remote: +-- postcss-import@7.1.3 remote: ¦ +-- glob@5.0.15 remote: +-- precss@1.4.0 remote: +-- postcss-mixins@2.1.1 remote: +-- globby@3.0.1 remote: +-- glob@5.0.15 remote: remote: remote: > @ build D:homesiterepository remote: > babel-node tools/run build remote: remote: ................... remote: [06:40:25] Starting 'build'... remote: [06:40:25] Starting 'clean'... remote: [06:40:25] Finished 'clean' after 31 ms remote: [06:40:25] Starting 'copy'... remote: build/package.json remote: [06:40:25] Finished 'copy' after 234 ms remote: [06:40:25] Starting 'bundle'... remote: ..................... remote: Child remote: Time: 24924ms remote: Asset Size Chunks Chunk Names remote: main.js?e1ad0f66932a7e39e0b4 2.85 MB 0 [emitted] main remote: Child remote: Time: 24236ms remote: Asset Size Chunks Chunk Names remote: server.js 204 kB 0 [emitted] main remote: server.js.map 186 kB 0 [emitted] main remote: [06:40:51] Finished 'bundle' after 25924 ms remote: [06:40:51] Finished 'build' after 26189 ms remote: .......................................................................................................................................... remote: D:homesiterepositorybuildassets.js remote: D:homesiterepositorybuildpackage.json remote: D:homesiterepositorybuildserver.js remote: D:homesiterepositorybuildserver.js.map remote: D:homesiterepositorybuildcontentabout.jade remote: D:homesiterepositorybuildcontentindex.jade remote: D:homesiterepositorybuildcontentprivacy.jade remote: D:homesiterepositorybuildpublicapple-touch-icon.png remote: D:homesiterepositorybuildpublicbrowserconfig.xml remote: D:homesiterepositorybuildpubliccrossdomain.xml remote: D:homesiterepositorybuildpublicfavicon.ico remote: D:homesiterepositorybuildpublichumans.txt remote: D:homesiterepositorybuildpublicmain.js remote: D:homesiterepositorybuildpublicrobots.txt remote: D:homesiterepositorybuildpublictile-wide.png remote: D:homesiterepositorybuildpublictile.png remote: 16 File(s) copied remote: Custom build finished successfully. remote: Running post deployment command(s)... remote: Deployment successful. To https://Mystagogue@axiscode.scm.azurewebsites.net:443/AxisCode.git 22fbf15..0585811 master -> master
-Brent Arias
-
Edited by
Friday, July 1, 2016 2:16 AM
update title -
Moved by
Jambor yaoMicrosoft employee
Friday, July 1, 2016 2:16 AM
-
Edited by
Answers
-
As the introduction at
How to Build, Test, Deploy of the react-starter-kit Getting Started guide, I followed it and configure a few custom configurations on Azure, it works fine on my side, here are my steps for your information:- clone the project repo at https://github.com/kriasoft/react-starter-kit to local, and deploy it to Azure Web app via GI, you can refer to Continuous
deployment using GIT in Azure App Service - login Azure mange portal, enabled EDIT IN VISUAL STUDIO ONLINE
under CONFIGURE tab, then I can leverage VSO to modify the code on Azure online.
- click the edit in VSO under the DASHBOARD of your manage portal site, click the «open console» at left nav on VSO.
- run following commands:
npm update npm run build
- create a file named `web.config` at the root directory of your site, with the following content:
<configuration> <system.webServer> <handlers> <!-- indicates that the app.js file is a node.js application to be handled by the iisnode module --> <add name="iisnode" path="build/server.js" verb="*" modules="iisnode" /> </handlers> <rewrite> <rules> <!-- All other URLs are mapped to the Node.js application entry point --> <rule name="DynamicContent"> <conditions> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True" /> </conditions> <action type="Rewrite" url="build/server.js" /> </rule> </rules> </rewrite> </system.webServer> </configuration>
After all, click «Run» button at left nav to have a test view.
Any concern, please feel free to let me know.
-
Marked as answer by
Brent Arias
Monday, January 18, 2016 6:15 PM
- clone the project repo at https://github.com/kriasoft/react-starter-kit to local, and deploy it to Azure Web app via GI, you can refer to Continuous
Describe the issue
I want to crawling information of place (api)
(url : http://store.naver.com/sogum/api/businesses?start=1&display=20&query=%EC%8B%A4%EB%82%B4%EB%86%80%EC%9D%B4%ED%84%B0)
When i did in local, it runs well. I got json.
But on ubuntu(ec2) it is not working.
Example Code
const axios = require(‘axios’)
const request = require(‘request’)
axios.get(http://store.naver.com/sogum/api/businesses?start=1&display=20&query=%EC%8B%A4%EB%82%B4%EB%86%80%EC%9D%B4%ED%84%B0)
.then(data => {
console.log(‘data : ‘, data)
})
Expected behavior, if applicable
[nodemon] starting node crawling.js
(node:5088) UnhandledPromiseRejectionWarning: Error: Request failed with status code 403
at createError (/home/ubuntu/internship_project/project/node_modules/axios/lib/core/createError.js:16:15)
at settle (/home/ubuntu/internship_project/project/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (/home/ubuntu/internship_project/project/node_modules/axios/lib/adapters/http.js:236:11)
at IncomingMessage.emit (events.js:327:22)
at endReadableNT (_stream_readable.js:1220:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
(node:5088) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag —unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:5088) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
[nodemon] clean exit — waiting for changes before restart
Environment
- Axios Version ^0.19.2
- Node.js Version 12LTS
- OS: aws ec2 ubuntu 16.04
Additional context/Screenshots
I want to know why it is not working, and if in virtual os, can’t i crawling information?
Thank you!