(转)通过扩展让ASP.NET Web API支持JSONP
原文地址:http://www.cnblogs.com/artech/p/3460544.html
同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝。同源策略以及跨域资源共享在大部分情况下针对的是Ajax请求。同源策略主要限制了通过XMLHttpRequest实现的Ajax请求,如果请求的是一个“异源”地址,浏览器将不允许读取返回的内容。JSONP是一种常用的解决跨域资源共享的解决方案,现在我们利用ASP.NET Web API自身的扩展性提供一种“通用”的JSONP实现方案。
一、JsonpMediaTypeFormatter
在《[CORS:跨域资源共享] 同源策略与JSONP》,我们是在具体的Action方法中将返回的JSON对象“填充”到JavaScript回调函数中,现在我们通过自定义的MediaTypeFormatter来为JSONP提供一种更为通用的实现方式。
我们通过继承JsonMediaTypeFormatter定义了如下一个JsonpMediaTypeFormatter类型。它的只读属性Callback代表JavaScript回调函数名称,改属性在构造函数中指定。在重写的方法WriteToStreamAsync中,对于非JSONP调用(回调函数不存在),我们直接调用基类的同名方法对响应对象实施针对JSON的序列化,否则调用WriteToStream方法将对象序列化后的JSON字符串填充到JavaScript回调函数中。
1: public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
2: {
3: public string Callback { get; private set; }
4:
5: public JsonpMediaTypeFormatter(string callback = null)
6: {
7: this.Callback = callback;
8: }
9:
10: public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
11: {
12: if (string.IsNullOrEmpty(this.Callback))
13: {
14: return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
15: }
16: try
17: {
18: this.WriteToStream(type, value, writeStream, content);
19: return Task.FromResult<AsyncVoid>(new AsyncVoid());
20: }
21: catch (Exception exception)
22: {
23: TaskCompletionSource<AsyncVoid> source = new TaskCompletionSource<AsyncVoid>();
24: source.SetException(exception);
25: return source.Task;
26: }
27: }
28:
29: private void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
30: {
31: JsonSerializer serializer = JsonSerializer.Create(this.SerializerSettings);
32: using(StreamWriter streamWriter = new StreamWriter(writeStream, this.SupportedEncodings.First()))
33: using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter) { CloseOutput = false })
35: {
36: jsonTextWriter.WriteRaw(this.Callback + "(");
37: serializer.Serialize(jsonTextWriter, value);
38: jsonTextWriter.WriteRaw(")");
39: }
40: }
41:
42: public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
43: {
44: if (request.Method != HttpMethod.Get)
45: {
46: return this;
47: }
48: string callback;
49: if (request.GetQueryNameValuePairs().ToDictionary(pair => pair.Key,
50: pair => pair.Value).TryGetValue("callback", out callback))
51: {
52: return new JsonpMediaTypeFormatter(callback);
53: }
54: return this;
55: }
56:
57: [StructLayout(LayoutKind.Sequential, Size = 1)]
58: private struct AsyncVoid
59: {}
60: }
我们重写了GetPerRequestFormatterInstance方法,在默认情况下,当ASP.NET Web API采用内容协商机制选择出与当前请求相匹配的MediaTypeFormatter后,会调用此方法来创建真正用于序列化响应结果的MediaTypeFormatter对象。在重写的这个GetPerRequestFormatterInstance方法中,我们尝试从请求的URL中得到携带的JavaScript回调函数名称,即一个名为“callback”的查询字符串。如果回调函数名不存在,则直接返回自身,否则返回据此创建的JsonpMediaTypeFormatter对象。

