简体   繁体   中英

Handling CORS Preflight in Asp.net Web API

I have three applications in my architecture.
They are on the same server but having different port numbers.

A - Token Application (port 4444) - Asp.net WebApi
B - API Application   (port 3333) - Asp.net WebApi
C - UI Application    (port 2222) - AngularJS App.

The application flow is like below

1- The UI project gets the token from Token Application (It requires Windows Auth.) Ex: awxrsdsaWeffs12da

2- UI application puts this token to a custom header which is named as "accessToken"

Ex: accessToken: awxrsdsaWeffs12da

3- UI application sends a request to API Application Ex: http:myaddress:3333/api/TheRestServiceHere

UI application gets 401 Error. Which sends OPTIONS method. (I guess preflight issue)

In my web api project I enabled Cors like this below.

public static void Register(HttpConfiguration config)
{
            ....

            //CORS
            var cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(cors);

            ....
}

Config

   public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {

            //CORS
            var cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors();


            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );


            var json = config.Formatters.JsonFormatter;
            json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
            json.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            json.SerializerSettings.Formatting = Formatting.None;
            json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

            config.Formatters.Remove(config.Formatters.XmlFormatter);
        }
    }

So I am looking for a solution to call API application (B) controllers and get 200:)

Regards

I fixed this in an application I am working on by creating a module that responds to requests that are using the OPTIONS verb. You should probably modify it a bit to include the verbs and content type that the application is requesting. In my case, I decided to post everything as JSON (which requires the pre-flight check). The module is as follows:

public class OptionsModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) =>
        {
            var app = (HttpApplication) sender;

            if (app.Request.HttpMethod == "OPTIONS")
            {
                app.Response.StatusCode = 200;
                app.Response.AddHeader("Access-Control-Allow-Headers", "content-type");
                app.Response.AddHeader("Access-Control-Allow-Origin", APISettings.ApplicationOrigin);
                app.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                app.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
                app.Response.AddHeader("Content-Type", "application/json");
                app.Response.End();
            }
        };
    }

    public void Dispose()
    {
    }
}

Then you need to register it in your web.config:

<system.webServer>
    <modules>
      <add name="HandleOptions" type="namespace.OptionsModule" />
    </modules>
</system.webServer>

Another thing you may want to do is specify the allowed origin explicitly. Chrome doesn't like having a wildcard there.

One of my friend solved the issue by using OPTIONSVerbHandler.

When UI application wants to use GET method, browser sends OPTION method first to the server (Preflight). Then if Preflight request is OK it sends GET request.

选项请求 - 预检获取请求 - 预检后

For CORS test purpose we used the following code to send GET method.

<html>
<head>
    <script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script>
    $( document ).ready(function() {
        var adress = "http://10.10.27.36:3434/backend/api/role";

        $.ajaxSetup({
            headers: { 
            'Content-Type': 'application/json',
            'accessToken': 'some value',
            'Origin' : ''
            }
        });

        $.ajax({
        type: "GET",
        url: adress,
        dataType: "json"
        });

    });

    </script></head><body></body></html>

To handle OPTION method which sends by browser before GET you should have the following settings. 在此输入图像描述

1- Webconfig

<system.webServer>
    <handlers>
        <add name="OPTIONSVerbHandler" path="*" verb="OPTIONS" modules="ProtocolSupportModule" resourceType="Unspecified" requireAccess="None" />
    </handlers>
</system.webServer> 

2- Adding OPTIONSVerbHandler with following settings

在此输入图像描述

Click on request restrictions 在此输入图像描述

在此输入图像描述在此输入图像描述

3- Our Header Settings we have accessToken which is custom as you can see

在此输入图像描述

For Azure Environment

You need allow origins from the portal.

Azure Cors设置

This problem happened for Cordova v11, platform for Android. I used the solution provided by Jereme (the top rated answer) with one exception: In OptionsModule, I had to omit the statement

app.Response.AddHeader("Access-Control-Allow-Origin", APISettings.ApplicationOrigin);

Instead in the web.config file I added in the <system.webServer> section the following:

 <httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />  
        <!-- Note:  "localhost" for Cordova is not valid, only * worked. -->
    </customHeaders>
</httpProtocol>

A word about Cordova in case one is unfamiliar. Cordova packages a native app for the Android platform with a “javascript page” included in the app, using a web view in the app to display the page. Using the Chrome debugger to view the javascript page, the page origin appears as localhost. However, localhost is not an acceptable value for the Access-Control-Allow-Origin header; therefore I had to use “*” in the Web.config file. When I had the response header in the OptionsModule, the preflight response was ok (status 200), but not the response for the XMLHttpRequest (the api) that initiated the preflight request. Putting the custom header only in the Web.config file allowed both responses to be ok (status 200).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM