In diesem Artikel werden hauptsächlich die allgemeinen Erweiterungspunkte von ASP.NET MVC vorgestellt: Filter und Modellbindung. Es ist von großem praktischem Wert.
1. Filter (Filter)
Jede Anfrage in ASP.NET MVC wird einer bestimmten Aktion (im Folgenden als „Methode“ bezeichnet) unter dem entsprechenden Controller (im Folgenden als „Methode“ bezeichnet) zugewiesen „Controller“)) Bei der Verarbeitung schreiben Sie unter normalen Umständen den Code einfach direkt in die Methode. Wenn Sie jedoch vor oder nach der Ausführung der Methode Logik verarbeiten möchten, müssen Sie hier Filter verwenden.
Es gibt drei häufig verwendete Filter: Authorize (Autorisierungsfilter), HandleError (Ausnahmefilter), ActionFilter (benutzerdefinierter Filter), und die entsprechenden Klassen sind: AuthorizeAttribute, HandleErrorAttribute und ActionFilterAttribute, Inherit Diese Klassen und überschreiben ihre Methoden, um verschiedene Funktionen zu erreichen.
1.Autorisierungsfilter
Wie der Name schon sagt, wird der Autorisierungsfilter zur Autorisierung verwendet. Der Autorisierungsfilter wird vor der Ausführung der Methode ausgeführt, um einzuschränken, ob die Anfrage kann diese Methode eingeben, eine neue Methode erstellen:
public JsonResult AuthorizeFilterTest() { return Json(new ReturnModel_Common { msg = "hello world!" }); }
Direkter Zugriff, um das Ergebnis zu erhalten:
Nehmen wir nun an, dass diese AuthorizeFilterTest-Methode eine ist Bei der Hintergrundmethode muss der Benutzer nur auf ein gültiges Token zugreifen. Der herkömmliche Ansatz besteht darin, das Token in der AuthorizeFilterTest-Methode zu empfangen und zu überprüfen. Da es jedoch viele Methoden gibt, ist es offensichtlich unpraktisch, in jede Methode einen Bestätigungscode zu schreiben . Zu diesem Zeitpunkt ist eine Autorisierung erforderlich:
public class TokenValidateAttribute : AuthorizeAttribute { /// <summary> /// 授权验证的逻辑处理。返回true则通过授权,false则相反 /// </summary> /// <param name="httpContext"></param> /// <returns></returns> protected override bool AuthorizeCore(HttpContextBase httpContext) { string token = httpContext.Request["token"]; if (string.IsNullOrEmpty(token)) { return false; } else { return true; } } }
Erstellen Sie eine neue Klasse, die AuthorizeAttribute erbt, und schreiben Sie die AuthorizeCore-Methode neu. Wenn das Token einen Wert hat, gibt es true zurück Wenn nicht, wird „false“ zurückgegeben. Markieren Sie es als „Methoden, die eine Autorisierung für den Zugriff erfordern“:
[TokenValidate] public JsonResult AuthorizeFilterTest() { return Json(new ReturnModel_Common { msg = "hello world!" }) }
Nach dem Markieren von TokenValidate wird die AuthorizeCore-Methode vor AuthorizeFilterTest ausgeführt. Wenn AuthorizeCore „true“ zurückgibt, führt die Autorisierung den Code erfolgreich aus in AuthorizeFilterTest, andernfalls schlägt die Autorisierung fehl. Token nicht weitergeben:
Token nicht weitergeben:
Token nicht weitergeben, wenn die Autorisierung fehlschlägt, ist MVC standardmäßig nicht autorisiert Seite eingegeben wird. Lassen Sie uns hier eine Verbesserung vornehmen: Unabhängig davon, ob die Autorisierung erfolgreich ist oder nicht, muss das Rückgabewertformat konsistent sein, um die Front-End-Verarbeitung zu erleichtern. Schreiben Sie zu diesem Zeitpunkt einfach die HandleUnauthorizedRequest-Methode in der AuthorizeAttribute-Klasse neu:
/// <summary> /// 授权失败处理 /// </summary> /// <param name="filterContext"></param> protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { base.HandleUnauthorizedRequest(filterContext); var json = new JsonResult(); json.Data = new ReturnModel_Common { success = false, code = ReturnCode_Interface.Token过期或错误, msg = "token expired or error" }; json.JsonRequestBehavior = JsonRequestBehavior.AllowGet; filterContext.Result = json; }
Wirkung:
Praktisch: Die am weitesten verbreitete Anwendung des Autorisierungsfilters ist das RechteverwaltungssystemNachdem sich der Benutzer angemeldet hat Bei Erfolg gibt der Server ein verschlüsseltes Token aus. Der Server entsperrt das Token in der AuthorizeCore-Methode, um die Benutzer-ID abzurufen und zu überprüfen, ob es vorhanden ist Berechtigung zum Anfordern der aktuellen -Schnittstelle . Wenn ja, wird true zurückgegeben, andernfalls wird false zurückgegeben. Der Vorteil dieser Methode zur Autorisierung gegenüber der erfolgreichen Anmeldung bei Cookie und Session besteht darin, dass eine Schnittstelle sowohl auf der PC-Seite als auch auf der App-Seite genutzt werden kann.
2. HandleError-Ausnahmefilter
Der Ausnahmefilter behandelt Codeausnahmen und wird ausgeführt, wenn der Systemcode standardmäßig eine Ausnahmefilterung auslöst FilterConfig.cs im App_Start-Verzeichnis:filters.Add(new HandleErrorAttribute());
Ausnahmebehandlung von MVC aus und gibt eine Standardfehlerseite zurück: Ansichten /Shared/Error (Diese Seite kann nur angezeigt werden, wenn das Programm an den Server gesendet wird, um einen Fehler zu melden. Wenn das lokale Debugging über hohe Berechtigungen verfügt, können Sie weiterhin die spezifischen Fehlerinformationen sehen)
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width" /> <title>错误</title> </head> <body> <hgroup> <h1>错误。</h1> <h2>处理你的请求时出错。</h2> </hgroup> </body> </html>
Parameter anfordern und Zeitpunkt, wenn ein Fehler gemeldet wird
2) JSON in einem bestimmten Format zurückgeben, um die Front-End-Verarbeitung zu erleichtern; Da die meisten Systeme jetzt Ajax-Anfragen verwenden, kehrt es bei der Meldung eines Fehlers zur MVC-Standardfehlerseite zurück, was im Frontend schwierig zu handhaben ist. Erstellen Sie eine neue Klasse LogExceptionAttribute, die HandleErrorAttribute erbt. und überschreibt die interne OnException-Methode:public override void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; string param = Common.GetPostParas(); string ip = HttpContext.Current.Request.UserHostAddress; LogManager.GetLogger("LogExceptionAttribute").Error("Location:{0}/{1} Param:{2}UserIP:{3} Exception:{4}", controllerName, actionName, param, ip, filterContext.Exception.Message); filterContext.Result = new JsonResult { Data = new ReturnModel_Common { success = false, code = ReturnCode_Interface.服务端抛错, msg = filterContext.Exception.Message }, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } if (filterContext.Result is JsonResult) filterContext.ExceptionHandled = true;//返回结果是JsonResult,则设置异常已处理 else base.OnException(filterContext);//执行基类HandleErrorAttribute的逻辑,转向错误页面 }
异常过滤器就不像授权过滤器一样标注在方法上面了,直接到App_Start目录下的FilterConfig.cs注册下,这样所有的接口都可以生效了:
filters.Add(new LogExceptionAttribute());
异常过滤器里使用了NLog作为日志记录工具,Nuget安装命令:
Install-Package NLog Install-Package NLog.Config
相比Log4net,NLog配置简单,仅几行代码即可,NLog.config:
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target xsi:type="File" name="f" fileName="${basedir}/log/${shortdate}.log" layout="${uppercase:${level}} ${longdate} ${message}" /> <target xsi:type="File" name="f2" fileName="D:\log\MVCExtension\${shortdate}.log" layout="${uppercase:${level}} ${longdate} ${message}" /> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="f2" /> </rules> </nlog>
如果报错,日志就记录在D盘的log目录下的MVCExtension目录下,一个项目一个日志目录,方便管理。全部配置完成,看下代码:
public JsonResult HandleErrorFilterTest() { int i = int.Parse("abc"); return Json(new ReturnModel_Data { data = i }); }
字符串强转成int类型,必然报错,页面响应:
同时日志也记录下来了:
3.ActionFilter自定义过滤器
自定义过滤器就更加灵活了,可以精确的注入到请求前、请求中和请求后。继承抽象类ActionFilterAttribute并重写里面的方法即可:
public class SystemLogAttribute : ActionFilterAttribute { public string Operate { get; set; } public override void OnActionExecuted(ActionExecutedContext filterContext) { filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnActionExecuted"); base.OnActionExecuted(filterContext); } public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnActionExecuting"); base.OnActionExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnResultExecuted"); base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.Write("<br/>" + Operate + ":OnResultExecuting"); base.OnResultExecuting(filterContext); } }
这个过滤器适合做系统操作日志记录功能:
[SystemLog(Operate = "添加用户")] public string CustomerFilterTest() { Response.Write("<br/>Action 执行中..."); return "<br/>Action 执行结束"; }
看下结果:
四个方法执行顺序:OnActionExecuting—>OnActionExecuted—>OnResultExecuting—>OnResultExecuted,非常精确的控制了整个请求过程。
实战中记录日志过程是这样的:在OnActionExecuting方法里写一条操作日志到数据库里,全局变量存下这条记录的主键,到OnResultExecuted方法里说明请求结束了,这个时候自然知道用户的这个操作是否成功了,根据主键更新下这条操作日志的是否成功字段。
二、模型绑定(ModelBinder)
先看一个普通的方法:
public ActionResult Index(Student student) { return View(); }
这个方法接受的参数是一个Student对象,前端传递过来的参数跟Student对象里的属性保持一直,那么就自动被绑定到这个对象里了,不需要在方法里new Student这个对象并挨个绑定属性了,绑定的过程由MVC中的DefaultModelBinder完成的,DefaultModelBinder同时继承了IModelBinder接口,现在就利用IModelBinder接口和DefaultModelBinder来实现更加灵活的模型绑定。
场景一、前端传过来了一个加密的字符串token,方法里需要用token里的某些字段,那就得在方法里接收这个字符串、解密字符串、转换成对象,这样一个方法还好说,多了的话重复代码非常多,就算提取通用方法,还是要在方法里调用这个通用方法,有没有办法直接在参数里就封装好这个对象?
模型绑定的对象:
public class TokenModel { /// <summary> /// 主键 /// </summary> public int Id { get; set; } /// <summary> /// 姓名 /// </summary> public string Name { set; get; } /// <summary> /// 简介 /// </summary> public string Description { get; set; } }
新建一个TokenBinder继承IModelBinder接口并实现其中的BindModel方法:
public class TokenBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var token = controllerContext.HttpContext.Request["token"]; if (!string.IsNullOrEmpty(token)) { string[] array = token.Split(':'); if (array.Length == 3) { return new TokenModel() { Id = int.Parse(array[0]), Name = array[1], Description = array[2] }; } else { return new TokenModel() { Id = 0 }; } } else { return new TokenModel() { Id = 0 }; } } }
这个方法里接收了一个token参数,并对token参数进行了解析和封装。代码部分完成了需要到Application_Start方法里进行下注册:
ModelBinders.Binders.Add(typeof(TokenModel), new TokenBinder());
现在模拟下这个接口:
public JsonResult TokenBinderTest(TokenModel tokenModel) { var output = "Id:" + tokenModel.Id + ",Name:" + tokenModel.Name + ",Description:" + tokenModel.Description; return Json(new ReturnModel_Common { msg = output }); }
调用下:
可以看出,“1:汪杰:oppoic.cnblogs.com”已经被绑定到tokenModel这个对象里面了。但是如果稍复杂的模型绑定IModelBinder就无能为力了。
场景二、去除对象某个属性的首位空格
public class Student { public int Id { get; set; } public string Name { get; set; } public string Class { get; set; } }
如果前端传来的Name属性有空格,如何去除呢?利用DefaultModelBinder即可实现更灵活的控制
public class TrimModelBinder : DefaultModelBinder { protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder) { var obj = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder); if (obj is string && propertyDescriptor.Attributes[typeof(TrimAttribute)] != null)//判断是string类型且有[Trim]标记 { return (obj as string).Trim(); } return obj; } }
标注下需要格式化首位属性的实体:
[ModelBinder(typeof(TrimModelBinder))] public class Student { public int Id { get; set; } [Trim] public string Name { get; set; } public string Class { get; set; } }
好了,测试下:
public JsonResult TrimBinderTest(Student student) { if (string.IsNullOrEmpty(student.Name) || string.IsNullOrEmpty(student.Class)) { return Json(new ReturnModel_Common { msg = "未找到参数" }); } else { return Json(new ReturnModel_Common { msg = "Name:" + student.Name + ",长度:" + student.Name.Length + " Class:" + student.Class + ",长度:" + student.Class.Length }); } }
可见,标注了Trim属性的Name长度是去除空格的长度:7,而没有标注的Class属性的长度则是6。
【相关推荐】
2. ASP.NET教程
Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung von Beispielen für Filter und Modellbindung in ASP.NET. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!