locked
Asp.net WebApi supports json, xml, jsonp? RRS feed

  • Question

  • User892074940 posted

    What's required to have our WebApi supports multiple Formatter?

    Here what i've:

    In my gloabl file, i've put
    GlobalConfiguration.Configuration.Formatters.Clear();
    GlobalConfiguration.Configuration.Formatters.Add(new JsonpMediaTypeFormatter());

    JsonpMediaTypeFormatter is custom Jsonp formatter that i've

    After this, when i call,
    function jsonpCallback(data) {
            alert('in' + data);
            var list = $('#courses');
            for (var i = 0; i < data.length; i++) {
                var course = data[i];
                list.append('<li>' + course.name + '</li>');
            }
        }
    $.getJSON("http://localhost:64009/api/courses?callback=?", null, jsonpCallback);

    It worked.

    But, from my global file, if i remove
    GlobalConfiguration.Configuration.Formatters.Clear();
    Then it doesn't work.

    What if i want to support multiple formatter?
    As i can use HttpClient for CORS and ask for xml or json or jsop?

    Please advice

    Saturday, May 5, 2012 7:58 PM

Answers

  • User1379254898 posted

    You need to pad it yourself. JSONP is JSON anyway.

    function jsonp(url, callback) {
        // create a unique id var id = "_" + (new Date()).getTime();
    
        // create a global callback handler window[id] = function (result) {
            // forward the call to specified handler if (callback)
                callback(result);
                        
            // clean up: remove script and id var sc = document.getElementById(id);
            sc.parentNode.removeChild(sc);
            window[id] = null;
        }
    
        url = url.replace("callback=?", "callback=" + id);
                    
        // create script tag that loads the 'JSONP script' // and executes it calling window[id] function var script = document.createElement("script");
        script.setAttribute("id", id);
        script.setAttribute("src", url);
        script.setAttribute("type", "text/javascript");
        document.body.appendChild(script);
    }

    Working example here:

    http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, May 6, 2012 10:27 AM

