//app.UseHttpsRedirection();现在,重新运行应用程序并且打开非https的url在浏览器,这次它不会跳转,接下来,我们修改Home控制器中的Index方法返回如下字符串:
public string Index() { return "This is the Index action on the Home controller"; }现在,重新运行应用程序并且打开非https的url在浏览器,这次它不会跳转,我们将看到下面消息显示在浏览器上:
[RequireHttps] public string Index() { return "This is the Index action on the Home controller"; }现在我们限制这个方法仅仅能访问HTTPS的请求,我们将[RequireHttps]特性添加到方法上,重新运行应用程序并且再次打开非https url,我们将获取如下页面:
[RequireHttps] public class HomeController : Controller { public string Index() { // 堆代码 duidaima.com return "This is the Index action"; } public string List() { return "This is the List action"; } public string Hello() { return "This is the Hello action"; } }三. ASP.NET Core MVC中常用过滤器
细
过滤器 |
接口 |
描述 |
Authorization |
IAuthorizationFilterIAsyncAuthorizationFilter |
使用它申请授权和安全策略 |
Action |
IActionFilter, IAsyncActionFilter |
在action方法之前或者之后指定执行的具体工作 |
Result |
IResultFilter, IAsyncResultFilter |
用于在操作方法的结果之前或之后立即执行指定工作 |
Exception |
IExceptionFilter, IAsyncExceptionFilter |
使用它来处理异常 |
public interface IAuthorizationFilter : IFilterMetadata { void OnAuthorization(AuthorizationFilterContext context); }IAsyncAuthorizationFilter 接口定义如下:
public interface IAsyncAuthorizationFilter : IFilterMetadata { Task OnAuthorizationAsync(AuthorizationFilterContext context); }IAsyncAuthorizationFilter接口使用创建异步的授权过滤器,接口的方法是- OnAuthorization() 和 OnAuthorizationAsync() 写代码授权进入的请求,参数-AuthorizationFilterContext 表示接收的描述请求的上下文数据,AuthorizationFilterContext 包含一个属性名字为Result的属性(类型是IActionResult),授权过滤器会设置该属性。
namespace AspNetCore.Filters.CustomFilters { public class HttpsOnly : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { if (!context.HttpContext.Request.IsHttps) context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden); } } }注意HttpsOnly类继承自Attribute类,为了能够在Controllers和Action方法上使用该特性,同时也继承了IAuthorizationFilter接口为了创建一个Authorization过滤器。
[HttpsOnly] public string Index() { return "This is the Index action on the Home controller"; }最后,运行应用程序并且打开非https地址,我们将会看到HTTP ERROR 403, 这是因为我们请求只允许非https url
当我们在浏览器中打开https的url时,Authorization过滤器不会阻止执行,我们不会设置AuthorizationFilterContext的Result属性,Index方法内部的代码将会执行,这个例子很好解释了授权过滤器的工作原理,接下来我们将创建一个Action 过滤器。
public interface IActionFilter : IFilterMetadata { void OnActionExecuting(ActionExecutingContext context); void OnActionExecuted(ActionExecutedContext context); }我们在Action方法上使用Action 过滤器时,OnActionExecuting在Action方法执行之前被调用,OnActionExecuted在Action方法执行之后被调用。
名称 |
描述 |
Controller |
将要调用的Action方法的控制器的名称 |
Result |
当这个属性设置为IActionResult的值时,框架会呈现IActionResult,阻止调用Action方法 |
名称 |
描述 |
Controller |
将要调用的Action方法的控制器的名称 |
Exception |
包含了在Action方法中发生的异常 |
ExceptionHandled |
将该属性设置为true时,阻止异常传播 |
Result |
返回IActionResult,我们可以用自己的业务逻辑来修改或者替换它 |
namespace AspNetCore.Filters.CustomFilters { public class TimeElapsed : Attribute, IActionFilter { private Stopwatch timer; public void OnActionExecuting(ActionExecutingContext context) { timer.Start(); } public void OnActionExecuted(ActionExecutedContext context) { timer.Stop(); string result = " Elapsed time: " + $" {timer.Elapsed.TotalMilliseconds} ms"; IActionResult iActionResult = context.Result; ((ObjectResult)iActionResult).Value += result; } } }创建Stopwatch类计算方法的执行时间, 在OnActionExecuting方法中开始执行,在OnActionExecuted方法中结束执行,接下使用ActionExecutedContext对象的Result属性获取到Action方法的执行结果,他包含了一个我们之前在Home控制器的Index方法设置的字符串,最后,我们转换它到ObjectResult类型并且添加时间到它的Value属性,为了使用filter,添加[TimerElapsed]特性到Home控制器的Index 方法,显示如下:
namespace AspNetCore.Filters.Controllers { public class HomeController : Controller { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } [HttpsOnly] [TimeElapsed] public string Index() { return "This is the Index action on the Home controller"; } } }
运行结果如下:
public interface IAsyncActionFilter : IFilterMetadata { Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next); }我们看到该接口中只有一个方法,ActionExecutingContext对象提供了上下文对象,ActionExectionDelegate 表示一个action方法(或者下一个Filter)
namespace AspNetCore.Filters.CustomFilters { public class TimeElapsedAsync : Attribute, IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { Stopwatch stopwatch = Stopwatch.StartNew(); await next(); stopwatch.Stop(); string result = "<div>Elapsed time: " + $"{stopwatch.Elapsed.TotalMilliseconds} ms</div>"; byte[] bytes = Encoding.ASCII.GetBytes(result); await context.HttpContext.Response.Body.WriteAsync(bytes, 0, bytes.Length); } } }七.ASP.NET Core Result 过滤器
public interface IResultFilter : IFilterMetadata { void OnResultExecuting(ResultExecutingContext context); void OnResultExecuted(ResultExecutedContext context); }注意:Result过滤器和Action过滤器定义类似
名称 |
描述 |
Controller |
被调用action方法的控制器的名称 |
Result |
这是一个IActionResult类型的属性,包含了action方法返回的IActionResult对象 |
Cancel |
设置这个属性为true将会阻止action结果的处理并且返回404错误 |
名称 |
描述 |
Controller |
被调用action方法的控制器的名称 |
Canceled |
只读属性表示请求是否被取消 |
Exception |
包含在action方法中抛出的异常 |
ExceptionHandled |
将该属性设置为true时,异常不会传播 |
Result |
IActionResult只读属性由含Action方法生成 |
namespace AspNetCore.Filters.CustomFilters { public class ChangeView : Attribute, IResultFilter { public void OnResultExecuted(ResultExecutedContext context) { } public void OnResultExecuting(ResultExecutingContext context) { context.Result = new ViewResult { ViewName = "List" }; } } }我们在OnResultExecuting()方法方法中设置ViewName为List 在, 当我们把这个过滤器使用到action方法时,List试图会替换默认试图来呈现,让我们做个测试,创建一个新的Action方法叫Message在Home控制器中,如下所示,我们把[ChangeView]过滤器使用到该Action方法上:
[ChangeView] public IActionResult Message() { return View(); }该Action方法默认调用Message.cshtml,现在我们在Views->Shared目录下添加两个试图:
Message.cshtml <h2>Message</h2> <p>This is Message View</p> List.cshtml <h2>List</h2> <p>This is Message View</p>运行应用程序测试一下该特性,运行应用程序并且进入URL - /Home/Message 我们将看到List试图被调用,如下图所示
public interface IAsyncResultFilter : IFilterMetadata { Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next); }我们在CustomFilters文件下添加一个ChangeViewAsync.cs类:
namespace AspNetCore.Filters.CustomFilters { public class ChangeViewAsync : Attribute, IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { context.Result = new ViewResult() { ViewName = "List" }; await next(); } } }IAsyncResultFilter只有一个方法OnResultExecutionAsync,参数有一个ResultExecutingContext类型我们使用它设置ViewName为List。
[ChangeViewAsync] public IActionResult Message() { return View(); }八. 混合Action/Result 过滤器
public class HybridActRes: ActionFilterAttribute { Stopwatch stopwatch; public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { stopwatch=Stopwatch.StartNew(); await next(); } public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { stopwatch.Stop(); context.Result = new ViewResult() { ViewName = "ShowTime", ViewData = new Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary( new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = "Elapsed time: " + $"{stopwatch.Elapsed.TotalMilliseconds} ms" } }; await next(); } }混合过滤器有2个方法:
[HybridActRes] public IActionResult List() { return View(); }最后在Views->Shared目录下创建一个ShowTime的视图,代码如下:
@model string <h2>Show Time</h2> @Model接下来我们测试一下该功能并且导航到URL- /Home/List,我们将看到ShowTime视图将被调用,并且显示执行时间
public interface IExceptionFilter : IFilterMetadata { void OnException(ExceptionContext context); }IAsyncExceptionFilter接口的定义
public interface IAsyncExceptionFilter : IFilterMetadata { Task OnExceptionAsync(ExceptionContext context); }在这两个接口,在OnException & OnExceptionAsynccontext 方法参数中提供了ExceptionContext对象,ExceptionContext 类有如下属性:
名称 |
描述 |
Exception |
这个属性包含抛出的异常 |
ExceptionDispatchInfo |
包含了异常堆栈的详细 |
ExceptionHandled |
只读的属性,异常是否处理 |
Result |
这个属性设置IActionResult生成response |
public class CatchError : Attribute, IExceptionFilter { public void OnException(ExceptionContext context) { context.Result = new ViewResult { ViewData = new Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary( new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = context.Exception.Message } }; } }我们实现了一个CatchError的异常过滤器,我们将方法应用[CatchError]特性,该方法发生异常时,过滤器会自动捕获异常,在OnException()方法内我们给了Model属性赋值为context.Exception.Message,Model值将会显示在View,现在在Home控制器中创建一个新的Action方法叫Exception并且添加CatchError特性:
[CatchError] public IActionResult Exception(int? id) { if (id == null) throw new Exception("Error Id cannot be null"); else return View($"The value is {id}"); }如果id为空时会触发一个异常否则将会在View中显示id的值,最后在View->Home文件夹下创建一个Exception试图
@model string <h2>Exception</h2> @Model接下来我们验证Exception过滤器,运行应用程序并且进入URL- /Home/Exception,你将会看到显示如下信息-Error Id cannot be null as an exception is raised ,该异常信息通过过滤器捕获到,现在进入URL- /Home/Exception/5 没有异常发生,你将会看到如下信息The value is 5