Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetch Post issues with CORS not getting header

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
like image 966
Puerto Avatar asked Mar 01 '17 22:03

Puerto


3 Answers

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.

like image 123
Puerto Avatar answered Oct 16 '22 00:10

Puerto


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();
});
like image 37
Forest baba Avatar answered Oct 15 '22 23:10

Forest baba


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.

like image 2
QuantumHive Avatar answered Oct 16 '22 00:10

QuantumHive