This CORS has brought me to my knees again. I mean it can be so discouraging. Please understand I have been looking at all 5 million posts on this topic before you down vote me. I realize there is a lot out there on this subject. Here is my Fetch Post in my React UI code. This is running on a IIS server with the compiled JS, and just Index.html for the SPA. Im trying to call a API on the same server different port. It's the preflight that is killing me in Chrome and other modern browsers (seems fine in IE).
Here is the fetch:
return (
fetch(mypost, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Accept": "application/json",
},
mode: 'cors',
body: JSON.stringify({
name: this.state.value,
})
}).then(response => {
if (response.status >= 400) {
this.setState({
value: 'no greeting - status > 400'
});
throw new Error('no greeting - throw');
}
return response.text()
}).then(data => {
var myData = JSON.parse(data);
this.setState({
greeting: myData.name,
path: myData.link
});
}).catch(() => {
this.setState({
value: 'no greeting - cb catch'
})
})
);
standard prefight error we've all seen.
Fetch API cannot load http://myApiServer:81/values/dui/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:82' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Here is the fiddle for the preflight:
OPTIONS http://myApiServer:81/values/dui/ HTTP/1.1
Host: myApiServer:81
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:82
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Access-Control-Request-Headers: access-control-allow-origin, content-type
Accept: */*
Referer: http://localhost:82/validator
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Now I have learned that setting no-cors will basically set a default header, which is not what I want either, I need my application/json, cause who doesn't want JSON, right? :)
I would love any advice on what I can do to resolve this issue. Basically since this is just compiled Javascript and index.html sitting on a IIS server, I need to know the best solution for dealing with these preflight options checks that seem to be happening.
******Update
I've tried adding webconfig to force the IIS server to handle the preflight. It seems like it needs to be on both ends, my UI and API??
Here is my UI Web.Config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
</system.webServer>
And my WebApi web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
</customHeaders>
</httpProtocol>
<aspNetCore processPath="dotnet" arguments=".\dui.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true" />
</system.webServer>
</configuration>
All Im getting now is:
fetch API cannot load http://myApiServer:81/values/dui/. Response for preflight has invalid HTTP status code 415.
Here is the fiddle for this.
req
OPTIONS http://myApiServer:81/values/dui/ HTTP/1.1
Host: myApiServer:81
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:82
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://localhost:82/validator
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
And response
HTTP/1.1 415 Unsupported Media Type
Content-Length: 0
Server: Kestrel
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Date: Thu, 02 Mar 2017 18:35:31 GMT
So I’m going to post this answer because it is in fact working for me now. Here's what I went through. I’m not entirely sure why but I'll do my best to explain what I've learned along the way. If anyone cares to correct or further explain, I will gladly give the credit and upvote.
So there are 3 layers of awareness with CORS in an API it seems.
Layer 1
The code for the API itself will have some CORS settings most likely which you should pay close attention too and definitely do you reading. In my case the standard location was in the Startup.cs for the webApi. I have always had these in my WebApi and frankly I thought that was all that was required initially (little did I know).
services.AddCors();
app.UseCors(builder => builder.AllowAnyOrigin())
I also had it set in my webconfig which bring us to:
Layer 2
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
</customHeaders>
I was using this because a wise man said (thanks @sideshowbarker) my Fetch in my React UI was generating a custom header and therefore producing a preflight reponse on my server so this was needed to handle that the response at the server level.
Layer 3
So layer 3 turns out really wasn't an issue in my case but it is something you must be aware of in this process. So not in the API code, not on the server but in the UI (calling application) itself.
Generally I had the right things in here besides the "Access-Control-Allow-Origin": "*"
, which apparently was not needed here of all places since this is response header and in the fetch I’m specifying the request headers, thanks again @sideshowbarker.
So my Fetch request looks like this:
fetch(mypost, {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
mode: 'cors',
body: JSON.stringify({
name: this.state.value,
})
})
In my case mainly it was the fact that I was 'context-type: application/json'
that was causing the preflight response. I guess this is not standard. Seems pretty standard to me in todays world, but what do I know.
Anyhow, with all that I was still getting the following message, which frankly was better than seeing that preflight error. progress is progress.
Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
I found some stuff online that mentioned I don't need to AllowAnyOrigin
in both places (webserver and API) so I tried comment this code.
//app.UseCors(builder => builder.AllowAnyOrigin());
and got:
Fetch API cannot load
http://ApiServer:81/values/dui/
. Response for preflight has invalid HTTP status code 415
So then based on some searches found an article saying that last error could be header-related. I went back to the API source dotnet Core cors.
https://learn.microsoft.com/en-us/aspnet/core/security/cors
It's a good reference. I should have been paying closer attention to this. It didn’t exactly solve my problem but I noticed there was another builder I could use for headers, so I plugged that in, rebuilt, published and bam, no more CORS issues. Everything works!
This is the code I plugged into my API:
app.UseCors(builder => builder.AllowAnyHeader());
All the rest of my code stayed the same for layer 2 and 3. I left the AllowAnyOrigin
builder in the API in Layer 1 commented out.
I’m sorry for being overly verbose here but I find CORS to be painful and I hope this helps someone. I'd say it's time for a Coors but I think we all know that beer is terrible. I guess that kinda makes sense after all.
As suggested by someone in another post, do the equivalent of this to your server-side assuming you are not using Nodejs. Note, my client-side where I was having a similar issue is Reactjs powered, that makes us even
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8888');
// 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();
});
I've been scratching my head for 2 hours now and finally came to a solution that works in my case. If you, like me, are in the same situation, I hope my answer can help you in saving those hours. Since I have control over both the web API (aspnet core
) and the client (fetch
), I did the following:
Now that we understand a bit about CORS, do the following in your Startup.cs
file:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment environment)
{
app.UseCors(builder =>
{
builder.WithOrigins("http://yourclient/");
builder.AllowAnyMethod();
builder.AllowAnyHeader();
});
app.UseMvc();
}
Now at the client side, do this:
fetch("http://yourapi/api/someresource", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Access-Control-Request-Headers": "*",
"Access-Control-Request-Method": "*"
},
body: JSON.stringify(data)
})...;
That's all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With