Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I produce JSONP from an ASP.NET web service for cross-domain calls?

Tags:

I've written a webservice which returns JSON and I've tried to call it using jQuery like this:

$.ajax({
    contentType: "application/json; charset=utf-8",
    url: "http://examplewebsite.com/service.asmx/GetData",
    data: { projectID: 1 },
    dataType: "jsonp",
    success: function () {alert("success");}
});

However the code never calls the success function, despite the webservice call being successful when looking at the HTTP traffic using Fiddler. I think this is because my web service is returning raw JSON instead of JSONP.

How can I produce JSONP as the response from a standard .NET webservice method like this:

[WebMethod(), ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public Project GetData(int projectID)
{
    Project p = new Project();
    p.Name = "foobar";
    return p;
}

Thanks.

like image 736
NickG Avatar asked Jan 08 '13 18:01

NickG


People also ask

Which are the methods used for cross-domain Ajax calls CORS and JSONP?

Two Approaches for cross-domain AJAX requests, Using JSONP (JSON with Padding) Enabling CORS (Cross-Origin Resource Sharing)

How do I get JSONP data?

JSONP is a technique by which you put your request into a script tag URL (which is allowed to any domain) and you pass in that URL a parameter which indicates the name of a function that you want the resulting script that is returned to call and pass it your data.

Why we are used JSONP explain with example?

JSONP stands for JSON with Padding. Requesting a file from another domain can cause problems, due to cross-domain policy. Requesting an external script from another domain does not have this problem. JSONP uses this advantage, and request files using the script tag instead of the XMLHttpRequest object.

Why is JSONP avoided?

JSONP is not actually JSON with padding, it's Javascript code that's executed. JSON is not a real subset of Javascript and the way it is not is important to us: via UTFGrid, we are all UTF-8 masters. JSONP is not safe: it's Javascript that's executed. It's trivial to XSS with JSONP, because JSONP is XSS.


2 Answers

OK, I've eventually figured it out myself. As I found it so hard to find a complete working solution on the web, I've decided to document my working solution here.

A JSONP response is just standard JSON string wrapped in a function call. ASP.NET doesn't seem to provide any way to return the reponse in this format directly, but it's very simple to do this yourself. You do though, have to override the default method of JSON encoding.

Below is an example of JSONP.

functionName({ name: 'value';});

..now this bit: { name: 'value';} is just standard JSON that any JSON serializer will give you, so all we need to do is tack on the function call wrapper. Unfortunately, doing that means we have to 'unwire' (or bypass) the existing JSON encoding which is handled transparently by the framework when you return an object from the web service function.

This is done by overriding the response from the web service function completely by writing the JSONP to the output stream (Response) using our own code. This is actually quite straightforward and I've included an example below.

You can use either the built in DataContractJsonSerializer (from the System.Runtime.Serialization.Json namespace in ASP.NET 3.5+) or the NewtonSoft JSON serializer, and both examples are shown below. I prefer to use the the NewtonSoft JSON (installed from nuget) rather than the built in JSON serializer as I find it gives you more control and also can output nicely formatted human readable JSON for debugging. It's also much faster on paper!

[WebMethod()]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public void GetData(int projectID, string callback)
{
    List<Video> videos = null;
    // <code here to populate list on line above>

    // Method 1: use built-in serializer:
    StringBuilder sb = new StringBuilder();
    JavaScriptSerializer js = new JavaScriptSerializer();
    sb.Append(callback + "(");
    sb.Append(js.Serialize(videos));
    sb.Append(");");    

    // Method 2: NewtonSoft JSON serializer (delete as applicable)
    // StringBuilder sb = new StringBuilder();
    // sb.Append(callback + "(");
    // sb.Append(JsonConvert.SerializeObject(videos, Formatting.Indented)); // indentation is just for ease of reading while testing
    // sb.Append(");");     

    Context.Response.Clear();
    Context.Response.ContentType = "application/json";
    Context.Response.Write(sb.ToString());
    Context.Response.End();
}

This method can then be called using the following JQuery code:

$.ajax({
    crossDomain: true,
    contentType: "application/json; charset=utf-8",
    url: "http://examplewebsite.com/service.asmx/GetData",
    data: { projectID: 1 }, // example of parameter being passed
    dataType: "jsonp",
    success: onDataReceived
});

function onDataReceived(data)
{
    alert("Data received");
    // Do your client side work here.
    // 'data' is an object containing the data sent from the web service 
    // Put a JS breakpoint on this line to explore the data object
}
like image 89
NickG Avatar answered Oct 05 '22 09:10

NickG


Thanks Nick, that was an excellent answer to a problem that I too had a hard time finding at first online. Worked great for me as well.

Wanted to make sure this this line of post got the attention it deserves.

Just wanted to add that I used the built in serializer (System.Runtime.Serialization.Json) and it worked like a charm as well.

        List<orderHistory> orderHistory = null;

        StringBuilder sb = new StringBuilder();
        JavaScriptSerializer js = new JavaScriptSerializer();
        sb.Append(callback + "(");
        sb.Append(js.Serialize(orderHistory));
        sb.Append(");");

        Context.Response.Clear();
        Context.Response.ContentType = "application/json";
        Context.Response.Write(sb.ToString());
        Context.Response.End();
like image 30
Joe Ramos Avatar answered Oct 05 '22 10:10

Joe Ramos