Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 828
zapoo Avatar asked Dec 05 '22 03:12

zapoo


2 Answers

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.

like image 90
Jereme Avatar answered Dec 20 '22 13:12

Jereme


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.

OPTION REQUEST - PREFLIGHTGET REQUEST - AFTER PREFLIGHT

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. enter image description here

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

enter image description here

Click on request restrictions enter image description here

enter image description hereenter image description here

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

enter image description here

like image 27
zapoo Avatar answered Dec 20 '22 15:12

zapoo