ASP.NET中Web API解決跨域問(wèn)題
一、什么是跨域問(wèn)題
跨域:指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制。(服務(wù)端可以正常接收瀏覽器發(fā)生的請(qǐng)求,也可以正常返回,但是由于瀏覽器的安全策略,瀏覽器不能處理服務(wù)端的返回)。
那么什么是同源策略呢?
同源策略/SOP(Same origin policy)是一種約定,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源是指"協(xié)議+域名+端口"三者相同,即便兩個(gè)不同的域名指向同一個(gè)ip地址,也非同源。
正是由于這個(gè)原因,如果是在不同的項(xiàng)目之間進(jìn)行調(diào)用(這里說(shuō)的調(diào)用指的是瀏覽器調(diào)用后端,如果是后端直接調(diào)用就不會(huì)存在跨域問(wèn)題)就會(huì)被瀏覽器阻止。WebApi中常見的場(chǎng)景:Web Api作為單獨(dú)的數(shù)據(jù)服務(wù)層,提供接口供前端界面調(diào)用,MVC項(xiàng)目作為顯示層,這種情況下如果在MVC的前端界面里面通過(guò)ajax調(diào)用WebApi的接口,就會(huì)存在跨域問(wèn)題。
二、如何解決跨域問(wèn)題
網(wǎng)上有很多跨域問(wèn)題的解決方案,這里就不在一一列舉了,下面主要講解一下在WebApi中如何使用CORS解決跨域問(wèn)題。CORS全稱Cross-Origin Resource Sharing,中文全稱是跨域資源共享。CORS解決跨域問(wèn)題的原理是在http的請(qǐng)求報(bào)文和響應(yīng)報(bào)文里面加入響應(yīng)的標(biāo)識(shí)告訴瀏覽器能夠訪問(wèn)哪些域名的請(qǐng)求。
三、使用代碼解決跨域問(wèn)題
下面結(jié)合一個(gè)具體的實(shí)例來(lái)講解在WebApi里面如何使用CORS解決跨域問(wèn)題。
1、場(chǎng)景描述
新建兩個(gè)單獨(dú)的項(xiàng)目:一個(gè)WebApi項(xiàng)目(帶有MVC功能,用來(lái)提供數(shù)據(jù)和頁(yè)面顯示),一個(gè)MVC項(xiàng)目(只是負(fù)責(zé)頁(yè)面顯示),項(xiàng)目結(jié)構(gòu)如下圖所示:
其中,WebApi項(xiàng)目的端口號(hào)是:33982,MVC項(xiàng)目的端口號(hào)是:34352(這兩個(gè)端口號(hào)是在本機(jī)的地址,在其他電腦上端口號(hào)可能不同)。顯而易見:兩個(gè)項(xiàng)目的端口號(hào)不同,不屬于同源,如果在MVC里面通過(guò)前端調(diào)用WebApi提供的數(shù)據(jù)接口,就會(huì)出現(xiàn)跨域的問(wèn)題。
2、項(xiàng)目結(jié)構(gòu)
2.1 WebApi項(xiàng)目結(jié)構(gòu)
新建WebApiController文件夾,用來(lái)存放WebApi控制器,并新建一個(gè)WebApi控制器,命名為Student。StudentController控制器的代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using WebApi.Entity; namespace WebApi.WebApiController { public class StudentController : ApiController { public static List<Student> studentList = InitStudentList(); private static List<Student> InitStudentList() { return new List<Student>() { new Student {StudentID =1,StudentName="唐僧",Age=24,Sex="男",Major="師范"}, new Student {StudentID =2,StudentName="孫悟空",Age=579,Sex="男",Major="管理"}, new Student {StudentID =3,StudentName="沙悟凈",Age=879,Sex="男",Major="水利工程"}, new Student {StudentID =4,StudentName="白骨精",Age=456,Sex="女",Major="表演"}, new Student {StudentID =5,StudentName="玉兔精",Age=456,Sex="女",Major="舞蹈"} }; } [HttpGet] public IHttpActionResult GetAllStudent() { return Json<List<Student>>(studentList); } } }
修改WebApi配置文件類,路由規(guī)則里面增加action,代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace WebApi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務(wù) // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
新建一個(gè)MVC控制器,命名為Student,并添加Index視圖,Index視圖代碼如下:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>測(cè)試跨域問(wèn)題</title> <script src="~/Scripts/jquery-1.10.2.min.js" type="text/javascript"></script> </head> <body> <div style="background-color:#008000;padding:10px;margin:5px;"> <div style="font-weight:bold;margin-bottom:5px;">Get Student List</div> <div style="padding-bottom:5px;"> <input id="btnGetStudentList" name="btnGetStudentList" type="button" value="Get Student List" /> </div> <div id="students"></div> </div> <script> $('#btnGetStudentList').click(function () { $.ajax({ url: '/api/Student/GetAllStudent', type: 'GET', dataType: 'json' }).success(function (result) { DisplayStudentList(result); }).error(function (data) { alert(data); }); }); function DisplayStudentList(result) { var studentTable = $("<table cellpadding='3' cellspacing='3'></table>"); var studentTableTitle = $("<tr><th>StudentID</th><th>StudentName</th><th>Age</th><th>Sex</th><th>Major</th></tr>"); studentTableTitle.appendTo(studentTable); for (var i = 0; i < result.length; i++) { var studentTableContent = $("<tr><td>" + result[i].StudentID + "</td><td>" + result[i].StudentName + "</td><td>" + result[i].Age + "</td><td>" + result[i].Sex + "</td><td>" + result[i].Major + "</td></tr>" ); studentTableContent.appendTo(studentTable); } $('#students').html(studentTable); } </script> </body> </html>
2.2 MVC項(xiàng)目結(jié)構(gòu)
MVC項(xiàng)目結(jié)構(gòu)比較簡(jiǎn)單,新建一個(gè)名為Student的控制器,并添加視圖,視圖如下:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>測(cè)試跨域問(wèn)題</title> <script src="~/Scripts/jquery-1.10.2.min.js" type="text/javascript"></script> </head> <body> <div style="background-color:#008000;padding:10px;margin:5px;"> <div style="font-weight:bold;margin-bottom:5px;">Get Student List</div> <div style="padding-bottom:5px;"> <input id="btnGetStudentList" name="btnGetStudentList" type="button" value="Get Student List" /> </div> <div id="students"></div> </div> <script> $('#btnGetStudentList').click(function () { $.ajax({ url: 'http://localhost:33982//api/Student/GetAllStudent', type: 'GET', dataType: 'json' }).success(function (result) { DisplayStudentList(result); }).error(function (data) { alert("失敗"); }); }); function DisplayStudentList(result) { var studentTable = $("<table cellpadding='3' cellspacing='3'></table>"); var studentTableTitle = $("<tr><th>StudentID</th><th>StudentName</th><th>Age</th><th>Sex</th><th>Major</th></tr>"); studentTableTitle.appendTo(studentTable); for (var i = 0; i < result.length; i++) { var studentTableContent = $("<tr><td>" + result[i].StudentID + "</td><td>" + result[i].StudentName + "</td><td>" + result[i].Age + "</td><td>" + result[i].Sex + "</td><td>" + result[i].Major + "</td></tr>" ); studentTableContent.appendTo(studentTable); } $('#students').html(studentTable); } </script> </body> </html>
四、測(cè)試
1、在不做任何處理情況下的測(cè)試
先看看同源下的訪問(wèn)情況,直接啟動(dòng)WebApi項(xiàng)目,截圖如下:
點(diǎn)擊按鈕,測(cè)試結(jié)果如下:
因?yàn)槭窃谕粋€(gè)域中,所以訪問(wèn)沒(méi)有問(wèn)題,前端可以正常獲取到WebApi返回的數(shù)據(jù)。下面在來(lái)看看在MVC項(xiàng)目中的測(cè)試情況。測(cè)試截圖如下:
從圖中可以看出訪問(wèn)失敗了,按F12查看訪問(wèn)情況:
從上面的截圖中可以看出,發(fā)生了跨域訪問(wèn),瀏覽器出于安全性的考慮,不能接收返回的數(shù)據(jù)。
五、使用CORS解決跨域問(wèn)題
1、安裝CORS
在WebApi項(xiàng)目上右鍵->管理NuGet程序包,然后搜索“microsoft.aspnet.webapi.cors”,選擇第一個(gè)進(jìn)行安裝
具體的安裝過(guò)程在這里不再詳細(xì)解釋。
2、配置跨域
在WebApiConfig.cs類中配置跨域,修改后的代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; using System.Web.Http.Cors; namespace WebApi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // 跨域配置 config.EnableCors(new EnableCorsAttribute("*","*","*")); // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
這里使用“*”號(hào)表示對(duì)所有的域名都可以跨域。再次從MVC里面訪問(wèn)WebApi,查看測(cè)試結(jié)果:
從上面的截圖中可以看出,這時(shí)就可以允許跨域訪問(wèn)了,在Response Headers里面添加了:Access-Control-Allow-Origin:*。
六、CORS參數(shù)詳解
在上面我們?cè)赪ebApi的配置文件中使用了:
config.EnableCors(new EnableCorsAttribute("*","*","*"));
這一句代碼解決了跨域問(wèn)題。但是這種“*”號(hào)是不安全的。查看MSDN,發(fā)現(xiàn)EnableCorsAttribute類有如下的構(gòu)造函數(shù):
public EnableCorsAttribute( string origins, string headers, string methods )
詳細(xì)的參數(shù)解釋請(qǐng)查看MSDN。
知道了EnableCorsAttribute類的構(gòu)造函數(shù)以后,我們可以使用下面的方法進(jìn)行改進(jìn)。
方法一:在Web.Config文件的appSettings節(jié)點(diǎn)里面配置參數(shù):
然后修改WebApiConfig.cs文件的Register方法:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; using System.Web.Http.Cors; using System.Configuration; namespace WebApi { public static class WebApiConfig { public static void Register(HttpConfiguration config) { #region 跨域配置 string allowedOrigin = ConfigurationManager.AppSettings["allowedOrigin"]; string allowedHeaders = ConfigurationManager.AppSettings["allowedHeaders"]; string allowedMethods = ConfigurationManager.AppSettings["allowedMethods"]; config.EnableCors(new EnableCorsAttribute(allowedOrigin, allowedHeaders, allowedMethods)); #endregion // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
方法二:如果只想對(duì)某些api或者api里面的某些方法做跨域,可以直接在API控制器類上面使用特性標(biāo)注或者在方法上面使用特性標(biāo)注。
允許Student控制器可以跨域:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using WebApi.Entity; using System.Web.Http.Cors; namespace WebApi.WebApiController { [EnableCors(origins:"http://localhost:34352",headers:"*",methods:"GET,POST,PUT,DELETE")] public class StudentController : ApiController { public static List<Student> studentList = InitStudentList(); private static List<Student> InitStudentList() { return new List<Student>() { new Student {StudentID =1,StudentName="唐僧",Age=24,Sex="男",Major="師范"}, new Student {StudentID =2,StudentName="孫悟空",Age=579,Sex="男",Major="管理"}, new Student {StudentID =3,StudentName="沙悟凈",Age=879,Sex="男",Major="水利工程"}, new Student {StudentID =4,StudentName="白骨精",Age=456,Sex="女",Major="表演"}, new Student {StudentID =5,StudentName="玉兔精",Age=456,Sex="女",Major="舞蹈"} }; } [HttpGet] public IHttpActionResult GetAllStudent() { return Json<List<Student>>(studentList); } } }
只允許Student控制器里面的GetAllStudent方法可以跨域:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using WebApi.Entity; using System.Web.Http.Cors; namespace WebApi.WebApiController { public class StudentController : ApiController { public static List<Student> studentList = InitStudentList(); private static List<Student> InitStudentList() { return new List<Student>() { new Student {StudentID =1,StudentName="唐僧",Age=24,Sex="男",Major="師范"}, new Student {StudentID =2,StudentName="孫悟空",Age=579,Sex="男",Major="管理"}, new Student {StudentID =3,StudentName="沙悟凈",Age=879,Sex="男",Major="水利工程"}, new Student {StudentID =4,StudentName="白骨精",Age=456,Sex="女",Major="表演"}, new Student {StudentID =5,StudentName="玉兔精",Age=456,Sex="女",Major="舞蹈"} }; } /// <summary> /// 允許跨域 /// </summary> /// <returns></returns> [EnableCors(origins: "http://localhost:34352", headers: "*", methods: "GET,POST,PUT,DELETE")] [HttpGet] public IHttpActionResult GetAllStudent() { return Json<List<Student>>(studentList); } /// <summary> /// 不允許跨域 /// </summary> [HttpPost] public void Post() { } } }
到此這篇關(guān)于ASP.NET中Web API解決跨域問(wèn)題的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- asp.net core webapi 服務(wù)端配置跨域的實(shí)例
- 談?wù)勅绾卧贏SP.NET Core中實(shí)現(xiàn)CORS跨域
- ASP.NET WebAPI2復(fù)雜請(qǐng)求跨域設(shè)置的方法介紹
- asp.net基于JWT的web api身份驗(yàn)證及跨域調(diào)用實(shí)踐
- ASP.NET MVC中設(shè)置跨域訪問(wèn)問(wèn)題
- ASP.net WebAPI跨域調(diào)用問(wèn)題的解決方法
- 支持Ajax跨域訪問(wèn)ASP.NET Web Api 2(Cors)的示例教程
- ASP.NET?CORE實(shí)現(xiàn)跨域
相關(guān)文章
Asp.Net實(shí)現(xiàn)404頁(yè)面與301重定向的方法
這篇文章主要介紹了Asp.Net實(shí)現(xiàn)404頁(yè)面與301重定向的方法,較為詳細(xì)的分析了404頁(yè)面的原理與針對(duì)404錯(cuò)誤與301跳轉(zhuǎn)的實(shí)現(xiàn)方法,是非常實(shí)用的技巧,需要的朋友可以參考下2014-11-11解析如何利用一個(gè)ASP.NET Core應(yīng)用來(lái)發(fā)布靜態(tài)文件
本文主要通過(guò)一些簡(jiǎn)單的實(shí)例來(lái)體驗(yàn)一下如何在一個(gè)ASP.NET Core應(yīng)用中發(fā)布靜態(tài)文件。針對(duì)不同格式的靜態(tài)文件請(qǐng)求的處理,ASP.NET Core為我們提供了三個(gè)中間件,它們將是本系列文章論述的重點(diǎn)。有需要的朋友可以看下2016-12-12為GridView的行添加鼠標(biāo)經(jīng)過(guò)、點(diǎn)擊事件的小例子
這篇文章介紹了GridView的行添加鼠標(biāo)經(jīng)過(guò)、點(diǎn)擊事件的小例子,有需要的朋友可以參考一下2013-11-11asp.net core 騰訊驗(yàn)證碼的接入示例代碼
這篇文章主要介紹了asp.net core 騰訊驗(yàn)證碼的接入示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10asp.net中日歷函數(shù)Calendar的使用方法
calendar 控件用于在瀏覽器中顯示日歷,該控件可顯示某個(gè)月的日歷,允許用戶選擇日期,也可以跳到前一個(gè)或下一個(gè)月2011-05-05asp.net treeview checkbox 相關(guān)問(wèn)題
asp.net treeview checkbox 相關(guān)問(wèn)題,需要的朋友可以看下。2009-06-06ASP.NET Core 數(shù)據(jù)保護(hù)(Data Protection 集群場(chǎng)景)下篇
這篇文章主要為大家再一次介紹了ASP.NET Core 數(shù)據(jù)保護(hù)(Data Protection),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09.NET?MAUI項(xiàng)目中創(chuàng)建超鏈接
這篇文章介紹了.NET?MAUI項(xiàng)目中創(chuàng)建超鏈接的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03