Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Azure WCF Rest Service – CORS Issue

5.00/5 (1 vote)
21 Jul 2016CPOL3 min read 9.6K  
What is CORS ?  A resource makes a cross-origin HTTP request when it requests a resource from a different domain than the one which the first resource itself serves. For example, an HTML page served from http://domain-a.com makes an <img> src request for http://domain-b.com/image.jpg.

What is CORS ? 

A resource makes a cross-origin HTTP request when it requests a resource from a different domain than the one which the first resource itself serves. For example, an HTML page served from http://domain-a.com makes an <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Img#Attributes"><img> src</a> request for http://domain-b.com/image.jpg. Many pages on the web today load resources like CSS stylesheets, images and scripts from separate domains.

For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts.  For example, XMLHttpRequest follows the same-origin policy. So, a web application using XMLHttpRequestcould only make HTTP requests to its own domain. To improve web applications, developers asked browser vendors to allow XMLHttpRequest to make cross-domain requests.

The W3C Web Applications Working Group recommends the new Cross-Origin Resource Sharing(CORS) mechanism. CORS gives web servers cross-domain access controls, which enable secure cross-domain data transfers. Modern browsers use CORS in an API container – such as XMLHttpRequest – to mitigate risks of cross-origin HTTP requests.

How does it work ?

The Cross-Origin Resource Sharing standard works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser.  Additionally,  for HTTP methods other than GET, or for POST usage with certain MIME types, the specification mandates that browsers “preflight” the request, soliciting supported methods from the server with an HTTPOPTIONS request method, and then, upon “approval” from the server, sending the actual request with the actual HTTP request method.  Servers can also notify clients whether “credentials” (including Cookies and HTTP Authentication data) should be sent with requests.

Source : Mozilla Developer – CORS

How does a Preflight error look like ?

1

If you look at the above error, you will get a error called “CORS preflight channel did not succeed”

What was i trying to solve ?

I was writing a supposed to be easy Azure Cloud service. I created a WCF Web Role in my Cloud Project and my Service Interface is as below :

[ServiceContract]
    public interface IService1
    {

        #region Getters
        [WebGet(UriTemplate = "/GetUsers",
 RequestFormat = WebMessageFormat.Json,
 ResponseFormat = WebMessageFormat.Json,
 BodyStyle = WebMessageBodyStyle.Bare)]
        string GetUsers();

        [WebGet(UriTemplate = "/GetUser/{Id}")]
        string GetUser(string Id);

        #endregion

        #region Create Methods

        [WebInvoke(Method = "POST", UriTemplate = "/AddUser",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool AddUser(User user);

        [WebInvoke(Method = "POST", UriTemplate = "/AddUserDetails",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool AddUserDetails(UserDetail userdetails);
        #endregion

        #region Update Methods

        [WebInvoke(Method = "PUT", UriTemplate = "/UpdateUser",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool UpdateUser(User user);

        [WebInvoke(Method = "PUT", UriTemplate = "/UpdateUserDetails",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        bool UpdateUserDetails(UserDetail userdetails);
        #endregion

        #region Delete Methods

        [OperationContract]
        [WebInvoke(Method = "DELETE",
       UriTemplate = "/DeleteUser/{Id}",
       BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        bool DeleteUser(string Id);

        [OperationContract]
        [WebInvoke(Method = "DELETE",
         UriTemplate = "/DeleteUserDetails/{Id}",
         BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        bool DeleteUserDetails(string Id);
        #endregion

        [OperationContract]
        [WebGet(UriTemplate = "/IsValidUser/{username}/{password}",
 RequestFormat = WebMessageFormat.Json,
 ResponseFormat = WebMessageFormat.Json,
 BodyStyle = WebMessageBodyStyle.Bare)]
        string IsValidUser(string username, string password);
    }

This is a service for CRUD operation on User and User Detail. Service Implementation is just calling the Database and performing the operation using LINQ to SQL.

After i complete my implementation and try to locally check in either “POSTMAN” or “Advance REST Client” (both being tools to work with REST Service) the request and response was proper. But after i deploy this in Azure, i was getting a Preflight error as shown earlier.

Solution :

We had to add “Access-Control-Allow-Origin” in the request header, and ask the WCF REST service to allow preflight request. Sometimes, depending on your browser a “POST” request may also end up being “OPTION” request.

So, we have to follow below steps to solve the problem,

1. Add a new Global.asax (Active Server Application file) in the WCF service if you do not have one.

2. Update the code in the “Application_BeginRequest” method as below,

protected void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                HttpContext.Current.Response.AddHeader("Cache-Control", "no-cache");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET,PUT, OPTIONS, DELETE");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
                HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
                HttpContext.Current.Response.End();
            }
        }

With this you will be able to see “Access-Control-Allow-Origin” in the request header.
A sample POSTMAN request is as below,

2
But sometimes the REST test Clients would send the request without any problems, but when working with Client (in this case Ionic Framework for cross platform app development) we may still get the error.

How to real time test it ?

I have written a simple “AJAX” client to test this as below,

<div>
    <select id="method">
<option value="get">POST</option>
<option value="post">GET</option>
<option value="put">PUT</option>
</select>
    <input type="button" value="Try it" onclick="sendRequest()" />
    <span id='value1'>(Result)</span></div>
@section scripts {
    <script>
        // TODO: Replace with the URL of your WebService app
        var serviceUrl = 'http://yoururl.cloudapp.net/Service1.svc/UpdateUser';
        var user =
              {
                  "user":
    {
        "Id": "28",
        "UserName": "K999",
        "Password": "k999",
        "IsAdmin": 1
    }
              }



        function sendRequest() {
            var method = $('#method').val();

            $.ajax({
                type: "PUT",
                url: serviceUrl,
                data: JSON.stringify(user),
                crossDomain: true,
                contentType: "application/json; charset=utf-8", // to the server
                ProcessData: true,
            }).done(function (data) {
                $('#value1').text(data);
            }).error(function (jqXHR, textStatus, errorThrown) {
                $('#value1').text(jqXHR.responseText || textStatus);
            });
        }
    </script>
}

Hope it helps somebody. Happy coding !!


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)