Select Page
如何解決 ASP.NET MVC WEB API 遇到 CORS 錯誤

如何解決 ASP.NET MVC WEB API 遇到 CORS 錯誤

要用 ASP.NET MVC 開發 WEB API給大家使用,前端 javascript 工程師,總是會遇到錯誤訊息 Response to preflight request doesn’t pass access control check: It does not have HTTP ok status,這是一個超常見的 CORS (Cross-Origin Resource Sharing) – 跨來源資源共用錯誤訊息,網路上很多解法,但可以用的情境都不太一樣,我這邊提供一個我覺得最簡單請快速的方法給大家。

解決方法是利用 NuGet 先安裝 Cors 套件

先上 NuGet 搜尋 Microsoft.AspNet.Cors , Microsoft.AspNet.WebApi.Cors,並且安裝起來

將 Cors 註冊起來

在 App_Start/WebApiConfig.cs 的 Register function 加入以下的程式碼

config.EnableCors();

會是長成以下的程式碼

        public static void Register(HttpConfiguration config)
        {
            // Web API 設定和服務
            config.EnableCors();

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

設定 WebAPI 的CORS 權限

我們可以在 WebApi 的 Class 上面設定,也可以在 Function 上做標記,但要注意的是不能有多個 CORS 繫結,如果你在 web.config 中設定,這邊就不能再做設定,如果你在 class 等級上設定了, function 等級就不能再設定,也是因為這個原因,所以我喜歡在 function 等級設定或是 class 等級上設定,web.config 比較少去做設定,雖然他有不用修改程式碼的好處。

其中設定值的資料參考請看https://docs.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference

  • origins,可以設定 (*) ,也可以指定特定網域,看你要對誰開放
  • headers,通常用 (*) 就可以了
  • methods,通常用 (*) 就可以了
    [System.Web.Http.Cors.EnableCors(origins: "https://yourdomain.com", headers: "*", methods: "*")]
    public class ValueController : ApiController
    {
    }

CORS web.config 的設定方法

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="*" />
            <add origin="https://*.yourdomain.com"
                 allowCredentials="true"
                 maxAge="120"> 
                <allowHeaders allowAllRequestedHeaders="true">
                    <add header="header1" />
                    <add header="header2" />
                </allowHeaders>
                <allowMethods>
                     <add method="DELETE" />
                </allowMethods>
                <exposeHeaders>
                    <add header="header1" />
                    <add header="header2" />
                </exposeHeaders>
            </add>
            <add origin="http://*" allowed="false" />
        </cors>
    </system.webServer>
</configuration>

C# HttpClient 與 PHP 開發的 Web API 連接

常常要接WEB API的人都需要在許多種語言中穿梭,其中最麻煩的就是加密的函式(Function)了,不同語言即使加密演算法都一樣,但在呈現的時候常常都是不一樣的,也會有些微的小地方要修正,但就是這些地方在捆擾著大家,浪費時間在處理顯示問題,所以今天的主題放在C#的MD5()要如何接上PHP的MD5()

一言不合就先上C#的程式碼,這一段程式碼式可以複製貼上後就可以呼叫的,你只要呼叫他就回你PHP格式的MD5字串,通常就在把她回填到WEB API的加密區域就可以用了

using System.Security.Cryptography;

	public string PHPMD5(string text)
	     {
            var md5 = new MD5CryptoServiceProvider();
            byte[] bytesText = Encoding.UTF8.GetBytes(text.ToLower());
            byte[] bytesMD5Text = md5.ComputeHash(bytesText);
            StringBuilder sb = new StringBuilder();
            foreach (var b in bytesMD5Text)
            {
                sb.Append(b.ToString("x2").ToLower());
            }
            return sb.ToString();
        }

處理完MD5顯示不相容的問題後,我們來處理 http request header 的問題,現在 C# 中比較建議用 httpclient 來替代以前的WebClient,替代後最常見的問題就是要把寫法改成非同步想法 Async,以及 HttpClient 功能相對 WebClient 要來的低接一些,不太熟悉底層的程序員會找不到方法使用,我先來介紹一下,如果你要在 HttpClient 中加入 http request header 的方法,程式碼如下

		using (HttpClient client = new HttpClient())
            {
                try
                {
                    client.DefaultRequestHeaders.Add("account", account);                   
                    client.DefaultRequestHeaders.Add("sign", sign);
                    client.DefaultRequestHeaders.Add("timestamp", timestamp);
                    client.Timeout = TimeSpan.FromSeconds(30);
                    HttpResponseMessage response = await client.GetAsync(url);
                    string responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(responseBody);
                }
                catch (HttpRequestException ex)
                {
                    Console.WriteLine("Message :{0} ", ex.Message);
                }
            }

這種寫法比較能夠勾起老人的回憶,比較像是webclient的邏輯,也比較直覺些