All replies

  • User1379254898 posted

    You can add as many formetters as you need. Just use Add to add to the end or use Insert to add to a specific location in the collection.

    You should not really clear all formatters, this will render the whole stack useless. You can remove individual formatter by using Remove or RemoveAt.

    You can have multiple formatters supporting the same media type. In that case order of the formatters becomes the key point for the framework to choose the formatter: the first one wins.

    Have a look at these articles:

    http://www.strathweb.com/2012/04/different-mediatypeformatters-for-same-mediaheadervalue-in-asp-net-web-api/

    http://weblogs.asp.net/gunnarpeipman/archive/2012/04/20/asp-net-web-api-extending-content-negotiation-with-new-formats.aspx

    http://byterot.blogspot.co.uk/2012/04/aspnet-web-api-series-part-5.html

    Sunday, May 6, 2012 5:10 AM
  • User892074940 posted

    Thanks Aliostad.

    Here what i've:

    In my project2, global file, i've addedd
    GlobalConfiguration.Configuration.Formatters.Add(new JsonpMediaTypeFormatter());

    I'm trying to call project2(http://localhost:64009) from project1(http://localhost:53723)
    $.ajax({
                url: "http://localhost:64009/api/courses",
                dataType: "jsonp",
                jsonp: "callback",
                jsonpCallback: "jsonpCallback"
            });

    Specifying that i require jsonp format and project2 supports jsonp format and also we added custom jsonp format to Configuration.

    But when i make that $.ajax call, i get the following in fiddler:

    Request:
    GET http://localhost:64009/api/courses?callback=jsonpCallback&_=1336310868941 HTTP/1.1
    Host: localhost:64009
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
    Accept: */*
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    Referer: http://localhost:53723/
    Pragma: no-cache
    Cache-Control: no-cache

    Response:
    HTTP/1.1 200 OK
    Server: ASP.NET Development Server/10.0.0.0
    Date: Sun, 06 May 2012 13:27:50 GMT
    X-AspNet-Version: 4.0.30319
    Transfer-Encoding: chunked
    Cache-Control: no-cache
    Pragma: no-cache
    Expires: -1
    Content-Type: application/json; charset=utf-8
    Connection: Close

    49
    [{"id":0,"name":".net"},{"id":1,"name":"oData"},{"id":2,"name":"WebApi"}]
    0

    If you see the response, it gives me json format and not jsonp format even i specify in my ajax call that i need jsonp format.
    When i put breakpoint @ my jsonp formatter, it's not even reaching that point.

    Last point:
    if i try "Insert" instead "Add", it works as i'm inserting @ 0th index
    GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter());

    Any idea? What i'm missing.

    Sunday, May 6, 2012 9:44 AM
  • User1379254898 posted

    You need to pad it yourself. JSONP is JSON anyway.

    function jsonp(url, callback) {
        // create a unique id var id = "_" + (new Date()).getTime();
    
        // create a global callback handler window[id] = function (result) {
            // forward the call to specified handler if (callback)
                callback(result);
                        
            // clean up: remove script and id var sc = document.getElementById(id);
            sc.parentNode.removeChild(sc);
            window[id] = null;
        }
    
        url = url.replace("callback=?", "callback=" + id);
                    
        // create script tag that loads the 'JSONP script' // and executes it calling window[id] function var script = document.createElement("script");
        script.setAttribute("id", id);
        script.setAttribute("src", url);
        script.setAttribute("type", "text/javascript");
        document.body.appendChild(script);
    }

    Working example here:

    http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, May 6, 2012 10:27 AM
  • User892074940 posted

    Thanks Aliostad.

    From "http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API", last paragraph:
    Note that I added the formatter at the top of the list of formatters, rather than adding it to the end which is required. The JSONP formatter needs to fire before any other JSON formatter since it relies on the JSON formatter to encode the actual JSON data. If you reverse the order the JSONP output never shows up. So, in general when adding new formatters also try to be aware of the order of the formatters as they are added.

    This explains why:
    it's not working when i use GlobalConfiguration .Configuration .Formatters.Add(JsonpFormatter)
    It's working when i user GlobalConfiguration .Configuration .Formatters.Insert(0, JsonpFormatter)

    I think Microsoft should improve this and make it like:
    Order for Formatter shouldn't matter if request specifically menstioned dataType/Accepts (json, xml, jsonp, etc.)
    if request doesn't specify dataType/Accepts, then by default it takes first formatter which is json.

    Any comment on this?

    Sunday, May 6, 2012 11:10 AM
  • User892074940 posted

    I research more on last paragraph:
    From "http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API", last paragraph:
    Note that I added the formatter at the top of the list of formatters, rather than adding it to the end which is required. The JSONP formatter needs to fire before any other JSON formatter since it relies on the JSON formatter to encode the actual JSON data. If you reverse the order the JSONP output never shows up. So, in general when adding new formatters also try to be aware of the order of the formatters as they are added.


    I found something wiered.

    1. I keep following in my global file (project2: http://localhost:64009/):
    GlobalConfiguration.Configuration.Formatters.Add(new JsonpMediaTypeFormatter());

    2. From the same project (project2), i call the following:
    $.ajax({
            url: "/api/courses",
            dataType: "jsonp",
            jsonp: "callback",
            jsonpCallback: "jsonpCallback"
        });

    And it worked! Menas it returns jsonp even we've added jsonp at last and not inserted @ 0th index

    3. The wiered thing is:
    From another project (project1: http://localhost:53723/, Cross domain), when i call the same webapi in project2, it returns json but not jsonp:
    $.ajax({
                url: "http://localhost:64009/api/courses",
                dataType: "jsonp",
                jsonp: "callback",
                jsonpCallback: "jsonpCallback"
            });

    So last paragraph is correct for Cross domain and not for same domain?

    Please advice!

    Sunday, May 6, 2012 11:25 AM
  • User892074940 posted

    One more question on unknown format:
    http://weblogs.asp.net/gunnarpeipman/archive/2012/04/19/asp-net-web-api-how-content-negotiation-works.aspx?CommentPosted=true#commentmessage

    I've added jsonp formatter to my GlobalConfiguration

    GlobalConfiguration.Configuration.Formatters.Add(new JsonpMediaTypeFormatter());

    Then i try to call my webapi using format that's not supported..that means it should return json:

    $.ajax({

           url: "/api/courses",

           dataType: "x-vcard",

           type: "GET",

           success: function (data) {

               var list = $('#courses');

               for (var i = 0; i < data.length; i++) {

                   var course = data[i];

                   list.append('<li>' + course.name + '</li>');

               }

           },

           statusCode: {

               401: function () {

                   alert('Login please');

               },

               200: function (jqXHR, textStatus, errorThrown) {

                   alert('succcess');

               }

           },

           error: function (jqXHR, textStatus, errorThrown) {

               alert('error');

               alert(jqXHR.statusText + " ----- " + jqXHR.responseText);

           }

       });

    Here's request/response from fiddler:

    Request:
    GET localhost/.../courses HTTP/1.1
    Host: localhost:64009
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
    Accept: */*
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    X-Requested-With: XMLHttpRequest
    Referer: localhost/.../courses

    Response:
    HTTP/1.1 200 OK
    Server: ASP.NET Development Server/10.0.0.0
    Date: Sun, 06 May 2012 16:01:07 GMT
    X-AspNet-Version: 4.0.30319
    Transfer-Encoding: chunked
    Cache-Control: no-cache
    Pragma: no-cache
    Expires: -1
    Content-Type: application/json
    Connection: Close

    49
    [{"id":0,"name":".net"},{"id":1,"name":"oData"},{"id":2,"name":"WebApi"}]
    0

    And it returns json, but my ajax callback goes to error and shows the following message:

    parseerror - [{"id":0,"name":".net"},{"id":1,"name":"oData"},{"id":2,"name":"WebApi"}]

    why it's not calling success callback function?

    Sunday, May 6, 2012 1:19 PM