ASP Web API POST request with CORS and IE9 (XDomai

2019-05-07 20:20发布

I've been going crazy here trying to get jquery.ajax to work with ie9. So I have a ASP Web API 2 Rest API that implements CORS. CORS requests from all browsers work. IE9 didnt work since it uses the XDomainRequest. I managed to get it too work by making a custom implementation of ajaxTransport for IE9.

Right now GET requests seem to work fine. But when I do a post request from IE9 I get a HTTP error 415 - unsuportted media type.

I've set the content-type to:"application/json" and I've also tried "application/x-www-form-urlencoded", but from what I understood XDomainRequest doesnt support everything with custom headers? Does anybody know if something specific needs to be setup on the WebAPI or do I need to tweak the request?

My request looks like this:

                        $.ajax({
                        url: hostname + "/api/DDC/Book",
                        type: "POST",

                        contentType: "application/json",
                        data: {
                            DealID: function () {
                                return viewModel.get("DealID");
                            },
                            LocationID: function () {
                                return viewModel.get("LocationID");
                            },
                            Time: function () {
                                return viewModel.get("selectedDateTime.Time");
                            }

                        }
                    })

On the server I have this:

[HttpPost("DDC/Book")]
    [EnableCors(origins: "*", headers: "*", methods: "POST, GET, OPTIONS, PUT, DELETE")]
    public dynamic Post(BookModel model)
    {
       .........

When I analyze the failed request in the IE debugger this are the request headers that get sent out:

Key Value
Request POST //api/DDC/Book HTTP/1.1
Accept  */*
Origin  http://myurl.com
Accept-Language hr-HR
Accept-Encoding gzip, deflate
User-Agent  Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host    www.somehost.com
Content-Length  55
DNT 1
Connection  Keep-Alive
Cache-Control   no-cache

I'm really losing all hope here and IE is making my go crazy (damn you Microsoft :D ), so any help or advice is much appriciated.

EDIT: From more reasearch I found out that WebAPI requires a content-type to work and XDomainRequest doesnt send out one. So the only solution I see is too tweak my webapi to have a default content-type when nothing is set. Don't know how to this yet though

EDIT2: Hacked my way through temporarily by transforming all my POSTs, to GETs, dont know how smart is this, but I see no bigger problem with it now, so it will do until I fix the problem

3条回答
叛逆
2楼-- · 2019-05-07 21:00

Just to confirm what you have already edited into your question over several updates: yes, XDomainRequest does not include a Content-Type header in the request. As you may know by now, you can't set any headers via this transport.

The lack of a Content-Type is particularly problematic for most server-side frameworks, as this means they will be unable to parse the content of the response automatically. In the absence of a Content-Type header, RFC 2616 says the body is assumed to be application/octet-stream, which is likely not what you want in this case. So, you'll need to "manually" parse the request body server-side by hard-coding the expected Content-Type for the associated request in this case.

I would strongly recommend you not simply convert all of your POSTs to GETs. GET requests should be "safe", per RFC 2616. By simply renaming all of your POSTs to GETs, you are no longer following the defined and accepted semantics of GET requests. In other words, don't do this.

查看更多
Fickle 薄情
3楼-- · 2019-05-07 21:09

Dennis' answer above uses async which is only available in .NET 4.5. For .NET 4 and possibly lower, use the following delegating handler instead:

 public class DefaultContentTypeMessageHandler : DelegatingHandler
 {
      protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
      {
          if (request.Method == HttpMethod.Post && request.Content.Headers.ContentType == null)
          {
              request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
          }
          return base.SendAsync(request, cancellationToken);
      }
 }

Also, don't forget your USING statements, you will need:

using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
查看更多
成全新的幸福
4楼-- · 2019-05-07 21:10

Managed to solve it myself. As pointed by Ray Nicholus when there is no Content-Type ASP Web API defaults to an "application/octet-stream" Content-Type. I need a default of "application/x-www-form-urlencoded".

I managed to achive this by writing my own simple message handler that checks an incoming requests "Content-Type" and if nothing is present it adds an "application/x-www-form-urlencoded" one.

This is the code:

public class DefaultContentTypeMessageHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Content.Headers.ContentType == null)
            request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");


        var response = await base.SendAsync(request, cancellationToken);


        return response;
    }

}

UPDATE:

As written by Robert Christ in the comment below I am extending the answer a bit for those who have not worked with message handlers before:

For those who don't understand at first glance, DelegatingHandlers allow you to modify requests / response objects before they really hit the WebAPI framework internals. Nothing else in the framework really lets you modify the incoming request before model binding, without actually writing custom model binders (eugh). so instead, here, you can sniff out a null content type (which is guaranteed by shortcomings in the XDomainRequest spec), update it to xml or json, and you will be able to parse the incoming request correctly.

After you have written a message handler you need to register it with WebAPI. You do that in the WebApiConfig class:

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

        config.MessageHandlers.Add(new DefaultContentTypeMessageHandler());
        // Rest of your code

     }
 }
查看更多
登录 后发表回答