天天看點

ASP.NET Core MVC基礎知識

本章将和大家分享ASP.NET Core MVC的一些基礎知識,包括Action接收參數、Action向視圖傳值、常用過濾器的使用、布局頁、分部視圖、視圖元件ViewComponent的使用以及在視圖中如何導入公共命名空間等,希望通過本文的分享能夠對初學者有所幫助。

本章将和大家分享ASP.NET Core MVC的一些基礎知識,包括Action接收參數、Action向視圖傳值、常用過濾器的使用、布局頁、分部視圖、視圖元件ViewComponent的使用以及在視圖中如何導入公共命名空間等,希望通過本文的分享能夠對初學者有所幫助。下面我們直接進入主題。

首先來看一下我們的解決方案:

ASP.NET Core MVC基礎知識
本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 運作結果如下:

ASP.NET Core MVC基礎知識

此外Action向視圖傳值還有一種特殊的情況就是匿名類型,具體的傳值方式可參考博文:https://www.cnblogs.com/xyh9039/p/11348684.html

2、 在視圖中如何導入公共命名空間

MVC架構給我們提供了一個特殊的視圖,叫 _ViewImports.cshtml,可以在該視圖當中導入公共命名空間。

ASP.NET Core MVC基礎知識

它的作用域是_ViewImports.cshtml視圖所在的Views檔案夾下的所有視圖。

另外我們還可以看到一個特殊的視圖,叫_ViewStart.cshtml,如下圖:

ASP.NET Core MVC基礎知識

其作用就是在所有的View在呈現之前都會先執行_ViewStart.cshtml裡面的代碼,但是有一種特殊情況就是如果一個View是按照分部視圖方式輸出的,則不會觸發_ViewStart.cshtml裡面的代碼。

3、 分部視圖

首先來看下目錄結構:

ASP.NET Core MVC基礎知識

其中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 運作結果如下:

ASP.NET Core MVC基礎知識

分部視圖微軟官方文檔:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/partial?view=aspnetcore-5.0

ASP.NET Core MVC基礎知識

分部視圖的視圖搜尋路徑:

//按名稱(無檔案擴充名)引用分部視圖,則按所述順序搜尋以下位置
//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" })      

接下來我們就來看下如何實作,同樣我們先來看下目錄結構:

ASP.NET Core MVC基礎知識

下面我們直接來看代碼:

視圖元件類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

先來看下目錄結構:

ASP.NET Core MVC基礎知識

下面我們直接上代碼:

 _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 運作結果如下:

ASP.NET Core MVC基礎知識

布局頁微軟官方文檔: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、常用過濾器的使用

老樣子,我們首先先來看下相應的目錄機構:

ASP.NET Core MVC基礎知識

下面我們重點來看下基類和過濾器:

基類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,看下最終控制台的輸出結果:

ASP.NET Core MVC基礎知識

關于過濾器的更多内容,有興趣的可參考我之前寫的另外一篇博文:https://www.cnblogs.com/xyh9039/p/14359665.html

至此本文就全部介紹完了,如果覺得對您有所啟發請記得點個贊哦!!! 

Demo源碼:

連結:https://pan.baidu.com/s/1EuNkf23sdDHhtFxERZxAvA 
提取碼:2cyo      

此文由部落客精心撰寫轉載請保留此原文連結:https://www.cnblogs.com/xyh9039/p/15174104.html

版權聲明:如有雷同純屬巧合,如有侵權請及時聯系本人修改,謝謝!!!  

繼續閱讀