同源策略(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}])

通过扩展让ASP.NET Web API支持JSONP -摘自网络的更多相关文章

  1. 通过扩展让ASP.NET Web API支持JSONP

    同源策略(Same Origin Policy)的存在导致了"源"自A的脚本只能操作"同源"页面的DOM,"跨源"操作来源于B的页面将会被拒 ...

  2. (转)通过扩展让ASP.NET Web API支持JSONP

    原文地址:http://www.cnblogs.com/artech/p/3460544.html 同源策略(Same Origin Policy)的存在导致了“源”自A的脚本只能操作“同源”页面的D ...

  3. 通过扩展让ASP.NET Web API支持W3C的CORS规范

    让ASP.NET Web API支持JSONP和W3C的CORS规范是解决"跨域资源共享"的两种途径,在<通过扩展让ASP.NET Web API支持JSONP>中我们 ...

  4. 通过扩展让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 ...

  5. 通过微软的cors类库,让ASP.NET Web API 支持 CORS

    前言:因为公司项目需要搭建一个Web API 的后端,用来传输一些数据以及文件,之前有听过Web API的相关说明,但是真正实现的时候,感觉还是需要挺多知识的,正好今天有空,整理一下这周关于解决COR ...

  6. 让ASP.NET Web API支持POST纯文本格式(text/plain)的数据

    今天在web api中遇到了这样一个问题,虽然api的参数类型是string,但只能接收post body中json格式的string,不能接收原始string. web api是这样定义的: pub ...

  7. 让ASP.NET Web API支持$format参数的方法

    在不使用OData的情况下,也可以让ASP.NET Web API支持$format参数,只要在WebApiConfig里添加如下三行红色粗体代码即可: using System; using Sys ...

  8. [转]让ASP.NET Web API支持$format参数的方法

    本文转自:http://www.cnblogs.com/liuzhendong/p/4228592.html 在不使用OData的情况下,也可以让ASP.NET Web API支持$format参数, ...

  9. 让ASP.NET Web API支持text/plain内容协商

    ASP.NET Web API的内容协商(Content Negotiation)机制的理想情况是这样的:客户端在请求头的Accept字段中指定什么样的MIME类型,Web API服务端就返回对应的M ...

随机推荐

  1. cdev、udev

    udev :应用层的守护进程,由启动脚本加载,负责建立热拨插的接点 cdev :建立字符设备接口 platform device :相关平台直接总线建立的设备,主要出现需要自己直接挂到平台的时候,因为 ...

  2. [HIHO1082]然而沼跃鱼早就看穿了一切(字符串水题)

    题目链接:http://hihocoder.com/problemset/problem/1082 数据范围小,胡搞. /* ━━━━━┒ギリギリ♂ eye! ┓┏┓┏┓┃キリキリ♂ mind! ┛┗ ...

  3. IE JS编程需注意的内存释放问题

    1.给DOM对象添加的属性是一个对象的引用.范例:var MyObject = {};document.getElementById('myDiv').myProp = MyObject;解决方法:在 ...

  4. HDU 1850 (Nim博弈 取胜方案数) Being a Good Boy in Spring Festival

    考虑到Bouton定理的证明过程,设n个数的Nim和(异或和)为X,其最高位的1在第k位,那么n个数中一定有个y的第k为也是个1. 将y的数量变为X xor y,那么n的数的Nim和为0,便转为先手必 ...

  5. Uva 10382 (区间覆盖) Watering Grass

    和 Uva 10020几乎是一样的,不过这里要把圆形区域转化为能够覆盖的长条形区域(一个小小的勾股定理) 学习一下别人的代码,练习使用STL的vector容器 这里有个小技巧,用一个微小量EPS来弥补 ...

  6. 51nod1403 有趣的堆栈

    看成括号序列的话第二种方法其实就是左括号和右括号之间有多少对完整的括号. #include<cstdio> #include<cstring> #include<ccty ...

  7. 漫游Kafka设计篇之性能优化

    Kafka在提高效率方面做了很大努力.Kafka的一个主要使用场景是处理网站活动日志,吞吐量是非常大的,每个页面都会产生好多次写操作.读方面,假设每个消息只被消费一次,读的量的也是很大的,Kafka也 ...

  8. 漫游Kafka设计篇之数据持久化

    Kafka大量依赖文件系统去存储和缓存消息.对于硬盘有个传统的观念是硬盘总是很慢,这使很多人怀疑基于文件系统的架构能否提供优异的性能.实际上硬盘的快慢完全取决于使用它的方式.设计良好的硬盘架构可以和内 ...

  9. (转)iOS动画Core Animation

    文章转载:http://blog.sina.com.cn/s/blog_7b9d64af0101b8nh.html 在iOS中动画实现技术主要是:Core Animation. Core Animat ...

  10. UVA 11354 Bond 邦德 (RMQ,最小瓶颈MST)

    题意: n个城市,m条路,每条路有个危险值,要使得从s走到t的危险值最小.回答q个询问,每个询问有s和t,要求输出从s到t最小的危险值.(5万个点,10万条边) 思路: 其实要求的是任意点对之间的最小 ...