1. 程式人生 > >通過擴充套件讓ASP.NET Web API支援JSONP

通過擴充套件讓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:     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="[email protected]"},
   8:             new Contact{ Name="李四", PhoneNo="456", EmailAddress="[email protected]"},
   9:             new Contact{ Name="王五", PhoneNo="789", EmailAddress="[email protected]"},
  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物件填充到回撥返回中而生成的一個函式呼叫語句。

   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":"[email protected]"},{"Name":"李四","PhoneNo":"456","EmailAddress":"[email protected]"},{"Name":"王五","PhoneNo":"789","EmailAddress":[email protected]}])
http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-03.html

相關推薦

通過擴充套件ASP.NET Web API支援JSONP

同源策略(Same Origin Policy)的存在導致了“源”自A的指令碼只能操作“同源”頁面的DOM,“跨源”操作來源於B的頁面將會被拒絕。同源策略以及跨域資源共享在大部分情況下針對的是Ajax請求。同源策略主要限制了通過XMLHttpRequest實現的Ajax請求

通過擴展ASP.NET Web API支持JSONP

web api enc pan star close web應用 lba dia 不存在 同源策略(Same Origin Policy)的存在導致了“源”自A的腳本只能操作“同源”頁面的DOM,“跨源”操作來源於B的頁面將會被拒絕。同源策略以及跨域資源共享在大部分情況下針

Asp.net Web Api開發Help Page 新增對資料模型生成註釋的配置和擴充套件

        在使用webapi框架進行介面開發的時候,編寫文件會需要與介面同步更新,如果採用手動式的更新的話效率會非常低。webapi框架下提供了一種自動生成文件的help Page頁的功能。 但是原始版本的效果不是很好,最重要的一點是沒有對資料模型的詳細

支援Ajax跨域訪問ASP.NET Web Api 2(Cors)的示例

隨著深入使用ASP.NET Web Api,我們可能會在專案中考慮將前端的業務分得更細。比如前端專案使用Angularjs的框架來做UI,而資料則由另一個Web Api 的網站專案來支撐。注意,這裡是兩個Web網站專案了,前端專案主要負責介面的呈現和一些前端的相應業務邏輯處

Asp.net Web Api 解決跨域問題

asp oss ros ner div exec space out color using System; using System.Collections.Generic; using System.Linq; using System.Web; using Syst

[ASP.NET Web API]如何Host定義在獨立程序集中的Controller

eps 運行 icon tel conf clu XML dom tex 通過《 ASP.NET Web API的Controller是如何被創建的?》的介紹我們知道默認ASP.NET Web API在Self Host寄宿模式下用於解析程序集的AssembliesRes

How ASP.NET Web API 2.0 Works?[持續更新中…]

throws case rep 生命 indexof http face auto 攔截 一、概述 RESTful Web API [Web標準篇]RESTful Web API [設計篇] 在一個空ASP.NET Web項目上創建一個ASP.NET Web API 2.

[轉]ASP.NET web API 2 OData enhancements

{0} per yourself res demon services host iss ges 本文轉自:https://www.pluralsight.com/blog/tutorials/asp-net-web-api-2-odata-enhancements Al

探秘如何操作 ASP.NET Web API (三)

asp ajax請求 log pic margin div 判斷 out turn 經過我三篇文章的解惑,webapi我相信大家沒有問題了! 先創建了一個UserModel public class UserModel { public string UserI

(四)Asp.net web api中的坑-【api的返回值】

技術分享 要求 data 都是 blog pan odi handle 自己 void無返回值 IHttpActionResult HttpResponseMessage 自定義類型 我這裏並不想贅述這些返回類型, 可以參考博文http://blog.csdn.net/

Web API系列教程】1.1 — ASP.NET Web API入門

表示 return param 全部 products cap asp.net control toys 前言 HTTP不僅僅服務於web頁面。同一時候也是構建暴露服務和數據的API的強大平臺。HTTP有著簡單、靈活和無處不在的特點。你能想到的差點兒全

ASP.NET Web API技術開發HTTP接口(一)

ble 身份驗證 刪除 發現 bapi try prot 好用 get 開發工具 Visual Studio 2013 SQL Server 2008 R2 準備工作 啟動Visual Studio 2013,新建一個ASP.NET Web應用程序,命名為SimpleAPI

ASP.NET Web API 過濾器創建、執行過程(二)

reading mar model驗證 type() 方法的參數 public 所有 ring attr 前言 前面一篇中講解了過濾器執行之前的創建,通過實現IFilterProvider註冊到當前的HttpConfiguration裏的服務容器中,當然默認的基礎服務也是

asp.net Web API 身份驗證 不記名令牌驗證 Bearer Token Authentication 簡單實現

驗證 tca star ati manager ace .com return public 1. Startup.Auth.cs文件 添加屬性 1 public static OAuthBearerAuthenticati

Web API 2 入門——創建ASP.NET Web API的幫助頁面(谷歌翻譯)

鏈接 所有 action 解決方案 fec amp 開發人員 sharp ima 在這篇文章中 創建API幫助頁面 將幫助頁面添加到現有項目 添加API文檔 在敞篷下 下一步 作者:Mike Wasson 創建Web API時,創建幫助

Authentication and Authorization in ASP.NET Web API

module to server -h alter prop strong bar isa som ?You‘ve created a web API, but now you want to control access to it. In this series o

[轉]Enabling CRUD Operations in ASP.NET Web API 1

storage next have get ini called each tail span 本文轉自:https://docs.microsoft.com/en-us/aspnet/web-api/overview/older-versions/creating-a-w

Asp.Net Web API(二)

default exceptio 服務器 路由 回復 事情 http響應 images art 創建一個Web API項目 第一步,創建以下項目 當然,你也可以創建一個Web API項目,利用 Web API模板,Web API模板使用 ASP.Net MVC提供AP

asp.net web api 異常捕獲

resp int 反序列化 存儲 cut type easyn esp https 1 向客戶端發送錯誤消息 使用throw new HttpResponseException()向客戶端拋出錯誤信息。 HttpResponseException包含兩個重載的構造函數,其中

asp.net web api 下載之斷點續傳

contain tor 服務器 download sea 異常 http files remove 一、基本思想 利用 HTTP 請求的Range標頭值,來向服務端點傳遞請求數據的開始位置和結束位置。服務端獲得這兩個參數後,將指定範圍內的數據傳遞給客戶端。當客戶端請求暫停或