通过扩展让ASP.NET Web API支持JSONP
同源策略(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: string Callback { get; private set; } 4:?
5: public JsonpMediaTypeFormatter(string callback = null) 6: {
7: this.Callback = callback; 8: }
9:?
10: 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,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,HttpContent content) 30: {
31: JsonSerializer serializer = JsonSerializer.Create(this.SerializerSettings); 32: using(StreamWriter streamWriter = new StreamWriter(writeStream,1)">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: override MediaTypeFormatter GetPerRequestFormatterInstance(Type type,HttpRequestMessage request,MediaTypeHeaderValue mediaType) 43: {
44: if (request.Method != HttpMethod.Get) 45: {
46: this; 47: }
48: string callback; 49: if (request.GetQueryNameValuePairs().ToDictionary(pair => pair.Key, 50: pair => pair.Value).TryGetValue("callback",1)">out callback)) 51: {
52: new JsonpMediaTypeFormatter(callback); 53: }
54: this; 55: }
56:?
57: [StructLayout(LayoutKind.Sequential,Size = 1)]
58: 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返回一组联系人列表。 class ContactsController : ApiController
public IEnumerable<Contact> GetAllContacts()
4: {
5: Contact[] contacts = new Contact[] 6: {
7: new Contact{ Name="张三",PhoneNo="123",EmailAddress="zhangsan@gmail.com"}, 8: "李四",1)">"456",1)">"lisi@gmail.com"}, 9: "王五",1)">"789",1)">"wangwu@gmail.com"}, 10: };
11: return contacts; 12: }
13: }
14:?
15: class Contact 16: {
17: string Name { get; set; } 18: string PhoneNo { get; set; } 19: string EmailAddress { get; set; } 20: }
现在我们在WebApi应用的Global.asax中利用如下的程序创建这个JsonpMediaTypeFormatter对象并添加当前注册的MediaTypeFormatter列表中。为了让它被优先选择,我们将这个JsonpMediaTypeFormatter对象放在此列表的最前端。 class HomeController : Controller
public ActionResult Index()
5: return View(); 6: }
7: }
如下所示的是Action方法Index对应View的定义。我们的目的在于:当页面成功加载之后以Ajax请求的形式调用上面定义的Web API获取联系人列表,并将自呈现在页面上。如下面的代码片断所示,我们直接调用$.ajax方法并将dataType参数设置为“jsonp”。 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 MVC中使用MySQL的AccountController
- asp.net-mvc – ASP.NET MVC泛型基础视图类
- MVC中TempData、ViewData和ViewBag的区别
- asp.net-mvc-3 – ASP.NET MVC视图模型不绑定在HTTP Post与
- asp.net-mvc – 如何使用需要js的Kendo UI MVC Extensions?
- asp.net-mvc-4 – 捆绑从CDN提供的多个CSS?
- asp.net-mvc – ASP.NET MVC读取原始JSON信息数据
- asp.net-mvc – 在显示模板中使用DisplayFor
- 代码翻译:ASP.NET Server.Transfer in PHP
- 成员资格 – 如何在ASP.NET中将用户与其各自的文件夹连接起
- ASP.NET MVC – 以编程方式添加动作过滤器
- asp.net – SQL Server 2005 – 您应该多久重建一
- 使用asp.net在互联网上流式传输音频和视频
- asp.net-mvc – 尝试确定在干净安装的窗口上托管
- asp.net-mvc – StructureMap / NHibernate Sess
- asp.net-mvc – 如何使用Asp.Net MVC框架创建Mas
- asp.net – 从MVC视图中的模型访问displayName属
- 使用实体框架在ASP.Net中创建报表
- 在ASP.NET MVC4中是否可以将C#或VB函数标记为Jav
- asp.net-mvc – MVC在会话中保存令牌