本章將和大家分享ASP.NET Core MVC的一些基礎知識,包括Action接收參數、Action向視圖傳值、常用過濾器的使用、布局頁、分部視圖、視圖組件ViewComponent的使用以及在視圖中如何導入公共命名空間等,希望通過本文的分享能夠對初學者有所幫助。下面我們直接進入主題。
首先來看一下我們的解決方案:
本Demo的Web項目為ASP.NET Core Web 應用程序(目標框架為.NET Core 3.1) MVC項目。
1、Action接收參數及Action向視圖傳值
下面直接通過代碼加注釋的方式來講解:
Home控制器:
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; using MvcDemo.Models; using MvcDemo.Attributes; namespace MvcDemo.Controllers { public class HomeController : BaseController { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } //Action接收參數方式1 //id 指的是路由中的占位符{id},如果有值則id就是那個值 //myId是URL中的值或者表單中name=myId的那個值 public async Task<IActionResult> Index(int? id, int? myId) { Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}開始執行 => This is Home/Index Action"); await Task.Delay(1000); Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}執行結束 => This is Home/Index Action"); //Action接收參數方式2 //var value1 = Request.Query["myId"]; //var value2 = Request.Form["myId"]; //var value3 = Request.Form["name"].ToArray(); //接收多個值 //var value4 = RouteData.Values["id"]; //獲取路由占位符的值 var listStu = new List<Student>(){ new Student(){ Name = "張三", Sex = "男", Age = 16 }, new Student(){ Name = "李四", Sex = "男", Age = 17 }, new Student(){ Name = "小美", Sex = "女", Age = 18 } }; //Action向View傳值的4種方式 //ViewData["鍵"] = 值; 此方式View在接收Action傳過去的值時 //1、如果是字符串或者是數值型則可以直接使用,否則需要轉換成對應的類型再進行處理 ViewData["Stu"] = listStu[0]; //2、ViewBag 動態類型,ViewBag底層用的是ViewData,所以這2個是相通的,設置其中一個值后另外一個也就有值了 ViewBag.Title = "Home Index Page"; ViewBag.ListStu = listStu; //3、TempData["鍵"] = 值; TempData["Love"] = 520; //4、View(obj); View接收的就是Model,可以指定Model的數據類型,這樣子就可以直接使用 return View(listStu[2]); } //Action接收參數方式3 //使用實體接收post提交的參數 public IActionResult MyLayoutDemo(Student stu) { return View(); } /// <summary> /// 分部視圖Demo /// 分部視圖微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/partial?view=aspnetcore-5.0 /// </summary> /// <returns></returns> public IActionResult MyPartialDemo() { ViewBag.Stu = new Student() { Name = "王五", Sex = "男", Age = 22 }; ViewBag.FatherViewVal = "我是來自父視圖ViewData里面的值"; return View(); } #region Ajax交互 //Action接收參數方式4 //參數列表IFormCollection對象,這種方式只能接收到表單提交的參數,該方式不常用 public async Task<JsonResult> GetListDataAsync(IFormCollection form) { var myId = form["myId"].ToString(); await Task.Delay(500); var result = new { code = 1, total = 10, data = new Student() { Id = myId, Name = "錢七", Sex = "男", Age = 22 }, msg = "獲取成功" }; return Json(result); } #endregion Ajax交互 } }
對應的Home/Index視圖:
@{ Layout = null; var value = "15345678910"; if (value.IsMobile()) { Console.WriteLine("This is Home/Index View"); } } @* 給Model指定數據類型 *@ @model Student @{ Student stu = ViewData["Stu"] as Student; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@ViewData["Title"]</title> </head> <body> <div> @foreach (var item in ViewBag.ListStu) { @:我是foreach <br /> @Html.Raw("我是Html.Raw<br />") <text> 我是foreach內部的text </text> <p> 姓名:@(item.Name),性別:@(item.Sex),年齡:@(item.Age) </p> <p> 我是foreach內部p標簽內的字符串 </p> <hr /> } <p> 我是TempData["Love"]:@TempData["Love"] </p> <p> 我是ViewData["Stu"]:@((ViewData["Stu"] as Student).Name) </p> <p> 我是Model.Name:@Model.Name </p> <p> 我是ViewBag.ListStu[0].Name:@ViewBag.ListStu[0].Name </p> <p> 我是stu.Name:@(stu.Name) </p> </div> </body> </html>
訪問 /Home/Index 運行結果如下:
此外Action向視圖傳值還有一種特殊的情況就是匿名類型,具體的傳值方式可參考博文:https://www.cnblogs.com/xyh9039/p/11348684.html
2、 在視圖中如何導入公共命名空間
MVC框架給我們提供了一個特殊的視圖,叫 _ViewImports.cshtml,可以在該視圖當中導入公共命名空間。
它的作用域是_ViewImports.cshtml視圖所在的Views文件夾下的所有視圖。
另外我們還可以看到一個特殊的視圖,叫_ViewStart.cshtml,如下圖:
其作用就是在所有的View在呈現之前都會先執行_ViewStart.cshtml里面的代碼,但是有一種特殊情況就是如果一個View是按照分部視圖方式輸出的,則不會觸發_ViewStart.cshtml里面的代碼。
3、 分部視圖
首先來看下目錄結構:
其中Action如下所示:
/// <summary> /// 分部視圖Demo /// 分部視圖微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/partial?view=aspnetcore-5.0 /// </summary> /// <returns></returns> public IActionResult MyPartialDemo() { ViewBag.Stu = new Student() { Name = "王五", Sex = "男", Age = 22 }; ViewBag.FatherViewVal = "我是來自父視圖ViewData里面的值"; return View(); }
對應的視圖如下:
其中_MyPartial.cshtml視圖:
@model Student <h2>我是_MyPartial.cshtml</h2> <p style="color:red;"> @(Model.Name),@(Model.Sex),@(Model.Age) <br /> @ViewBag.Name <br /> @ViewData["Name"] <br /> @ViewBag.FatherViewVal </p>
其中MyPartialDemo.cshtml視圖:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>MyPartialDemo</title> </head> <body> <div> <p> @* 分部視圖 *@ 我是Html.PartialAsync: @await Html.PartialAsync("_MyPartial", ViewBag.Stu as Student, new ViewDataDictionary(this.ViewData) { { "Name", "老李" } }) </p> <hr /> <p> @* 分部視圖 *@ @* 調用無返回值的方法時必須放在花括號內部 *@ 我是Html.RenderPartialAsync: @{await Html.RenderPartialAsync("_MyPartial", ViewBag.Stu as Student, new ViewDataDictionary(this.ViewData) { { "Name", "老王" } });} </p> <hr /> <p> @* 視圖組件 *@ 我是Component.InvokeAsync: @await Component.InvokeAsync("StuInfo", new { id = "10086" }) </p> </div> </body> </html>
訪問 /Home/MyPartialDemo 運行結果如下:
分部視圖微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/partial?view=aspnetcore-5.0
分部視圖的視圖搜索路徑:
//按名稱(無文件擴展名)引用分部視圖,則按所述順序搜索以下位置 //1、/Areas/<Area-Name>/Views/<Controller-Name> //2、/Areas/<Area-Name>/Views/Shared //3、/Views/Shared //4、/Pages/Shared @await Html.PartialAsync("_PartialName") //存在文件擴展名時 //該視圖必須與調用分部視圖的文件位於同一文件夾中 @await Html.PartialAsync("_PartialName.cshtml") //從應用程序根目錄引用分部視圖 //以波形符斜杠 (~/) 或斜杠 (/) 開頭的路徑指代應用程序根目錄 @await Html.PartialAsync("~/Views/Folder/_PartialName.cshtml") @await Html.PartialAsync("/Views/Folder/_PartialName.cshtml") //引用使用相對路徑的分部視圖 @await Html.PartialAsync("../Account/_LoginPartial.cshtml")
4、視圖組件
可以看到上一步在講解分部視圖的時候里面就有調用了一個視圖組件
//調用視圖組件 @await Component.InvokeAsync("StuInfo", new { id = "10086" })
接下來我們就來看下如何實現,同樣我們先來看下目錄結構:
下面我們直接來看代碼:
視圖組件類StuInfoViewComponent.cs,名稱以ViewComponent后綴結尾:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using MvcDemo.Models; namespace MvcDemo.ViewComponents { /// <summary> /// 學生信息視圖組件 /// 視圖組件微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/view-components?view=aspnetcore-5.0 /// </summary> [ViewComponent(Name = "StuInfo")] //[ViewComponent] 屬性可以更改用於引用視圖組件的名稱。 public class StuInfoViewComponent : ViewComponent { private readonly ILogger<StuInfoViewComponent> _logger; public StuInfoViewComponent(ILogger<StuInfoViewComponent> logger) { _logger = logger; } public async Task<IViewComponentResult> InvokeAsync(string id) { var stu = new Student { Id = id, Name = "隔壁老王", Sex = "男", Age = 32 }; #region 摘自官網 /* 視圖搜索路徑 運行時在以下路徑中搜索視圖: /Views/{Controller Name}/Components/{View Component Name}/{View Name} /Views/Shared/Components/{View Component Name}/{View Name} /Pages/Shared/Components/{View Component Name}/{View Name} */ #endregion 摘自官網 return View(stu); } } }
Default.cshtml視圖:
@* 視圖組件 *@ @model Student <h2>我是StuInfoViewComponent</h2> <div> StuId=@(Model.Id),StuName=@(Model.Name),StuAge=@(Model.Age),StuSex=@(Model.Sex) </div>
調用視圖組件:
//調用視圖組件 @await Component.InvokeAsync("StuInfo", new { id = "10086" })
視圖組件微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/view-components?view=aspnetcore-5.0
其中值得注意的是:
1、[ViewComponent] 屬性可以更改用於引用視圖組件的名稱。
2、視圖組件的默認視圖名稱為“Default”,這意味着視圖文件通常命名為“Default.cshtml”。 可以在創建視圖組件結果或調用 View 方法時指定不同的視圖名稱。
3、官方建議將視圖文件命名為 Default.cshtml 並使用 Views/Shared/Components/{View Component Name}/{View Name} 路徑。
4、視圖搜索路徑
/* 視圖搜索路徑 運行時在以下路徑中搜索視圖: /Views/{Controller Name}/Components/{View Component Name}/{View Name} /Views/Shared/Components/{View Component Name}/{View Name} /Pages/Shared/Components/{View Component Name}/{View Name} */
5、布局頁Layout
先來看下目錄結構:
下面我們直接上代碼:
_Header.cshtml視圖:
<script src="~/js/jquery-3.6.0.min.js"></script>
_MyLayout.cshtml自定義布局頁:
@{ //指定布局頁 //默認使用 /Views/Shared/ 目錄下的_Layout布局頁 Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@(ViewBag.Title)</title> @* 加載分部視圖 *@ @await Html.PartialAsync("_Header") @* 占位符寫法1 *@ @RenderSection("header", required: false) @* 占位符寫法2 *@ @await RenderSectionAsync("header2", required: false) </head> <body> <h1>我是_MyLayout.cshtml</h1> <div> @* 將來內容將填充到這里 *@ @RenderBody() </div> @RenderSection("footer", required: false) @await RenderSectionAsync("footer2", required: false) </body> </html>
為視圖MyLayoutDemo.cshtml指定布局頁:
@{ ViewBag.Title = "MyLayoutDemo"; //指定布局頁 //此發現過程與用於發現分部視圖的過程相同 //Layout = "~/Views/Shared/_MyLayout.cshtml"; //指定布局頁 Layout = "_MyLayout"; //指定布局頁 } @* 填充布局頁里面對應的占位符 *@ @section header{ <script type="text/javascript"> </script> } <h2>MyLayoutDemo</h2> @* 填充布局頁里面對應的占位符 *@ @section footer2{ <script type="text/javascript"> </script> }
訪問 /Home/MyLayoutDemo 運行結果如下:
布局頁微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/layout?view=aspnetcore-5.0
其中值得注意的是:
指定的布局可以使用完整路徑(例如 /Pages/Shared/_Layout.cshtml 或 /Views/Shared/_Layout.cshtml)或部分名稱(示例:_Layout)。
如果提供了部分名稱, Razor 視圖引擎將使用其標准發現進程搜索布局文件。 首先搜索處理程序方法(或控制器)所在的文件夾,然后搜索 Shared 文件夾。
此發現過程與用於發現分部視圖的過程相同。
6、常用過濾器的使用
老樣子,我們首先先來看下相應的目錄機構:
下面我們重點來看下基類和過濾器:
基類BaseController如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using MvcDemo.Attributes; namespace MvcDemo.Controllers { /// <summary> /// 自定義控制器的基類 /// </summary> public class BaseController : Controller { /// <summary> /// 是否需要登入驗證 /// </summary> protected bool IsNeedLogin = true; /// <summary> /// 在Action執行前觸發(用於讓子類重寫) /// </summary> /// <param name="context">Action執行前上下文對象</param> protected virtual void OnSubActionExecuting(ActionExecutingContext context) { } /// <summary> /// 在Action執行前觸發(如果繼承該類的子類也重寫了該方法,則先執行子類的方法,再執行父類的方法) /// </summary> /// <param name="context">Action執行前上下文對象</param> public override void OnActionExecuting(ActionExecutingContext context) { base.OnActionExecuting(context); OnSubActionExecuting(context); //先執行子類的 //獲取請求進來的控制器與Action var controllerActionDescriptor = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor; #region 【權限驗證】【登入驗證】 //獲取區域名稱 var areaName = context.ActionDescriptor.RouteValues["area"] ?? ""; areaName = (string)context.RouteData.Values["area"]; //獲取控制器名稱 var controllerName = context.ActionDescriptor.RouteValues["controller"]; controllerName = controllerActionDescriptor.ControllerName; controllerName = context.RouteData.Values["controller"].ToString(); //獲取action名稱 var actionName = context.ActionDescriptor.RouteValues["action"]; actionName = controllerActionDescriptor.ActionName; actionName = context.RouteData.Values["action"].ToString(); //獲取當前的請求方式:Get或者Post string requestMethod = context.HttpContext.Request.Method.ToLower(); //獲取路由占位符對應的值 object currId = context.RouteData.Values["id"]; //獲取當前請求URL參數 var value = context.HttpContext.Request.Query.ContainsKey("key") ? context.HttpContext.Request.Query["key"].ToString() : ""; //獲取Action參數值 IDictionary<string, object> actionArguments = context.ActionArguments; if (actionArguments != null) { foreach (KeyValuePair<string, object> item in actionArguments) { var key = item.Key; //參數名 var val = item.Value; //參數值 } } ParamsFilter(context); //參數過濾 //判斷當前所請求的控制器上是否有打上指定的特性標簽 if (controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(SkipLoginValidateAttribute), false)) { //有的話就不進行相關操作【例如:不進行登入驗證】 //return; } //獲取當前所請求的Action上指定的特性標簽 object[] arrObj1 = controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(SkipLoginValidateAttribute), false); //獲取當前所請求的Action上所有的特性標簽 object[] arrObj2 = controllerActionDescriptor.MethodInfo.GetCustomAttributes(false); //判斷當前所請求的Action上是否有打上指定的特性標簽 if (controllerActionDescriptor.MethodInfo.IsDefined(typeof(SkipLoginValidateAttribute), false) || !IsNeedLogin) { //有的話就不進行相關操作【例如:不進行登入驗證】 return; } #endregion 【權限驗證】【登入驗證】 //【權限驗證】【登入驗證】邏輯 //To Do Something } #region 參數過濾 /// <summary> /// 參數過濾 /// </summary> void ParamsFilter(ActionExecutingContext context) { //獲取參數集合 var parameters = context.ActionDescriptor.Parameters; //遍歷參數集合 foreach (var param in parameters) { if (!context.ActionArguments.ContainsKey(param.Name)) { continue; } if (context.ActionArguments[param.Name] == null) { continue; } //當參數是字符串 if (param.ParameterType.Equals(typeof(string))) { //業務處理 var value = context.ActionArguments[param.Name].ToString(); context.ActionArguments[param.Name] = value.Replace(" ", ""); } else if (param.ParameterType.Equals(typeof(string[]))) //如果是字符串數組則遍歷每一個成員 { var arrStrs = context.ActionArguments[param.Name] as string[]; if (arrStrs != null && arrStrs.Length > 0) { for (int i = 0; i < arrStrs.Length; i++) { arrStrs[i] = arrStrs[i].Replace(" ", ""); //業務處理 } context.ActionArguments[param.Name] = arrStrs; } } else if (param.ParameterType.IsClass) //當參數是一個實體 { ModelParamsFilter(param.ParameterType, context.ActionArguments[param.Name]); } } } /// <summary> /// 實體參數過濾 /// </summary> object ModelParamsFilter(Type type, object obj) { if (obj == null) { return obj; } var properties = type.GetProperties(); foreach (var item in properties) { if (item.GetValue(obj) == null) { continue; } var attribute = typeof(SkipLoginValidateAttribute); if (item.IsDefined(attribute, false)) //特殊參數處理 { continue; } //當參數是字符串 if (item.PropertyType.Equals(typeof(string))) { //業務處理 string value = item.GetValue(obj).ToString(); item.SetValue(obj, value.Replace(" ", "")); } else if (item.PropertyType.Equals(typeof(string[]))) //如果是字符串數組則遍歷每一個成員 { var arrStrs = item.GetValue(obj) as string[]; if (arrStrs != null && arrStrs.Length > 0) { for (int i = 0; i < arrStrs.Length; i++) { arrStrs[i] = arrStrs[i].Replace(" ", ""); //業務處理 } item.SetValue(obj, arrStrs); } } else if (item.PropertyType.IsClass) //當參數是一個實體 { item.SetValue(obj, ModelParamsFilter(item.PropertyType, item.GetValue(obj))); } } return obj; } #endregion 參數過濾 /// <summary> /// 在Action執行后觸發(如果繼承該類的子類也重寫了該方法,則先執行子類的方法,再執行父類的方法) /// </summary> /// <param name="context">Action執行后上下文對象</param> public override void OnActionExecuted(ActionExecutedContext context) { base.OnActionExecuted(context); } /// <summary> /// 在Action執行前觸發 /// 注意:OnActionExecuting和OnActionExecuted是同步方法,而OnActionExecutionAsync是異步方法,同步和異步是不能同時都執行的,如果都寫上了那只會執行異步的方法。 /// 官網相關資料:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { //await base.OnActionExecutionAsync(context, next); #region 摘自官網 /* // Do something before the action executes. // next() calls the action method. var resultContext = await next(); // Do something after the action executes. */ #endregion 摘自官網 //Action執行之前的業務處理 Console.WriteLine("======================================================================================"); Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}開始執行 => This is BaseController/OnActionExecutionAsync"); Stopwatch sw = new Stopwatch(); sw.Start(); ActionExecutedContext resultContext = await next(); //執行Action sw.Stop(); //Action執行之后的業務處理 Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}執行結束 => This is BaseController/OnActionExecutionAsync 處理花費時間:{sw.ElapsedMilliseconds}ms"); } } }
自定義Action過濾器如下所示:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; using MvcDemo.Attributes; namespace MvcDemo.Filters { /// <summary> /// 自定義Action過濾器(也可通過BaseController實現過濾器功能) /// </summary> public class MyActionFilterAttribute : ActionFilterAttribute { /// <summary> /// 在Action執行前觸發(如果在控制器中也重寫了該方法,則先執行控制器中的方法,再執行過濾器中的方法) /// </summary> /// <param name="filterContext">Action執行前上下文對象</param> public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); //獲取請求進來的控制器與Action var controllerActionDescriptor = filterContext.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor; #region 【權限驗證】【登入驗證】 //獲取區域名稱 var areaName = filterContext.ActionDescriptor.RouteValues["area"] ?? ""; areaName = (string)filterContext.RouteData.Values["area"]; //獲取控制器名稱 var controllerName = filterContext.ActionDescriptor.RouteValues["controller"]; controllerName = controllerActionDescriptor.ControllerName; controllerName = filterContext.RouteData.Values["controller"].ToString(); //獲取action名稱 var actionName = filterContext.ActionDescriptor.RouteValues["action"]; actionName = controllerActionDescriptor.ActionName; actionName = filterContext.RouteData.Values["action"].ToString(); //獲取當前的請求方式:Get或者Post string requestMethod = filterContext.HttpContext.Request.Method.ToLower(); //獲取路由占位符對應的值 object currId = filterContext.RouteData.Values["id"]; //獲取當前請求URL參數 var value = filterContext.HttpContext.Request.Query.ContainsKey("key") ? filterContext.HttpContext.Request.Query["key"].ToString() : ""; //獲取Action參數值 IDictionary<string, object> actionArguments = filterContext.ActionArguments; if (actionArguments != null) { foreach (KeyValuePair<string, object> item in actionArguments) { var key = item.Key; //參數名 var val = item.Value; //參數值 } } ParamsFilter(filterContext); //參數過濾 //判斷當前所請求的控制器上是否有打上指定的特性標簽 if (controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(SkipLoginValidateAttribute), false)) { //有的話就不進行相關操作【例如:不進行登入驗證】 //return; } //獲取當前所請求的Action上指定的特性標簽 object[] arrObj1 = controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(SkipLoginValidateAttribute), false); //獲取當前所請求的Action上所有的特性標簽 object[] arrObj2 = controllerActionDescriptor.MethodInfo.GetCustomAttributes(false); //判斷當前所請求的Action上是否有打上指定的特性標簽 if (controllerActionDescriptor.MethodInfo.IsDefined(typeof(SkipLoginValidateAttribute), false)) { //有的話就不進行相關操作【例如:不進行登入驗證】 return; } #endregion 【權限驗證】【登入驗證】 //【權限驗證】【登入驗證】邏輯 //To Do Something } #region 參數過濾 /// <summary> /// 參數過濾 /// </summary> void ParamsFilter(ActionExecutingContext filterContext) { //獲取參數集合 var parameters = filterContext.ActionDescriptor.Parameters; //遍歷參數集合 foreach (var param in parameters) { if (!filterContext.ActionArguments.ContainsKey(param.Name)) { continue; } if (filterContext.ActionArguments[param.Name] == null) { continue; } //當參數是字符串 if (param.ParameterType.Equals(typeof(string))) { //業務處理 var value = filterContext.ActionArguments[param.Name].ToString(); filterContext.ActionArguments[param.Name] = value.Replace(" ", ""); } else if (param.ParameterType.Equals(typeof(string[]))) //如果是字符串數組則遍歷每一個成員 { var arrStrs = filterContext.ActionArguments[param.Name] as string[]; if (arrStrs != null && arrStrs.Length > 0) { for (int i = 0; i < arrStrs.Length; i++) { arrStrs[i] = arrStrs[i].Replace(" ", ""); //業務處理 } filterContext.ActionArguments[param.Name] = arrStrs; } } else if (param.ParameterType.IsClass) //當參數是一個實體 { ModelParamsFilter(param.ParameterType, filterContext.ActionArguments[param.Name]); } } } /// <summary> /// 實體參數過濾 /// </summary> object ModelParamsFilter(Type type, object obj) { if (obj == null) { return obj; } var properties = type.GetProperties(); foreach (var item in properties) { if (item.GetValue(obj) == null) { continue; } var attribute = typeof(SkipLoginValidateAttribute); if (item.IsDefined(attribute, false)) //特殊參數處理 { continue; } //當參數是字符串 if (item.PropertyType.Equals(typeof(string))) { //業務處理 string value = item.GetValue(obj).ToString(); item.SetValue(obj, value.Replace(" ", "")); } else if (item.PropertyType.Equals(typeof(string[]))) //如果是字符串數組則遍歷每一個成員 { var arrStrs = item.GetValue(obj) as string[]; if (arrStrs != null && arrStrs.Length > 0) { for (int i = 0; i < arrStrs.Length; i++) { arrStrs[i] = arrStrs[i].Replace(" ", ""); //業務處理 } item.SetValue(obj, arrStrs); } } else if (item.PropertyType.IsClass) //當參數是一個實體 { item.SetValue(obj, ModelParamsFilter(item.PropertyType, item.GetValue(obj))); } } return obj; } #endregion 參數過濾 /// <summary> /// 在Action執行后觸發(如果在控制器中也重寫了該方法,則先執行過濾器中的方法,再執行控制器中的方法) /// </summary> /// <param name="filterContext">Action執行后上下文對象</param> public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); } /// <summary> /// 在Action返回前觸發,執行View前 /// </summary> /// <param name="filterContext">Action返回前上下文對象</param> public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } /// <summary> /// 在Action返回后,View顯示后觸發 /// </summary> /// <param name="filterContext">Action返回后上下文對象</param> public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); } /// <summary> /// 在Action執行前觸發 /// 注意:OnActionExecuting和OnActionExecuted是同步方法,而OnActionExecutionAsync是異步方法,同步和異步是不能同時都執行的,如果都寫上了那只會執行異步的方法。 /// 官網相關資料:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { //await base.OnActionExecutionAsync(context, next); #region 摘自官網 /* // Do something before the action executes. // next() calls the action method. var resultContext = await next(); // Do something after the action executes. */ #endregion 摘自官網 //Action執行之前的業務處理 Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}開始執行 => This is MyActionFilterAttribute/OnActionExecutionAsync"); Stopwatch sw = new Stopwatch(); sw.Start(); ActionExecutedContext resultContext = await next(); //執行Action sw.Stop(); //Action執行之后的業務處理 Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}執行結束 => This is MyActionFilterAttribute/OnActionExecutionAsync 處理花費時間:{sw.ElapsedMilliseconds}ms"); } /// <summary> /// 在Result執行前觸發 /// 注意:OnResultExecuting和OnResultExecuted是同步方法,而OnResultExecutionAsync是異步方法,同步和異步是不能同時都執行的,如果都寫上了那只會執行異步的方法。 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { //await base.OnResultExecutionAsync(context, next); //Result執行之前的業務處理(View執行前) Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}開始執行 => This is MyActionFilterAttribute/OnResultExecutionAsync"); Stopwatch sw = new Stopwatch(); sw.Start(); ResultExecutedContext resultContext = await next(); //執行Result(執行View) sw.Stop(); //Result執行之后的業務處理(View執行后) Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId}執行結束 => This is MyActionFilterAttribute/OnResultExecutionAsync 處理花費時間:{sw.ElapsedMilliseconds}ms"); } } }
在Startup.cs里面注冊全局過濾器:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using MvcDemo.Filters; namespace MvcDemo { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(options => { options.Filters.Add<MyActionFilterAttribute>(); //注冊全局過濾器 任意控制器-Action }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
最后我們使用cmd命令行方式啟動我們的Demo,並且訪問 /Home/Index,看下最終控制台的輸出結果:
關於過濾器的更多內容,有興趣的可參考我之前寫的另外一篇博文:https://www.cnblogs.com/xyh9039/p/14359665.html
至此本文就全部介紹完了,如果覺得對您有所啟發請記得點個贊哦!!!
Demo源碼:
鏈接:https://pan.baidu.com/s/1EuNkf23sdDHhtFxERZxAvA 提取碼:2cyo
此文由博主精心撰寫轉載請保留此原文鏈接:https://www.cnblogs.com/xyh9039/p/15174104.html
版權聲明:如有雷同純屬巧合,如有侵權請及時聯系本人修改,謝謝!!!