[MVC]ASP.NET Web API遇到了JSONP
今天使用Asp.NET MVC中的Web API,使用Jsonp取值時,居然直接跑到了錯誤的函式之中。
查到了「Creating a JSONP Formatter for ASP.NET Web API」,發現目前Asp.NET MVC中的Web API並沒有針對Jsonp來動態去建立CallBack的函式。
所以要自已建立一個JSONP Formatter來處理它。
以下是測試的程式,
Model
namespace MVCInActionChp5.Models
{
public class CustomerSummary
{
public string Name { get; set; }
public string Active { get; set; }
public string ServiceLevel { get; set; }
public string OrderCount { get; set; }
public string MostRecentOrderDate { get; set; }
}
}
測試的Web API
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MVCInActionChp5.Models;
namespace MVCInActionChp5.Controllers
{
public class ApiAController : ApiController
{
// GET api/ApiA/10
public List<CustomerSummary> Get(int id)
{
return new List<CustomerSummary>
{
new CustomerSummary{
Name = "Rainmaker" + id.ToString()
, Active = "Y"
, MostRecentOrderDate = "2012/09/11"
, OrderCount = "3"
, ServiceLevel = "100"
}
};
}
}
}
測試的Html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<script src="Scripts/jquery-1.8.0.js" type="text/javascript"></script>
</head>
<body>
<input type="button" id="btnTestApiA" value="TestApiA" />
<script type="text/javascript">
$(document).ready(function () {
$('#btnTestApiA').bind('click', function () {
var apiurl = "http://localhost:27063/api/apiA/10";
$.ajax({
url: apiurl
, type: 'GET'
, dataType: 'jsonp'
, success: function (result) {
alert(result[0].Name);
},
error: function () {
alert('fail');
}
});
});
});
</script>
</body>
</html>
JSONP Formatter
using System;
using System.IO;
using System.Net;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Net.Http;
using Newtonsoft.Json.Converters;
using System.Web.Http;
//http://www.west-wind.com/weblog/posts/2012/Apr/02/Creating-a-JSONP-Formatter-for-ASPNET-Web-API
namespace MVCInActionChp5.Models
{
/// <summary>
/// Handles JsonP requests when requests are fired with text/javascript
/// </summary>
public class JsonpFormatter : JsonMediaTypeFormatter
{
public JsonpFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
JsonpParameterName = "callback";
}
/// <summary>
/// Name of the query string parameter to look for
/// the jsonp function name
/// </summary>
public string JsonpParameterName { get; set; }
/// <summary>
/// Captured name of the Jsonp function that the JSON call
/// is wrapped in. Set in GetPerRequestFormatter Instance
/// </summary>
private string JsonpCallbackFunction;
public override bool CanWriteType(Type type)
{
return true;
}
/// <summary>
/// Override this method to capture the Request object
/// </summary>
/// <param name="type"></param>
/// <param name="request"></param>
/// <param name="mediaType"></param>
/// <returns></returns>
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new JsonpFormatter()
{
JsonpCallbackFunction = GetJsonCallbackFunction(request)
};
// this doesn't work unfortunately
//formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
// You have to reapply any JSON.NET default serializer Customizations here
formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
return formatter;
}
public override Task WriteToStreamAsync(Type type, object value,
Stream stream,
HttpContent content,
TransportContext transportContext)
{
if (string.IsNullOrEmpty(JsonpCallbackFunction))
return base.WriteToStreamAsync(type, value, stream, content, transportContext);
StreamWriter writer = null;
// write the pre-amble
try
{
writer = new StreamWriter(stream);
writer.Write(JsonpCallbackFunction + "(");
writer.Flush();
}
catch (Exception ex)
{
try
{
if (writer != null)
writer.Dispose();
}
catch { }
var tcs = new TaskCompletionSource<object>();
tcs.SetException(ex);
return tcs.Task;
}
return base.WriteToStreamAsync(type, value, stream, content, transportContext)
.ContinueWith(innerTask =>
{
if (innerTask.Status == TaskStatus.RanToCompletion)
{
writer.Write(")");
writer.Flush();
}
}, TaskContinuationOptions.ExecuteSynchronously)
.ContinueWith(innerTask =>
{
writer.Dispose();
return innerTask;
}, TaskContinuationOptions.ExecuteSynchronously)
.Unwrap();
}
/// <summary>
/// Retrieves the Jsonp Callback function
/// from the query string
/// </summary>
/// <returns></returns>
private string GetJsonCallbackFunction(HttpRequestMessage request)
{
if (request.Method != HttpMethod.Get)
return null;
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
var queryVal = query[this.JsonpParameterName];
if (string.IsNullOrEmpty(queryVal))
return null;
return queryVal;
}
}
}
Global.asax.cs中的Application_Start()中要多加入JsonpFormatter
GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpFormatter());
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^