二、将JsonpMediaTypeFormatter的应用到ASP.NET Web API中
接下来我们通过于一个简单的实例来演示同源策略针对跨域Ajax请求的限制。如图右图所示,我们利用Visual Studio在同一个解决方案中创建了两个Web应用。从项目名称可以看出,WebApi和MvcApp分别为ASP.NET Web API和MVC应用,后者是Web API的调用者。我们直接采用默认的IIS Express作为两个应用的宿主,并且固定了端口号:WebApi和MvcApp的端口号分别为“3721”和“9527”,所以指向两个应用的URI肯定不可能是同源的。
我们在WebApi应用中定义了如下一个继承自ApiController的ContactsController类型,它具有的唯一Action方法GetAllContacts返回一组联系人列表。
1: public class ContactsController : ApiController
2: {
3: public IEnumerable<Contact> GetAllContacts()
4: {
5: Contact[] contacts = new Contact[]
6: {
7: new Contact{ Name="张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},
8: new Contact{ Name="李四", PhoneNo="456", EmailAddress="lisi@gmail.com"},
9: new Contact{ Name="王五", PhoneNo="789", EmailAddress="wangwu@gmail.com"},
10: };
11: return contacts;
12: }
13: }
14:
15: public class Contact
16: {
17: public string Name { get; set; }
18: public string PhoneNo { get; set; }
19: public string EmailAddress { get; set; }
20: }
现在我们在WebApi应用的Global.asax中利用如下的程序创建这个JsonpMediaTypeFormatter对象并添加当前注册的MediaTypeFormatter列表中。为了让它被优先选择,我们将这个JsonpMediaTypeFormatter对象放在此列表的最前端。
1: public class WebApiApplication : System.Web.HttpApplication
2: {
3: protected void Application_Start()
4: {
5: GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter ());
6: //其他操作
7: }
8: }
接下来们在MvcApp应用中定义如下一个HomeController,默认的Action方法Index会将对应的View呈现出来。
1: public class HomeController : Controller
2: {
3: public ActionResult Index()
4: {
5: return View();
6: }
7: }
如下所示的是Action方法Index对应View的定义。我们的目的在于:当页面成功加载之后以Ajax请求的形式调用上面定义的Web API获取联系人列表,并将自呈现在页面上。如下面的代码片断所示,我们直接调用$.ajax方法并将dataType参数设置为“jsonp”。
1: <html>
2: <head>
3: <title>联系人列表</title>
4: <script type="text/javascript" src="@Url.Content("~/scripts/jquery-1.10.2.js")"></script>
5: </head>
6: <body>
7: <ul id="contacts"></ul>
8: <script type="text/javascript">
9: $(function ()
10: {
11: $.ajax({
12: Type : "GET",
13: url : "http://localhost:3721/api/contacts",
14: dataType : "jsonp",
15: success : listContacts
16: });
17: });
18:
19: function listContacts(contacts) {
20: $.each(contacts, function (index, contact) {
21: var html = "<li><ul>";
22: html += "<li>Name: " + contact.Name + "</li>";
23: html += "<li>Phone No:" + contact.PhoneNo + "</li>";
24: html += "<li>Email Address: " + contact.EmailAddress + "</li>";
25: html += "</ul>";
26: $("#contacts").append($(html));
27: });
28: }
29: </script>
30: </body>
31: </html>
直接运行该ASP.NET MVC程序之后,会得到如下图所示的输出结果,通过跨域调用Web API获得的联系人列表正常地显示出来。

