2015年6月3日 星期三

【往F2E這個路子走】jQuery - .ajax 方法遇到 CORS (cross origin request sharing) 問題

今天看書想說學著人家書上用jQuery來呼叫遠端Service,
又正好一直想學 WebAPI 以後好找工作,
所以就決定要來嘗試用jQuery連接遠端 WebAPI取回資料,
也心想早在多年前我玩 PhoneGap 也用過 jQuery的ajax取service資料,
應該沒啥難度吧!!

霸特, 永遠在心想說很簡單時就會總有這個霸特, 我遇到了我解決不了的問題啊啊啊啊啊!!!!!

好的,
讓我娓娓道來這個問題發生的情境吧!!

首先
我很簡單的寫了一個html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
      $(document).ready(function(){
        $('a').click(function(event){
          event.preventDefault();
          $.ajax({
            url:'http://localhost:58501/api/Values',
            success:function(data){alert('success');},
            error:function(xhr){alert('error');}
          });
        });
      });
    </script>
  </head>
  <body>
    <div>
      <h3>CORS TESTING</h3>
      <a href="#">Click Me!!</a>
    </div> 
  </body>
</html>
P.S. 我的Web API 就是當你在建立Web API專案時最原始的那個Values API, 然後在本機執行時我的port是58501。

就當我點下 Click Me連結覺得說就會很自然的像是每個人每天都需要喝水一樣的自然會跳出訊息框跟我說 : success
但是我卻得到了個 error !!!!

後來我用FireBug去查了查揪竟是怎麼個一回事兒啊
得到了下圖:
李組長他是眉頭一皺發現案情不太單純
我明明就是在本地端localhost 而且我腿很短怎麼可能會有跨網域問題勒?


後來股溝了一下才知道
原來不同的port就算是跨網域了啦
HTML網頁放在原生的localhost (C:/inetpub/wwwroot/)底下
Web API則是透過Visual Studio的虛擬伺服器幫我在localhost:58501底下執行
所以就算是跨網域存取嚕....

然而了解目前的這個摸們的狀況後
剩下要解決的就是怎樣可以讓我的網頁跨網域存取啊?
基本上來說這個所謂CORS狀況是出現在javascript才會有的
因為從前從前有一批壞蛋喜歡用跨網域攻擊別人
所以javascript就開始不能存取跨網域的資源啦
那可好了
我們通常要用ajax就是想說可以背後偷偷來跟人家說一些五四三的
如果不能跨網域存取那不就自廢武功了啊啊啊啊啊啊啊

因此在一番妥協之後
最後演變成如果你允許哪個網站存取你的資源
那就自己設定開放囉
重點就是你要在header裡面註明要開放給誰, 也就是設定'Access-Control-Allow-Origin'的內容 (允許哪個來源網站存取我的資源)
也就是上圖中他跟你說我的網頁沒檔頭這件事....(咦?)

好吧
其實這問題出在說Web API那端沒有給我權限去存取它的資源
也就是說我必須去修改Web API就是了

所以我就去找了找網路文章看要怎麼樣去修訂我的Web API讓他可以允許我跨網域存取
原來也就很簡單
只要去NuGet下載相關dll library之後進行引用
然後在你要開啟的Web API方法上面加註屬性即可唷!!
步驟如下:

(1). 先去NuGet Search "CORS", 然後Install "Microsoft ASP.NET Web API 2.2 Cross-Origin Support"這個套件

(2). 安裝好之後其實你就可以看到專案中的References已經加入了System.Web.Http.Cors這個命名空間的dll囉 !!

















(3). 焦點跳到WebApiConfig.cs上面, 在Register方法中加入 config.EnableCors(); 讓Web API 可以允許設定Cross Origin Request Sharing功能


(4). 最後回到我們要提供開放跨網域存取的Web API方法 (本例中就是去ValuesController.cs檔案)上面去加註此方法開放跨網域存取唷!! 另外記得要先引入System.Web.Http.Cors命名空間唷!!!

(5). 回頭我們再測一下網頁吧~perfect !!!! :D

以上就是簡單介紹一下在jQuery要用ajax方法去呼叫遠端Web API遇到CORS的解決方式,
不過也看出來其實問題都不是呼叫端,
都還是要去改伺服器端才行啊~

此外上面去設置[EnableCors]的屬性中可以置入三個參數,
分別是用於設置CORS的作用範圍
依序為 :
(1). Action
(2). Controller
(3). Global
影響層面應該是由內而外這樣
大家可以自行參考一下下面的Reference囉~

以上, 謝謝收看嚕~

【Reference】
(1). http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api
(2). http://blogs.msdn.com/b/msdntaiwan/archive/2013/09/10/asp-net-web-api-2.aspx



後續補充】
後來陸續又看了一些影片介紹Web API的時候
發現其實早在很久很久很久很久以前人家就講解過這個議題了
所以其實我算是後知後覺啦
但這其實不是重點
重點是他有另一個設定方式
不是在Controller裡的Action上面加Attribute唷
而是寫在Web.Config檔案裡面
但是但是
這樣的做法是屬於全域性的修改作法喔
大家也還是要小心注意這樣的唷
設定範例如下:
<system.webserver>
    <httpprotocol>
        <customheaders>
            <add name="Access-Control-Allow-Origin" value="*" />
            ......
        </customheaders>
    </httpprotocol>
</system.webserver>


簡單的說也就是全域的WebAPI都加上Access-Control-Allow-Origin=*這個檔頭就是嚕

【Reference】
 http://www.microsoftvirtualacademy.com/training-courses/developing-web-services-using-aspnet-webapi2

沒有留言: