ASP.NET WebApi 內建跨網域支援(參考:進擊的 ASP.NET Web API 2 巨人 – 打造支援各種裝置及平台的服務 - MSDN 台灣部落格),但基於專案的特殊需求,最後我還是決定自己寫 CORS 支援。
程式在 IIS Express 測試正常,搬到 IIS 後部分呼叫正常,部分失效。經分析問題如下:
瀏覽器在發出跨網域請求時,若符合以下條件:(參考:MDN)
- It uses methods other than GET, HEAD or POST.
執行 GET/HEAD/POST 以外的方法 - Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
使用 POST 且使用 application/x-www-form-urlencoded, multipart/form-data, or text/plain 之外的 Content-Type,例如:以 POST 傳送 XML、JSON 等。 - It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
使用自訂 Header
瀏覽器會在正式 Request 發送前,先送出一個 OPTIONS 的行前檢查請求(Preflight Request)。例如以下範例,開啟 Chrome 連上 jQuery 官網,於 F12 開發工具主控台透過 jQuery.ajax 分別發出三個 Request 到本機 localhost(標準跨網域情境),第一個為 POST、第二個為 GET,第三個 POST Request 指定 ContentType="text/xml"[ 註:測試時誤寫成plain/xml(羞),寫到昏頭,但仍吻合條件,擷圖就容我偷懶不修正,順便當成彩蛋(謎:這樣也成?I 服了U) ]。前兩次測試都是單一 Request 完成,第三個測試則會先送出一個 OPTIONS 請求,接著才正式送出 POST。
同樣的測試搬到 IIS,重覆前述的第三個 Request,送出的 OPTIONS Preflight Request 會因 Server 端未正確傳回 Access-Control-Allow-Origin 而失敗!
探究其原因,在 IIS 失敗是因為 OPTIONS Request 被預設載入的 OPTIONSVerbHandler 攔截,直接回應,未傳回對應的 Access-Control-Allow-Origin 標頭,導致 CORS 呼叫失敗。
要校正問題,可透過 IIS 管理員介面或修改 web.config 將其移除:
<system.webServer>
<handlers>
<removename="OPTIONSVerbHandler"/>
<!-- 略 -->
</handlers>
</system.webServer>
測試結果便正常了!
註: 在 IIS8,該 Handler 名稱為 OPTIONS。參考