Saturday, 2 March 2013

How to return Jsonp from WebApi

This allows you to make service requests from jQuery to a different domain (cross site scripting).

1. Add a Formatters folder to your WebApi project and add the following class:

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
private readonly HttpRequestMessage _request;
private string _callbackQueryParameter;

public JsonpMediaTypeFormatter()
{
SupportedMediaTypes.Add(DefaultMediaType);
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));

MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", "application/json"));
}

public JsonpMediaTypeFormatter(HttpRequestMessage request)
: this()
{
_request = request;
}

public string CallbackQueryParameter
{
get { return _callbackQueryParameter ?? "callback"; }
set { _callbackQueryParameter = value; }
}

public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
if (type == null)
throw new ArgumentNullException("type");
if (request == null)
throw new ArgumentNullException("request");

return new JsonpMediaTypeFormatter(request) { SerializerSettings = SerializerSettings };
}

public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
string callback;
if (IsJsonpRequest(_request, out callback))
{

var writer = new StreamWriter(stream);
writer.Write(callback + "(");
writer.Flush();

return base.WriteToStreamAsync(type, value, stream, content, transportContext).ContinueWith(_ =>
{

//TODO: Inspecting the task status and acting on that is better
writer.Write(")");
writer.Flush();
});
}

return base.WriteToStreamAsync(type, value, stream, content, transportContext);
}

private bool IsJsonpRequest(HttpRequestMessage request, out string callback)
{
callback = null;

if (request == null || request.Method != HttpMethod.Get)
{
return false;
}

var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
callback = query[CallbackQueryParameter];

return !string.IsNullOrEmpty(callback);
}
}
image

2. Add a FormatterConfig class to your App_Start folder

public class FormatterConfig
{
public static void RegisterFormatters(MediaTypeFormatterCollection formatters)
{
formatters.Remove(formatters.JsonFormatter);
formatters.Insert(0, new JsonpMediaTypeFormatter
{
SerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}
});
}
}
image

3. Update your Global.asax.cs file to register the FormatterConfig

public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
FormatterConfig.RegisterFormatters(GlobalConfiguration.Configuration.Formatters);
}
}

That's it!


How to test your service call


Now your services can be called for Json or Jsonp.


Json


You can continue to get your Json response in the normal way:  http://localhost:2626/api/underlyings


image



image


image


Jsonp


You can now call your service passing in the callback to receive Jsonp:

$.getJSON("http://localhost:2626/api/underlyings/jsonp?callback=?", function (result) {
// process response
});

You’ll see here that the response wraps the data in a jQuery function.


image


image


image


Here’s the HTML code I used in this example:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Jsonp test</title>
<script src="Scripts/jquery-1.9.1.min.js" type="text/javascript"></script>
</head>
<body>
<button id="getjsonp">Get JSONP</button>
<h4>Request result:</h4>
<div id="jsonp"></div>

<script type="text/javascript">
$(document).ready(function () {
$("#getjsonp").click(function () {
$.getJSON("http://localhost:2626/api/underlyings/jsonp?callback=?", function (result) {
$(result).each(function(index, item) {
$('#jsonp').append('<p>' + item.currencyPair + '</p>');
});
});
});
})
</script>
</body>
</html>





For a working downloadable example I recommend downloading the WebApiContrib solution for Jsonp:


https://github.com/WebApiContrib/WebApiContrib.Formatting.Jsonp

1 comment:

  1. Thank you for a very informative article and example! Just what I needed.

    ReplyDelete