三、针对JSONP的请求和响应
如下所示的针对JSONP的Ajax请求和响应内容。可以看到请求的URL中通过查询字符串“callback”提供了JavaScript回调函数的名称,而响应的主体部分不是单纯的JSON对象,而是将JSON对象填充到回调返回中而生成的一个函数调用语句。
1: GET http://localhost:3721/api/contacts?callback=jQuery110205729522893670946_1386232694513 &_=1386232694514 HTTP/1.1
2: Host: localhost:3721
3: Connection: keep-alive
4: Accept: */*
5: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
6: Referer: http://localhost:9527/
7: Accept-Encoding: gzip,deflate,sdch
8:
9: HTTP/1.1 200 OK
10: Cache-Control: no-cache
11: Pragma: no-cache
12: Content-Type: application/json; charset=utf-8
13: Expires: -1
14: Server: Microsoft-IIS/8.0
15: X-AspNet-Version: 4.0.30319
16: X-SourceFiles: =?UTF-8?B?RTpc5oiR55qE6JGX5L2cXEFTUC5ORVQgV2ViIEFQSeahhuaetuaPreenmFxOZXcgU2FtcGxlc1xDaGFwdGVyIDE0XFMxNDAzXFdlYkFwaVxhcGlcY29ud?=
17: X-Powered-By: ASP.NET
18: Date: Thu, 05 Dec 2013 08:38:15 GMT
19: Content-Length: 248
20:
21: jQuery110205729522893670946_1386232694513([{"Name":"张三","PhoneNo":"123","EmailAddress":"zhangsan@gmail.com"},{"Name":"李四","PhoneNo":"456","EmailAddress":"lisi@gmail.com"},{"Name":"王五","PhoneNo":"789","EmailAddress":wangwu@gmail.com}])
CORS系列文章
[1] 同源策略与JSONP
[2] 利用扩展让ASP.NET Web API支持JSONP
[3] W3C的CORS规范
[4] 利用扩展让ASP.NET Web API支持CORS
[5] ASP.NET Web API自身对CORS的支持: 从实例开始
[6] ASP.NET Web API自身对CORS的支持: CORS授权策略的定义和提供
[7] ASP.NET Web API自身对CORS的支持: CORS授权检验的实施
[8] ASP.NET Web API自身对CORS的支持: CorsMessageHandler
(转)通过扩展让ASP.NET Web API支持JSONP的更多相关文章
- 通过扩展让ASP.NET Web API支持JSONP
同源策略(Same Origin Policy)的存在导致了"源"自A的脚本只能操作"同源"页面的DOM,"跨源"操作来源于B的页面将会被拒 ...
- 通过扩展让ASP.NET Web API支持JSONP -摘自网络
同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的DOM,“跨源”操作来源于B的页面将会被拒绝.同源策略以及跨域资源共享在大部分情况下针对的是Ajax请 ...
- 通过扩展让ASP.NET Web API支持W3C的CORS规范
让ASP.NET Web API支持JSONP和W3C的CORS规范是解决"跨域资源共享"的两种途径,在<通过扩展让ASP.NET Web API支持JSONP>中我们 ...
- 通过扩展让ASP.NET Web API支持W3C的CORS规范(转载)
转载地址:http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-04.html CORS(Cross-Origin Resource Shari ...
- 通过微软的cors类库,让ASP.NET Web API 支持 CORS
前言:因为公司项目需要搭建一个Web API 的后端,用来传输一些数据以及文件,之前有听过Web API的相关说明,但是真正实现的时候,感觉还是需要挺多知识的,正好今天有空,整理一下这周关于解决COR ...
- 让ASP.NET Web API支持POST纯文本格式(text/plain)的数据
今天在web api中遇到了这样一个问题,虽然api的参数类型是string,但只能接收post body中json格式的string,不能接收原始string. web api是这样定义的: pub ...
- 让ASP.NET Web API支持$format参数的方法
在不使用OData的情况下,也可以让ASP.NET Web API支持$format参数,只要在WebApiConfig里添加如下三行红色粗体代码即可: using System; using Sys ...
- [转]让ASP.NET Web API支持$format参数的方法
本文转自:http://www.cnblogs.com/liuzhendong/p/4228592.html 在不使用OData的情况下,也可以让ASP.NET Web API支持$format参数, ...
- 让ASP.NET Web API支持text/plain内容协商
ASP.NET Web API的内容协商(Content Negotiation)机制的理想情况是这样的:客户端在请求头的Accept字段中指定什么样的MIME类型,Web API服务端就返回对应的M ...
随机推荐
- 使用wifi网卡笔记4---工具dhcp及全自动使用wifi
dhcp : 使得WIFI网卡动态获取IP ·1.源码获取 输入dh看有哪些应用程序,输入dhclient -v查看一下相关信息,源码获取的网址 2.开发环境配置 解压 tar xzf dhcp-4. ...
- IOZONE测试工具使用方法(转载)
IOZONE主要用来测试操作系统文件系统性能的测试工具,该工具所测试的范围主要有,write , Re-write, Read, Re-Read, Random Read, Random Write, ...
- svn使用常用错误
描述 :SVN更新提交显示文件被锁异常: Working copy XXXXXXXX locked Please execute "Cleanup" command 原因 : 待查 ...
- C# 命名空间(Namespace)
命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式.在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突. 也即是不同的命名空间可以有相同的类名
- springboot-shiro chapter01——创建springboot webmvc环境
简介:本章提供了springboot简单例子,主要包含以下内容 1.pom.xml依赖资源 2.springboot配置 3.web应用spring mvc 环境: IDEA15+ JDK1.8+ M ...
- 多线程环境下的UI异步操作
转自原文 多线程环境下的UI异步操作 解决VS中,线程间不可互操作的问题,一揽子解决方案: 一.首先,定义一个类:SetControlProperty using System.Reflection; ...
- JNI 里使用STL
JNI里的c或者c++ 调用stl 的时候,比如引入map头文件: #include <map> 在cygwin使用NDK编译的时候,会提示: fatal error:map: No su ...
- 服务器意外重启导致storm报错的问题处理
解决方法 cat /opt/storm-0.8.2/conf/storm.yaml中找到storm.local.dir设定的目录,备份supervisor和workers两个文件夹,#nohup su ...
- 使用heap profiler进行内存占用分析
最近在项目中用到了google的heap profiler工具来分析内存占用,效果非常显著,因此在这里写一篇博客记录一下使用过程中遇到的一些问题. heap profiler依赖于tcmalloc,所 ...
- java 蓝桥杯算法提高 _2最大最小公倍数
解题思路: 1. n是奇数,那就最大的三个数相乘2. n是偶数,得分两种情况了, ①如果n不是3的倍数,那就s=n*(n-1)*(n-3)---n与n-2同为偶数,故排除一个n-2: ②n是3的倍数, ...