Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mvc webapi cross domain post [duplicate]

Possible Duplicate:
CORS with WebAPI for XmlHttpRequest



I'm trying to implement cross-domain ajax post to my webApi project. I had few troubles with that:
1. I always was getting 204 error until changed my webapi action from

public void submit(Submission model)

to

public bool submit(Submission model)

don't know why, but now I'm getting 200 OK status

2. Still my ajax firing error callback.

3. Long time ago I solved this kind of error of cross-domain posting by adding

HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");

to my controller. But now in webApi i'm inherent from : ApiController and this trick doesn't work. Shows me compiler Error an object reference is required for the non-static field, method, or property"System.Web.HttpContext.Response.get"

I have tryed to post via dataType: 'JSONP' but I get null model.

Here goes Javascript request:

var model = {
        "type": $("#model-type").val(),
        "subject": $("#subject-text").val(),
        "body": $("#body-text").val()
    };

    $.ajax({
        type: "POST",
        dataType: 'JSONP',
        url: $("#submit-url").val(),
        data: model,
        success: function () {
            alert("Succesfully submitted");
        },
        error: function () {
            alert("Error...");
        }
    });

What I'm doing wrong?

SOLVED

Thanks to everybody for helping me out. I found solution in one of the comment links. I used following approach, which I find pretty simple.

Source:
Implementing CORS support in ASP.NET Web APIs


What I made:

1. Created new Class in my project: CorsHandler.cs and just copy-pasted following code:


public class CorsHandler : DelegatingHandler
    {
        const string Origin = "Origin";
        const string AccessControlRequestMethod = "Access-Control-Request-Method";
        const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
        const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
        const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
        const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";

        protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            bool isCorsRequest = request.Headers.Contains(Origin);
            bool isPreflightRequest = request.Method == HttpMethod.Options;
            if (isCorsRequest)
            {
                if (isPreflightRequest)
                {
                    return Task.Factory.StartNew(() =>
                    {
                        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
                        response.Headers.Add(AccessControlAllowOrigin, request.Headers.GetValues(Origin).First());

                        string accessControlRequestMethod = request.Headers.GetValues(AccessControlRequestMethod).FirstOrDefault();
                        if (accessControlRequestMethod != null)
                        {
                            response.Headers.Add(AccessControlAllowMethods, accessControlRequestMethod);
                        }

                        string requestedHeaders = string.Join(", ", request.Headers.GetValues(AccessControlRequestHeaders));
                        if (!string.IsNullOrEmpty(requestedHeaders))
                        {
                            response.Headers.Add(AccessControlAllowHeaders, requestedHeaders);
                        }

                        return response;
                    }, cancellationToken);
                }
                else
                {
                    return base.SendAsync(request, cancellationToken).ContinueWith(t =>
                    {
                        HttpResponseMessage resp = t.Result;
                        resp.Headers.Add(AccessControlAllowOrigin, request.Headers.GetValues(Origin).First());
                        return resp;
                    });
                }
            }
            else
            {
                return base.SendAsync(request, cancellationToken);
            }
        }
    }

  1. Opened my Global.asax and modifyed Application_Start :

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsHandler());
        }

Pay attention to the last line in action.

This approach is compatible with MVC3 and .NET 4.0. Works great, now I can handle "success" and "error" callbacks in ajax.

like image 533
DmitryL Avatar asked Oct 22 '12 19:10

DmitryL


1 Answers

Answers to your questions respectively:

  1. Status 204 is not an error, which means no content to return but everything's good. Here's the definition of 204 in RFC2616

10.2.5 204 No Content

The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.

If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view.

The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.

  1. Could you articulate what's the error you met? The ASP.NET Web API currently doesn't have a JSONP formatter out of box. Here's some 3rd part implementation:
  • http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API
  • http://www.nuget.org/packages/WebApi.JsonP I hope they're helpful.
  1. In Web API the way in which you refers to an Response is not through HttpContext. There are multiple ways to access.

The first option is to define action return HttpResponse directly.

    public HttpResponseMessage Get(int id)
    {
        var response = this.Request.CreateResponse();
        response.StatusCode = HttpStatusCode.OK;
        response.Headers.Add("Access-Control-Allow-Origin", "*");

        return response;
    }

The second option is to use ActionFilter:

// define action filter for cross domain
public class CrossDomainActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        bool needCrossDomain = true;

        if (needCrossDomain)
        {
            actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
        }

        base.OnActionExecuted(actionExecutedContext);
    }
}

// At Controller
// GET api/values/5
[CrossDomainActionFilter]
public string Get(int id)
{
    return "value";
}

The last option is to use MessageHandler:

public class CrossDomainMessageHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);
        response.Headers.Add("Access-Control-Allow-Origin", "*");

        return response;
    }
}
like image 155
Troy Dai Avatar answered Oct 18 '22 13:10

Troy Dai