一般來說,WebAPI只提供給自己網站使用的話是不用作任何調整的
但是WebAPI要開放給其他網站進行呼叫與使用,就必須進行跨網域的設定
網路上有很多關於跨網域的呼叫與使用,像是設定Access-Control-Allow-Origin,或是將client端的呼叫方式更改為JSONP等等的
若是使用的是.NET WebAPI的開發方式,很快速就可以完成跨網域存取的WebAPI,甚至連Client端呼叫的程式碼都不用去修改
要讓.NET的WebAPI啟用跨網域存取很簡單,照著下面的步驟就可以完成了
1.在WebAPI的專案中先增加一個[CorsController]的控制器
2.在專案中加入[Microsoft.AspNet.WebApi.Cors]的Nuget套件
3.安裝完成後,打開專案中的[App_Start\WebApiConfig.cs]檔案,並在裡面加上一行"config.EnableCors();"
4.最後,在剛剛增加的CorsController控制器中,加上下面的程式碼,就可以讓控制器中的Action提供指定來源網域的請求
[EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
其中,在EnableCors的Attribute中,origins代表允許存取的來源網域,headers代表允許的標頭,而method則是代表允許使用的方法,如Get或是Post等等,這幾個屬性的設定,若是更改為*,就代表允許所有的來源以及所有請求方式
要完成允許跨網域的WebAPI,這樣就可以達到我們要的需求了,不過如果是要更進一步,動態設定允許的來源網域該如何處理?
要作到動態網域存取,可以有下面兩種方式,不過這兩種方式的實作前提,都是在已經完成了[Microsoft.AspNet.WebApi.Cors]的Nuget套件安裝以及上述步驟的設定下進行實作的
實作一、透過[ICorsPolicyProvider]完成動態設定
透過ICorsPolicyProvider的方式,作到動態設定方式,必須在專案中增加一個AOP的物件,首先先在專案中加入一個[CorsHandle.cs]的類別庫並將下面的程式碼放入至這個類別庫之中
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Cors;
using System.Web.Http.Cors;
public class CorsHandle : Attribute, ICorsPolicyProvider
{
private CorsPolicy objProlicy;
public CorsHandle()
{
// 建立一個跨網域存取的原則物件
objProlicy = new CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true
};
// 在這裡透過資料庫或是設定的方式,可動態加入允許存取的來源網域清單
objProlicy.Origins.Add("http://myclient.azurewebsites.net");
objProlicy.Origins.Add("http://www.facebook.com");
}
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(objProlicy);
}
}
在這個類別庫中,主要是繼承了Attribute以及ICorsPolicyProvider的介面,並實作在WebAPI執行時的跨網域驗證處裡,當然,在程式碼標註的位置中,可以動態的加入允許存取的網域名稱清單
接著回到CorsController的控制器中,將原本[EnableCors]的Attribute,更改成[CorsHandle],就可以在這個Action中套用網域的驗證動作了
實作二、透過自訂ActionFilter的AOP物件實作網域控制的驗證
在這個方式中,就比較偏向不採用ICorsPolicyProvider的方式來處理,而是在自訂的ActionFilter物件中進行過濾與驗證,同樣的,我們在專案中新增一個CorsOnActionHandle.cs的類別庫
將下面的程式碼,加入至類別庫中
using System.Net;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
public class CorsOnActionHandle : ActionFilterAttribute
{
/// <summary>
/// 進行專案使用時的授權驗證
/// </summary>
/// <param name="actionContext"></param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
// 設定允許的網域清單
List<string> strAllowDomain = new List<string>()
{
"http://myclient.azurewebsites.net",
"http://www.facebook.com"
};
// 取出來自呼叫端的網域
string strOrigin = actionContext.Request.Headers.GetValues("Origin").FirstOrDefault();
// 確認呼叫端的網域是否存在於允許的清單中
bool blCheckDomain = strAllowDomain.Contains(strOrigin);
// 如果不存在允許的網域清單,就回傳自訂的錯誤訊息
if (!blCheckDomain)
{
UnauthorizedObject result = new UnauthorizedObject()
{
code = "401",
message = "domain is not allow"
};
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, result);
}
}
public class UnauthorizedObject
{
public string code { get; set; }
public string message { get; set; }
}
}
在上面的程式中可以很清楚的看得出來,透過了一般的ActionFilterAttribute在OnActionExecuting的覆寫處理上,當WebAPI一被呼叫時就把呼叫端的來源網域取出,並與已經設定好的網域清單作比對,若是來源網域不在允許的清單中,就透過CreateResponse的方法回傳指定的HttpStatusCode以及Response的內容。這樣的作法雖然較為繁瑣,但是可以提供的訊息內容以及使用的靈活性也比較高一些
最後,在要使用這個ActionFilter的Action上,加入相對應的Attribute在這裡別忘了把[EnableCors]的Attribute加上去,因為不是實作ICorsPolicyProvider,所以還是必須讓這個Action允許所有網域的呼叫,再透過ActionFilter去進行來源網域的過濾