jquery - Cors, Web Api, IE8, Post Complex Data -


as part of work environment need support ie8, move forward technology, cors.

i'm having trouble posting complex objects cors service in ie8. object null. below steps reproduce. if needed can upload project github.

i've created new mvc4 project. added api controller. , made following changes.

to support preflight complex cors calls (global.asax):

    protected void application_beginrequest()     {         //this needed preflight message         //https://stackoverflow.com/questions/13624386/handling-cors-preflight-requests-to-asp-net-mvc-actions         if (request.headers.allkeys.contains("origin") && request.httpmethod == "options")  {  response.flush(); }     } 

source: handling cors preflight requests asp.net mvc actions

to support text/plain (ie8 sends text/plain cors)(global.asax):

    protected void application_start()     {         //this needed support text/plain         httpconfiguration config = globalconfiguration.configuration;         config.formatters.jsonformatter.supportedmediatypes.add(new mediatypeheadervalue("text/plain"));         config.formatters.remove(config.formatters.formurlencodedformatter);         config.formatters.remove(config.formatters.xmlformatter);           ...     } 

credit: posting text/plain complex object in webapi cors

to support additional function names other verbs (put/post/etc) (webapiconfig.cs)"

    public static void register(httpconfiguration config)     {         config.routes.maphttproute(             name: "apicustom",             routetemplate: "api/{controller}/{action}/{id}",             defaults: new { id = routeparameter.optional }         );          ...     } 

to support cors (web.config)

<httpprotocol>    <customheaders>      <!-- cors -->      <add name="access-control-allow-origin" value="*" />      <add name="access-control-allow-headers" value="content-type" />    </customheaders> </httpprotocol> 

api controller, called personcontroller.cs

 public class personcontroller : apicontroller {      public list<string> get()     {         list<string> s = new list<string>();         s.add("s");         s.add("t");         s.add("u");         return s;     }        [serializable()]     public class basereply     {         public bool successful = true;         public string error;     }     [serializable()]     public class updatesomethingreply:  basereply     {         public updatesomethingrequest request;         public list<string> stuff = new list<string>();     }     [serializable()]     public class updatesomethingrequest     {         public int hasint;         public string hasstring;     }     //[frombody]      [httppost]     public updatesomethingreply updatesomething([frombody] updatesomethingrequest request)     {         string body = request.content.readasstringasync().result;         updatesomethingreply reply = new updatesomethingreply();         reply.request = request;          reply.stuff.add("v");         reply.stuff.add("w");         reply.stuff.add("x");         return reply;     } 

that extent on changes on service. next create client. mvc4 project. pretty basic stuff here.

to polyfill ie8 cors (index.cshtml):

<script src="~/scripts/jquery.xdomainrequest.js"></script> 

source: https://github.com/moonscript/jquery-ajaxtransport-xdomainrequest

to call cors service

 $(document).ready(function () {         $.when(           $.ajax({               url: urls.person.updatesomething,               type: 'post',               contenttype: "application/json; charset=utf-8",               datatype: 'json',               data: json.stringify({                   hasint: 1,                   hasstring: "u"               })           })         )         .fail(function (jqxhr, textstatus, errorthrown) {         })         .done(function (data) {             console.log(json.stringify(data));         });          $.when(           $.ajax({               url: urls.person.get,               datatype: 'json'           })         )         .fail(function (jqxhr, textstatus, errorthrown) {         })         .done(function (data) {             console.log(json.stringify(data));         });          $.when(           $.ajax({               url: urls.person.updatesomething,               type: 'post',               contenttype: "text/plain",               datatype: 'json',               data: json.stringify({                   hasint: 1,                   hasstring: "u"               })           })         )         .fail(function (jqxhr, textstatus, errorthrown) {         })         .done(function (data) {             console.log(json.stringify(data));         });     }); 

as stated earlier 3 calls complete in ie8. request object in service null in ie8 , in firefox populated, when force content-type text/plain

ie8 console output:

{"request":null,"stuff":["v","w","x"],"successful":true,"error":null} 

firefox console output:

{"request":{"hasint":1,"hasstring":"u"},"stuff":["v","w","x"],"successful":true,"error":null} 

update 9/25/2013

i can confirm body being sent, isn't being parsed web api. if add following hack return data expected. in firefox body empty , request object populated. in ie8 body still contains contents , request null.

    [httppost]     public updatesomethingreply updatesomething(updatesomethingrequest request)     {         if (request == null && request.content.readasstringasync().result !="")         {             request = jsonconvert.deserializeobject<updatesomethingrequest>(request.content.readasstringasync().result);        }          updatesomethingreply reply = new updatesomethingreply();         reply.request = request;         reply.body=request.content.readasstringasync().result;         reply.headers = request.headers.tostring();         reply.stuff.add("v");         reply.stuff.add("w");         reply.stuff.add("x");         return reply;     } 

here's code talking about. create new class, created delegatinghandlers folder in webapi project (but again, have filters folder, model bindings folder...)

i've included tons of comments remove.

the below assumes ie 8/9 sending "json" data. if webapi implementation allows content negotiation, , want include feature ie8/9 need add few if statements below code, should more enough going. stated accept json ie 8/9.

namespace redacted.webapi.delegatinghandlers {     using system.net.http;     using system.net.http.headers;     using system.threading;     using system.threading.tasks;      /// <summary>     /// gives webapi ability handle xdomainrequest objects embedded json data.     /// </summary>     public class xdomainrequestdelegatinghandler : delegatinghandler     {         protected override task<httpresponsemessage> sendasync(             httprequestmessage request, cancellationtoken cancellationtoken)         {             // xdomainrequest objects set content type null, unchangable setting.             // microsoft specification states xdomainrequest has contenttype of text/plain, documentation wrong.             // obviously, breaks every specification, turns out extensibility             // point handle before request hits webapi framework, here.              // read apology developer created xdomainrequest object, see here:              // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx              // international specification, null content type supposed result in application/octect-stream (spelling mistake?),             // since such edge case, webapi framework doesn't convert before hit point.  unlikely,              // possible in future web.api release, need sniff here octect header.             if (request.content.headers.contenttype == null)             {                 request.content.headers.contenttype = new mediatypeheadervalue("application/json");             }              return base.sendasync(request, cancellationtoken);         }     } } 

my webapiconfig file looks follows:

        public static void register(httpconfiguration config)         {              // normal config.routes statements go here              // deserialize / model bind ie 8 , 9 ajax requests             config.messagehandlers.add(new xdomainrequestdelegatinghandler());         } 

then make sure post calls ie 8 , 9 compliant, in js put following (though need include if consuming own api)

esbpost: function (apiurl, apidata, fonsuccess, fonfailure) {     $.support.cors = true; // not sure need this.      var testmodernajax = function () {         if (window.xmlhttprequest) {             var testrequest = new xmlhttprequest;              // ie 8 / 9 jquery can create xmlhttprequest objects, modern              // cors implementing browsers (everything + ie10) include withcredentials specification.             if ('withcredentials' in testrequest) {                 return true;             }             return false;         }         return false;     };      var testmsieajax = function () {         if (window.xdomainrequest) {             return true;         }         return false;     };      //all browsers, , ie 10     if (testmodernajax()) {         $.ajax({             url: apiurl,             type: 'post',             datatype: 'json',             data: apidata,             success: function (result) {                 if (fonsuccess) {                     fonsuccess(result);                 }             },             error: function (jqxhr, textstatus, errorthrown) {                 if (fonfailure) {                     fonfailure(jqxhr, textstatus, errorthrown);                 }             }         });     //ie 8 / 9     } else if (testmsieajax()) {         var xdr = new xdomainrequest();         xdr.onload = function () {             var parsedresponse = $.parsejson(xdr.responsetext);             if (fonsuccess) {                 fonsuccess(parsedresponse);             }         };         xdr.onerror = function () {             if (fonfailure) {                 fonfailure();             }         };         xdr.onprogress = function () { };         xdr.open("post", apiurl);         xdr.send(json.stringify(apidata));     } else {         // ie 7 can ajax calls through flash/iframe exploit, earlier not include ajax support.         throw new 'this browser unsupported solution.';     } }, 

personally, i'm using jsonp gets, , not using puts or deletes whatsoever, that's sufficient me. if project on again, use puts , deletes. make ie 8 / 9 handle cross domain puts , deletes apparently common practice include new node on data being sent, or in header, called variant of "type", , use string "put" or "delete". i'm not sure i'd sniff out though.

enabling cors easy putting following in web.config.

<system.webserver>     <httpprotocol>       <customheaders>         <add name="access-control-allow-origin" value="*" />         <!--<add name="access-control-allow-methods" value="get, post, put, delete, options" />-->       </customheaders>     </httpprotocol> 

as can see in above comment, can restrict cors originating url (the *) , type of request (put, post, etc). totally makes stuff this completely unnecessary. this guy's blog gives walkthrough.

and that's literally need brand new webapi project make support both cors , ie 8/9.


Comments

Popular posts from this blog

c# - How Configure Devart dotConnect for SQLite Code First? -

java - Copying object fields -

c++ - Clear the memory after returning a vector in a function -