I know that this subject seems to be already answered many time but i really don't understand it.
I have an local angular-fullstack app (Express, NodeJs) which has his
server A (localhost:9000/).
i have an another local front app B (BlurAdmin done with
generator-gulp-angular) (localhost:3000/)
My server A was autogenerate with Express and Nodejs:
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', false);
next();
});
In my app B, a GET request to my API of serveur A works
$http.get('http://localhost:9000/api/shows')
But with a PUT request:
$http.put('http://localhost:9000/api/shows/1', object)
i have the following error:
XMLHttpRequest cannot load http://localhost:9000/api/shows/1. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3000' is therefore not allowed
access. The response had HTTP status code 403.
I try with:
$http({
method: "PUT",
url:'http://localhost:9000/api/shows/1',
headers: {
'Content-type': 'application/json'
},
data:object
});
Not working,
I try with:
res.header('Access-Control-Allow-Origin', '*');
Not working,
I have installed *ExtensionAllow-Control-Allow-Origin: ** for chrome and that has done nothing (that return just not all the code but only error 403 (forbidden)
I don't really know why this is not working for PUT when it's working with GET.
Have you already had this problem? Thanks!
Updated:1 The result in my Browser:
for GET
General:
Request URL:http://localhost:9000/api/shows/1
Request Method:OPTIONS
Status Code:200 OK
Request header:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,de-DE;q=0.2,de;q=0.2
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:PUT
Connection:keep-alive
Host:localhost:9000
Origin:http://localhost:3000
Referer:http://localhost:3000/
Response header:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:X-Requested-With,content-type
Access-Control-Allow-Methods:GET, POST, OPTIONS, PUT, PATCH, DELETE
Access-Control-Allow-Origin:http://localhost:3000
Allow:GET,HEAD,PUT,PATCH,DELETE
Connection:keep-alive
Content-Length:25
Content-Type:text/html; charset=utf-8
For PUT request:
general:
Request URL:http://localhost:9000/api/shows/1
Request Method:PUT
Status Code:403 Forbidden
Response Header:
Connection:keep-alive
Content-Encoding:gzip
Content-Type:application/json; charset=utf-8
Date:Thu, 25 Aug 2016 16:29:44 GMT
set-cookie:connect.sid=s%3AsVwxxbO-rwLH-1IBZdFzZK1xTStkDUdw.xuvw41CVkFCRK2lHNlowAP9vYMUwoRfHtc4KiLqwlJk; Path=/; HttpOnly
set-cookie:XSRF-TOKEN=toaMLibU5zWu2CK19Dfi5W0k4k%2Fz7PYY%2B9Yeo%3D; Path=/
Strict-Transport-Security:max-age=31536000; includeSubDomains; preload
Request header:
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,de-DE;q=0.2,de;q=0.2
Connection:keep-alive
Content-Length:27
Content-Type:application/json
Host:localhost:9000
Origin:http://localhost:3000
Referer:http://localhost:3000/
Request Payload:
{url_name: "myData"}
url_name
:
"myData"
Thanks for helping
Update 2: with https://github.com/troygoode/node-cors-server/blob/master/app.js and the work of #Chris Foster
I have install npm cors --save
in my app.js,
i have now the following code:
import express from 'express';
import cors from 'cors';
var app = express()
var corsOptions = {
origin: 'http://localhost:3000'
}
var issuesoption = {
origin: true,
methods: ['PUT'],
credentials: true,
};
app.use(cors(corsOptions))
app.options('*', cors(issuesoption));
app.put('/api/shows/:id',cors(issuesoption) ,function(req,res){
res.json({
data: 'Issue #2 is fixed.'
});
});
And that is still not working with:
$http.put("http://localhost:9000/api/shows/1",object)
BUT i don't have any 403 error, I have now,
Request URL:http://localhost:9000/api/shows/1
Request Method:PUT
Status Code:200 OK
But that doesn't put the data into the server and i have in Preview:
{data: "Issue #2 is fixed."}
When you make a request that can change something (POST, PUT, etc), CORS rules require that the browser makes a preflight request (OPTIONS) before making the actual request.
In Node, you have to specifically handle these OPTION requests. It's likely you are only handling the PUT (app.put('/etc/etc/')), and the preflight request is failing.
You need to add handling for preflight OPTION requests, but better yet check out the cors package which makes this all much more simple and does it for you:
const express = require('express')
const cors = require('cors')
const app = express()
const corsOptions = {
origin: 'http://example.com'
}
app.use(cors(corsOptions))
Related
This question already has answers here:
How to enable cors nodejs with express?
(10 answers)
Express cors not allowing credentials
(2 answers)
How to read JSON file with fetch() in javascript?
(2 answers)
Closed 12 months ago.
I have a Node.js Express.js REST API server using the cors middleware running on localhost:4000. A React app running on localhost:3000 uses fetch to send a GET request to the Express server and expects a JSON response.
However, React is unable to perform the fetch due to a CORS error.
Access to fetch at 'http://localhost:4000/metrics' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Why is it still getting a CORS error despite using the cors middleware on the API server?
api_server.js:
import { selectMetrics } from './db'
import express from 'express'
import cors from 'cors'
const PORT = 4000
const app = express()
const corsOptions = {
origin: '*',
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus: 200,
}
app.use(cors(corsOptions))
app.set('json spaces', 2)
app.get('/metrics', async (req, res) => {
const metrics = await selectMetrics()
return res.json(metrics)
})
app.listen(PORT, () => console.log(`API server listening on port ${PORT}`))
Fetch from inside the React app:
async () => {
const apiUrl = "http://localhost:4000/metrics";
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error("Error fetching from /metrics");
}
console.log("/metrics response:", response);
}
General:
Request URL: http://localhost:4000/metrics
Request Method: GET
Status Code: 200
Referrer Policy: strict-origin-when-cross-origin
Response Headers:
Content-Length: 30008
Content-Type: application/json; charset=utf-8
Date: Thu, 24 Feb 2022 05:12:01 GMT
ETag: W/"7538-9fQpPD2E0HULEM9eRjgd2o2OHGo"
X-Powered-By: Express
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: localhost:4000
If-None-Match: W/"7538-9fQpPD2E0HULEM9eRjgd2o2OHGo"
Origin: http://localhost:3000
Referer: http://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-GPC: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Also tried the following, but still getting CORS error:
Express server restarted after each try
const app = express()
app.use(cors())
const app = express()
const corsOptions = {
origin: '*',
optionSuccessStatus: 200,
}
app.use(cors(corsOptions))
const app = express()
app.use(cors())
app.options('*', cors())
const app = express()
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:4000')
res.header('Access-Control-Allow-Credentials', true)
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
res.header(
'Access-Control-Allow-Headers',
'Origin,X-Requested-With,Content-Type,Accept,content-type,application/json',
)
next()
})
Result of running curl -v -H "Origin: http://localhost:3000" "http://localhost:4000/metrics":
Headers:
* Trying 127.0.0.1:4000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 4000 (#0)
> GET /metrics HTTP/1.1
> Host: localhost:4000
> User-Agent: curl/7.68.0
> Accept: */*
> Origin: http://localhost:3000
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 30008
< ETag: W/"7538-9fQpPD2E0HULEM9eRjgd2o2OHGo"
< Date: Thu, 24 Feb 2022 05:44:21 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
<
Expected JSON response is received
My stack is as follows:
Backend: Spring boot(Java) exposed at :8088
Frontend: Vue hosted on a Node development server exposed at :8080
On the frontend, I am re-configuring axios in a http-common.js to put the baseURL to the Spring boot application, and allow connection from the node development server:
import axios from 'axios'
export const AXIOS = axios.create({
baseURL: `http://localhost:8088`,
headers: {
'Access-Control-Allow-Origin': 'http://localhost:8080'
}
})
However, when attempting to make a post request to log in, I will get the following message in the console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8088/api/login. (Reason: CORS preflight channel did not succeed).
Which makes me think: Is the issue with the spring boot application?
But no, in the main method, I have enabled CORS globally when reaching the /api/* endpoints from the node application running at :8080:
#Bean
public WebMvcConfigurer corsConfigurer() { // Enables CORS globally
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/*").allowedOrigins("http://localhost:8080");
}
};
}
To me it looks as if it should be configured correctly. However, as of now, the following POST of username + password never even reaches the backend Spring boot application at all. The issue must be with the Node application?
This is the Login method in the frontend:
login ({commit}, authData) {
AXIOS.post('/api/login', {
username: authData.username,
password: authData.password,
withCredentials: true
})
.then(res => {
console.log(res)
commit('authUser', {
token: res.data.idToken,
userId: res.data.localId
})
})
.catch(error => console.log(error))
}
To further solidate my point, i can cURL to the spring boot application and get the correct response(a valid JWT!):
Request:
curl -i -H "Content-Type: application/json" -X POST -d '{
"username": "sysadmin",
"password": "sysadmin"
}' http://localhost:8088/api/login
Response:
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
X-Application-Context: application:8088
authentication: <very long JWT string>
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
So, via cUR - I get a HTTP 200 OK, and a valid JWT. But via the same POST method from :8080, I get a 403 and a warning message.
As per other posts, I have attempted to add CORS to my dev server configuration(Node/Express):
var app = express()
app.use(cors())
app.options('*', cors())
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8088')
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE')
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type')
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true)
// Pass to next layer of middleware
next()
})
The result is exactly the same as previously
Adding the 'Access-Control-Allow-Origin' header to your ajax post call is useless since is part of cors specification and must be set by the server as part of the http response.
export const AXIOS = axios.create({
baseURL: `http://localhost:8088`,
headers: {
//you can remove this header
'Access-Control-Allow-Origin': 'http://localhost:8080'
}
})
You can curl the application because the cors exception is caused by the browser disallowing you to access the payload. The browser performs the preflight (OPTION) request before any Cross domain call, and before your actual http request to make sure you have the rights to see the payload, you can see it just inspecting the console under the network tab.
the issue is most likely server side, somehow you did not configure correctly the cors header to your http response.
make sure you're setting not only the Access-Control-Allow-Origin header (that must contain the specific domain, not * since you're in credential mode), but even Access-Control-Allow-Credential since you're sending credentials, and the Access-Control-Allow-Methods (that must contain at least the PUSH and the OPTION methods)
in your chrome dev tools console under the network tab if you inspect your ajax call you can see the header of the http response, should end up with something like this.
Have you tried to add #CrossOrigin to your login REST method?
#CrossOrigin(origins = "http://localhost:8080")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
Update: I just read this on javadoc:
Exact path mapping URIs (such as "/admin") are supported as well as Ant-style path patterns (such as "/admin/**").
I don't see here a path with one star, but your path is a correct Ant-style path..
I am using react with redux and axios for async actions. And laravel 5.2 api for backend. React is in actual domain and api is in subdomain. When I try to call async get request to the api I am getting 200 Ok from network but getting error on console.
Console Log :
XMLHttpRequest cannot load
http://api.doublecurlybraces.me/api/test?api_token=inpm38XbI9Fof7CZv99VlfNQTjx02EjaL5V1B3###########
. Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://doublecurlybraces.me' is therefore not
allowed access.
Network :
--General--
Request Method:OPTIONS
Status Code:200 OK
Remote Address:46.101.###.###:80
----
--Response Header--
Allow:GET,HEAD,POST
Cache-Control:no-cache
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Wed, 10 Aug 2016 15:08:01 GMT
Server:nginx/1.11.1
Transfer-Encoding:chunked
--Request Header--
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, access-control-allow-headers, access-control-allow-origin, x-requested-with
Access-Control-Request-Method:GET
Cache-Control:max-age=0
Connection:keep-alive
Host:api.doublecurlybraces.me
Origin:http://doublecurlybraces.me
Referer:http://doublecurlybraces.me/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
#
Action Code
import axios from "axios";
var config = {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Headers':'*',
'Access-Control-Allow-Origin' : 'http://doublecurlybraces.me',
'X-Requested-With': 'XMLHttpRequest'
},
params: {
api_token : 'inpm38XbI9Fof7CZv99VlfNQTjx02EjaL#############',
},
};
export function stockChanges(){
return function(dispatch){
axios.get('http://api.doublecurlybraces.me/api/test',config)
.then(function (response) {
dispatch({type : "FETCH_STOCK_FULFILLED" , payload : response.data})
})
.catch(function (error) {
dispatch({type : "FETCH_STOCK_REJECTED" , payload : error})
});
}
}
The problem is that the server does not return Access-Control-Allow-Origin header.
Access-Control-Allow-Origin: http://doublecurlybraces.me
Please note that Access-Control-Allow-Origin is a response header, so you should not add it to the axios request config.
I'm working on making an AJAX call that hit the Mailgun API to send email. Documentation on Mailgun says that post requests should be made to "https://api.mailgun.net/v3/domain.com/messages". I've included my api key as specified by mailgun (they instruct to use a username of 'api'). Since this involves CORS, I can't get past the error: Request header field Authorization is not allowed by Access-Control-Allow-Headers.
However, I've inspected the requests/responses in the Network tab and "Access-Control-Allow-Origin" in the response from Mailgun is set to "*"...which should indicate that it should allow it? (See request/response below): I've edited the actual domain and my API key.
Remote Address:104.130.177.23:443
Request URL:https://api.mailgun.net/v3/domain.com/messages
Request Method:OPTIONS
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, authorization
Access-Control-Request-Method:POST
Connection:keep-alive
Host:api.mailgun.net
Origin:null
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36
Response Headersview source
Access-Control-Allow-Headers:Content-Type, x-requested-with
Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:600
Allow:POST, OPTIONS
Connection:keep-alive
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Fri, 20 Mar 2015 19:47:29 GMT
Server:nginx/1.7.9
My code for the ajax call is below, in which I include my credentials in the headers and the domain to where the post is supposed to go. Not sure what's causing this not to work. Is it because I'm testing on local host? I didn't think that would make a difference since the "Access Control Allow Origin:*" in the response header. Any help would be greatly appreciated! Thank you.
function initiateConfirmationEmail(formObj){
var mailgunURL;
mailgunURL = "https://api.mailgun.net/v3/domain.com/messages"
var auth = btoa('api:MYAPIKEYHERE');
$.ajax({
type : 'POST',
cache : false,
headers: {"Authorization": "Basic " + auth},
url : mailgunURL,
data : {"from": "emailhere", "to": "recipient", etc},
success : function(data) {
somefunctionhere();
},
error : function(data) {
console.log('Silent failure.');
}
});
return false;
}
Drazisil is correct above. The response needs to include Access-Control-Allow-Headers: Authorization as you are including that header in your request and Authorization is not a simple header.
I have 2 parts in my app - Angular frontend and rails server. And because it's different domains, requests doesn't work by default. There are a lot of staff about that, including stack, but it doesn't works for me.
This is my method in angular controller:
$scope.test =->
# transform = (data) ->
# $.param data
$http.post('http://localhost:3000/session/create', 'token=some',
headers:
'Access-Control-Allow-Origin': 'http://localhost:9000',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'
# transformRequest: transform
).success (responseData) ->
console.log(responseData)
I commented transform data, there is no difference in server response.
And I configured app (saw this in some post) like that:
.config ["$httpProvider", ($httpProvider) ->
$httpProvider.defaults.useXDomain = true
delete $httpProvider.defaults.headers.common["X-Requested-With"]
]
I guess that makes request not ajax like (but i'm not sure).
But there is no headers in my request. They all in some field Access-Control-Request-Headers:access-control-allow-origin, accept, access-control-allow-headers, access-control-allow-methods, content-type:
Request URL:http://localhost:3000/session/create
Request Method:OPTIONS
Status Code:404 Not Found
Request Headers
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,ru;q=0.6
Access-Control-Request-Headers:access-control-allow-origin, accept, access-control-allow-headers, access-control-allow-methods, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
DNT:1
Host:localhost:3000
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.8 Safari/537.36
Response Headers
Content-Length:12365
Content-Type:text/html; charset=utf-8
X-Request-Id:2cf4b37b-eb47-432a-ab30-b802b3e33218
X-Runtime:0.030128
Chrome console:
OPTIONS http://localhost:3000/session/create 404 (Not Found) angular.js:6730
OPTIONS http://localhost:3000/session/create No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. angular.js:6730
XMLHttpRequest cannot load http://localhost:3000/session/create. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. localhost/:1
Other insignificant information:
On the server:
class ApplicationController < ActionController::Base
before_filter :allow_cross_domain_access
protected
def allow_cross_domain_access
headers['Access-Control-Allow-Origin'] = '*'# http://localhost:9000
headers['Access-Control-Allow-Headers'] = 'GET, POST, PUT, DELETE'
headers['Access-Control-Allow-Methods'] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(',')
headers['Access-Control-Max-Age'] = '1728000'
end
end
Routes is obvious post "session/create". And session controller:
class SessionController < ApplicationController
respond_to :json, :html, :js
def create
respond_with "logged in"
# render nothing: true
end
end
I use latest version of angular 1.2.0.rc-2. this is my project on github
You mix up the request and response headers in your example.
When doing a cross domain request (CORS) and you want to make anything different than a plain GET - so for example a POST or adding custom headers - the browser will first make an OPTIONS request.
This is what you see in your developer console:
OPTIONS http://localhost:3000/session/create 404 (Not Found) angular.js:6730
The server then should add the appropriate headers to the response of the OPTIONS request.
So you can remove the custom headers from the Angular call.
Next you need to make sure that your server/application answers the OPTIONS request, not returning a 404.