10

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
Puerto
  • 1,072
  • 3
  • 11
  • 32
  • Remove the `"Access-Control-Allow-Origin": "*",` line from your `headers` value. `Access-Control-Allow-Origin` is a *response* header. If you put it in a request it has no effect other than to trigger a preflight if one’s not been triggered otherwise. – sideshowbarker Mar 01 '17 at 22:54
  • 1
    Do you admin access to the server environment for `http://server:81/values/dui/` so that you can configure it to send CORS response headers in responses? – sideshowbarker Mar 01 '17 at 22:55
  • @sideshowbarker I tried removing the access control allow from my request before I left work but not change. I think the application/Json might still force the preflight?? I do have admin access on the IIS server so I can try anything tomorrow. let me know. thanks man! – Puerto Mar 02 '17 at 01:52
  • The application/json content-type browser header makes browsers do a preflight yeah – sideshowbarker Mar 02 '17 at 02:02
  • @sideshowbarker so I have been messing around with the IIS server, more so just directly with a web.config now which sets the response headers in IIS. I feel like a dog chasing his tail. I have both my API and UI setup the same regarding customHeaders (see updated code in post for webconfigs). My api already had some code in it as well to deal with CORS. Mainly 'services.AddCors();' and 'app.UseCors(builder => builder.AllowAnyOrigin());' That second seems to cause this error. ('Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.) – Puerto Mar 02 '17 at 18:22
  • So there is something between setting in the Api and setting preflight on the IIS server. If I take out the CORS from the API but the leave the preflight stuff on the server with the webConfig. I get. (Fetch API cannot load http://server:81/values/dui/. Response for preflight has invalid HTTP status code 415) – Puerto Mar 02 '17 at 18:27

4 Answers4

4

I have since switched to this solution for my dotnet core webAPi. I find it's cleaner since I don't need a webconfig plus the cors config which needs to be set just right for the 2 to work together. This gets rid of the cors stuff in your webConfig so your only setting it in one place, in you API application code itself.

https://www.codeproject.com/Articles/1150023/Enable-Cross-origin-Resource-Sharing-CORS-in-ASP-N

Just to give a brief summary of what's in the link.

Add to project.json

//Cross Origin Resource Sharing
"Microsoft.AspNetCore.Cors": "1.0.0"

In startup add this to your ConfigureServices.

services.AddCors(
options => options.AddPolicy("AllowCors",
builder =>
{
    builder
    //.WithOrigins("http://localhost:4456") //AllowSpecificOrigins;
    //.WithOrigins("http://localhost:4456", 
    "http://localhost:4457") //AllowMultipleOrigins;
    .AllowAnyOrigin() //AllowAllOrigins;

    //.WithMethods("GET") //AllowSpecificMethods;
    //.WithMethods("GET", "PUT") //AllowSpecificMethods;
    //.WithMethods("GET", "PUT", "POST") //AllowSpecificMethods;
    .WithMethods("GET", "PUT", 
    "POST", "DELETE") //AllowSpecificMethods;
    //.AllowAnyMethod() //AllowAllMethods;

    //.WithHeaders("Accept", "Content-type", "Origin", "X-Custom-Header");  
    //AllowSpecificHeaders;
    .AllowAnyHeader(); //AllowAllHeaders;
})
);

Also at it to Configure in Startup

//Enable CORS policy "AllowCors"
    app.UseCors("AllowCors");

Then in your controller ensure you have these references:

 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Cors;
 using CrossOrigin.WebService.Models.DbEntities;
 using Microsoft.EntityFrameworkCore;

And Finally add the attribute to your controller class.

[EnableCors("AllowCors"), Route("api/[controller]")]
 public class ContactController : Controller
Puerto
  • 1,072
  • 3
  • 11
  • 32
3

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();
});
OhhhThatVarun
  • 3,981
  • 2
  • 26
  • 49
forestbaba
  • 189
  • 1
  • 4
2

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:

  • First of all it's actually really important to understand CORS. I advice you to reserve 15~30 minutes of your time, this will save you time upfront, trust me. As Puerto mentioned, read the docs: https://learn.microsoft.com/en-us/aspnet/core/security/cors
  • In my opinion the Microsoft docs about CORS is easier to understand for us .NET developers. If you want, you can also read the MDN docs, but I had a harder time to understand and put the pieces together.

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.

QuantumHive
  • 5,613
  • 4
  • 33
  • 55
  • Obviously you must have the `Microsoft.AspNetCore.Cors` package installed to enable CORS on the server. – QuantumHive Jun 29 '17 at 16:33
  • @QuantumHive-With application/json you will generate a preflight repsonse. Im surprised you don't need a webconfig on the server to deal with the preflight. Are you running as a container or standard Application in IIS? – Puerto Jun 29 '17 at 21:33
  • @Puerto I'm not sure what you mean with '*container*' or '*standard Application*'. Perhaps I should mention that I'm using `dotnet core 1.1`. As far as I can tell from the docs, the only thing you need to do is to enable CORS with `app.UseCors()` and `services.AddCors()`. I'm just using the Kestrel server behind IIS. This the default when creating a new `aspnet core` webapp project from the Visual Studio template. – QuantumHive Jun 30 '17 at 15:23
  • You answered my question. Your running on IIS (with Kestrel because you are dotnet core). But you could be kestrel running as a cross platform containerized application running in say OpenShift or something. But since you are not, I'm curious how you are able to get around not setting anything on the IIS server to deal with the preflight requests your application/json request will surely generate. The documentation may say all you need are those 2 things but that's not entirely true if you are generating preflight requests I think. – Puerto Jul 03 '17 at 14:50
  • Ah I see. In `aspnet core`, IIS *just* delegates the HTTP request to Kestrel. I've recently learned that the [HTTP Modules and Handlers](https://msdn.microsoft.com/en-us/library/bb398986.aspx) (we were used to in IIS and ASP.NET) are not applicable anymore with `aspnet core` applications. The HTTP requests are handled by Kestrel in the pipeline we define with middleware (e.g. `app.UserCors()`). Initially the *entire* HTTP pipeline is just empty. But I'm not entirely sure what kind of business IIS does before it delegates the request to Kestrel. – QuantumHive Jul 19 '17 at 08:20
  • Just a heads up: I've migrated my SPA to the same host as where my API is hosted under the same domain name (so I don't need to enable CORS anymore). Because Safari on iOS will **not** save cookies by default. If you use [`aspnet core identity`](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity) and the cookie authentication middleware, your application will render useless for iOS users. Damn you Apple! – QuantumHive Jul 19 '17 at 08:27
  • Frankly I ended up doing the same thing. Same Server, Same Website and Port. Just created separate applications for my API and UI under default website on my IIS server. If you have that flexibility, you might as well do it and skip the CORS. It is an archaic PITA! :) – Puerto Jul 19 '17 at 14:03
  • But in the future, if your API grows, as it should, and others want to tap in, that CORS is coming back. YOu better be ready for it. Good luck either way! – Puerto Jul 19 '17 at 14:03
1

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.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Puerto
  • 1,072
  • 3
  • 11
  • 32