Web API之手动实现JSONP或者安装配置Cors跨域(七)
照理来说本节也应该讲Web API原理,目前已经探讨完了比较底层的Web API消息处理管道以及Web Host寄宿管道,接下来应该要触及控制器、Action方法,以及过滤器、模型绑定等等,想想也是心痛不已,水太深了,摸索原理关键是太枯燥和乏味了,但是呢,从情感上还是挺乐意去摸索原理,而情绪上不太乐意去探究原理,于是乎,本文就由此诞生了,借此文缓解下枯燥的心情和压抑的情绪。后续继续摸索原理。
接下来我们要讲的就是利用JSONP和利用Cors这两种方式来实现跨域,请看下文。。。。。
JSONP实现跨域Web API并没有提供JSONP Formatter,但是这并不能影响我们前进的脚步,我们可以自定义Formatter来实现JSONP功能。既然是利用JSONP跨域,那么就得简单介绍下JSONP。
为什么需要JSONP?浏览器都是基于同源策略,使其脚本不能跨站点来获得服务器端数据,但是办法总是人想出来的,这个时候就需要JSONP了,当然也可以用别的办法实现,JSONP是一种能实现让基于JavaScript的客户端程序绕过跨站点脚本的限制从而从非当前的服务器上来获得数据的方式。默认情况下,应用程序利用Ajax是不允许访问远程跨域,但是我们可以利用<script>标签加载JSONP来实现这种跨站点限制。这也不失为一种好的解决方案。JSONP的工作原理是当JSON数据返回时通过组合JSON数据,并将其包裹到一个函数中进行调用,利用JQuery更能很好的去实现这点。
假如有这样如下的一个URL:
http://
但我们利用Ajax发出GET请求来获取服务器端数据时那将是轻而易举,但是,但是,但是,重要的前提说三遍,前提是在相同域下,若是不同的域下,利用Ajax来访问数据估计不是这么轻松了吧。但是,但是,但是,重要的话再说三遍,此时我们就利用JSONP来实现跨域,此时将会变成如下请求模式:
http://www.cnblogs.com/CreateMyself/WebAPI/xpy0928?callback=?
发出如下URL请求通过一个callback回调,这样得到的结果是和同一站点的结果是一致的,JQuery会反序列会这些数据并将其推入到函数中。
JSONP数据是怎样的?它主要就是通过调用函数将返回的JSON数据进行包裹,类似于如下形式:
Query7d59824917124eeb85e5872d0a4e7e5d([{"Id":"123","Name":"xoy0928"},{......}])
JSONP的工作原理是怎样的呢?在JavaScript客户端发出请求后,当响应数据时,将其数据作为执行要调用函数的参数,并在其内部将JSON数据进行反序列化
下面我们就原理来进行演示,请看如下代码:
function JSONP(url, callback) { var id = "_" + "Query" + (new Date()).getTime(); //创建一个几乎唯一的id window[id] = function (result) { //创建一个全局回调处理函数 if (callback) callback(result); var getId = document.getElementById(id); //移除Script标签和id getId.parentNode.removeChild(getId); window[getId] = null; } url = url.replace("callback=?", "callback=" + id); var script = document.createElement("script"); //创建Script标签并执行window[id]函数 script.setAttribute("id", id); script.setAttribute("src", url); script.setAttribute("type", "text/javascript"); document.body.appendChild(script); }
简单进行调用则如下:
function JSONPFunction() { JSONP(":23133/api/default?callback=?", function(jsonData){ //将返回的数据jsonData作为调用函数的参数 } };
JSONP在Web API中如何实现呢?上述讲了JSONP原理和实现,那么结合Web API是如何实现的呢?我们只能自定义Formatter来手动实现这个功能,既然是有关于JSON,那么自然是继承于 JsonMediaypeFormatter 了,代码如下:
第一步自定义JsonpFormatter并继承于JsonMediaTypeFormatter:
public class JsonpFormatter : JsonMediaTypeFormatter
{
//当请求过来是带有text/javascript时处理JSONP请求
public JsonpFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
JsonpParameterName = "callback";
}
//查找函数名
public string JsonpParameterName { get; set; }
private string JsonpCallbackFunction;
public override bool CanWriteType(Type type)
{
return true;
}
//重写此方法来捕获请求对象
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, System.Net.Http.HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new JsonpFormatter()
{
JsonpCallbackFunction = GetJsonCallbackFunction(request)
};
//运用JSON.NET来序列化自定义
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;
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();
}
//从查询字符串中获得JSONP Callback回调函数
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;
}
}
此时只需将此自定义类在Web API配置文件中进行注册即可:
GlobalConfiguration .Configuration .Formatters .Insert(0, new JsonpFormatter());
第三步给出后台测试数据:
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/67057.html