MVC filter Use Case: Uniform Exception Handling, streamlined code, mvc Filter

Source: Internet
Author: User

MVC filter Use Case: Uniform Exception Handling, streamlined code, mvc Filter

The fun of refactoring lies in streamlining code, modular design, and decoupling functions ...... The reconstruction of exception handling just satisfies the above three aspects. below is my caution.

I. Related Learning

In the article "streamline your own 20% code", we discuss how to handle exceptions in a unified manner, and how to streamline the code by encapsulating exception handling. There are two solutions:

2. Applications on the MVC site

Let's briefly review previous knowledge:

In a standard three-tier structure, MVC corresponds to the presentation layer. The Action in the Controller is used to collect data and transmit the data to the business logic layer. The logic layer calls the data access layer, obtain the data and perform corresponding business logic processing, and finally return the processed data to the Model. Finally, the Model and View are combined to form a page that is displayed to end users. For example:

 

According to the idea of method 1, when calling the logical layer method in Action, wrap it with the encapsulated exception handling method. The Code is as follows:

Public ActionResult Index (int id) {Boolean result = false; // receives the returned value Process. execute () => result = Save (id); // return View ();}View Code

Lambda can also be used to encapsulate more complex logic, and the bool value is not necessarily returned:

Public ActionResult Index () {Process. Execute () => {... // more complex logic}); return View ();}View Code

This will include a large number of Process. Execute method calls in the Action. If it is moved to the logic layer for encapsulation, a large number of repeated code will be generated in the logic layer, or it is not concise enough. In addition, this code is not only manually written repeatedly, but is not decoupled from the functions of the logic layer and is not modularized. Therefore, we need to continue refactoring.

Iii. MVC Filter

From the idea of using Attribute in method 2, it is easy to think of the MVC filter. The Interception Function of the filter can well implement Exception Handling According to the AOP idea and decouple the module in the logic layer. There are many articles on the Internet about the MVC filter. We recommend the MVC filter details. Here we will focus on the execution sequence of the filter.

  • General filter execution sequence
  • When a filter is set in both the Controller and Action, the execution sequence is generally from external to internal, that is, "Global"-> "Controller"-> "behavior"
  • Because an exception is thrown from the inside out, the processing order of the exception is the opposite. It is generally from the inside out, that is, "action"-> "controller"-> "Global"
Iv. System-provided Exception Handling

The filters we use are either adding Attribute to Action or adding Attribute to Controller. What is the global filter mentioned above? First look at the code in Gloabal:

Protected void Application_Start () {// register Area AreaRegistration. registerAllAreas (); // register the filter RegisterGlobalFilters (GlobalFilters. filters); // register the route RegisterRoutes (RouteTable. routes);} public static void RegisterGlobalFilters (GlobalFilterCollection filters) {filters. add (new HandleErrorAttribute ());}View Code

As you can see, the global filter has been registered when the application is started, and handleerrorattri is the exception filter that comes with the system. The global filter registered here does not need to be declared by each Controller or Action. It directly acts on the global, which can capture all exceptions of the entire site. Let's see how the source code handles exceptions:

Public virtual void OnException (ExceptionContext filterContext) {if (filterContext = null) {throw new ArgumentNullException ("filterContext");} if (! FilterContext. IsChildAction &&(! FilterContext. exceptionHandled & filterContext. httpContext. isCustomErrorEnabled) {Exception innerException = filterContext. exception; if (new HttpException (null, innerException ). getHttpCode () = 500) & this. predictiontype. isInstanceOfType (innerException) {string controllerName = (string) filterContext. routeData. values ["controller"]; string actionName = (string) filterContext. routeData. values ["action"]; HandleErrorInfo model = new HandleErrorInfo (filterContext. exception, controllerName, actionName); ViewResult result = new ViewResult {ViewName = this. view, MasterName = this. master, ViewData = new ViewDataDictionary <HandleErrorInfo> (model), TempData = filterContext. controller. tempData}; filterContext. result = result; filterContext. exceptionHandled = true; filterContext. httpContext. response. clear (); filterContext. httpContext. response. statusCode = 500; filterContext. httpContext. response. trySkipIisCustomErrors = true ;}}}View Code

In the exception handling logic of HandleErrorAttribute, a Model of the HandleErrorInfo class is generated, and the returned result is set to a new ViewResult. The default ViewName of this view is Error, which corresponds to the Error view in the Share folder. The built-in Error view does not use the HandleErrorInfo Model, so there is not much public information. You can modify it based on your specific needs. For example:

@ Model HandleErrorInfo <br/> <div class = "container"> <div class = "alert-error"> To make the filter effective, you also need to configure it in the configuration file:

<customErrors mode="On" />
5. unified handling of custom exceptions

Before handling exceptions in a unified manner, clarify the requirements:

Implementation 1 and 3:

Because the entire site needs to record abnormal logs, we need to design a LogExceptionAttribute to intercept the logs. This not only reflects the idea of AOP but also satisfies the needs of decoupling. The Code is as follows:

[AttributeUsage (AttributeTargets. Class, Inherited = true, AllowMultiple = false)] public class LogExceptionAttribute: HandleErrorAttribute {public override void OnException (ExceptionContext filterContext) {if (! FilterContext. exceptionHandled) {string controllerName = (string) filterContext. routeData. values ["controller"]; string actionName = (string) filterContext. routeData. values ["action"]; string msgTemplate = "An exception occurs when the controller [{0}] action [{1}] is executed."; LogManager. getLogger ("LogExceptionAttribute "). error (string. format (msgTemplate, controllerName, actionName), filterContext. exception);} base. onException (filterContext );}}View Code

LogExceptionAttribute inherits HandleErrorAttribute. After the OnException method is rewritten to record the exception log, the base. OnException method is called to return to the default exception handling process of the system, enabling redirection to the error page.

LogExceptionAttribute sets its AttributeUsage feature. AttributeTargets. Class specifies that the filter can only be used at the Class level, that is, Controller; AllowMultiple = false. The setting does not allow many executions, that is, only one execution at the Controller level.

Implementation 4:

Obviously, because the need to record abnormal logs is global, you can use the registration global filter to meet the need to streamline the code as much as possible. Add the following code when registering a filter in Gloabal:

Public static void RegisterGlobalFilters (GlobalFilterCollection filters) {filters. Add (new HandleErrorAttribute (); filters. Add (new LogExceptionAttribute ());}View Code

Implementation 2:

The error message returned in JSON format is not global, but only required by some specific actions. Therefore, an exception filter must be designed to return JSON information of exceptions. This filter only needs to act on the Action. According to the previous exception handling sequence, the internal and external principles are used to process the JSON exception filter before processing the previously defined LogExceptionAttribute, in this way, the JSON error message is returned and the exception log is recorded. The Code is as follows:

[AttributeUsage (AttributeTargets. Method, Inherited = true, AllowMultiple = false)] public class JsonExceptionAttribute: HandleErrorAttribute {public override void OnException (ExceptionContext filterContext) {if (! FilterContext. ExceptionHandled) {// return JSON filterContext. Result = new JsonResult {Data = new {Success = false, Message = filterContext. Exception. Message }};}}}View Code

In JsonExceptionAttribute, a new JsonResult object is generated and assigned to the returned result (of course, The JSON return format of the entire site must be unified here). At the same time, AttributeTargets is used. method specifies that the filter can only be used at the Method level, that is, the corresponding Action.

Note that you do not need to call base. onException. Otherwise, the LogExceptionAttribute will be skipped and the HandleErrorAttribute processing logic will be executed first, so that the returned result is not JsonResult, but ViewResult, and the client will not be able to process non-JSON results.

You do not need to set filterContext. ExceptionHandled = true here. Otherwise, when processing LogExceptionAttribute, because! The judgment condition of filterContext. ExceptionHandled. The logic of LogExceptionAttribute is not executed, and the exception logs are not recorded.

In use, you only need to declare this feature on the Action. The Code is as follows:

[HttpPost] [JsonException] public JsonResult Add (string ip, int port ){... // processing logic return Json (new {Success = true, Message = "added successfully "});}View Code

To work properly with JsonExceptionAttribute, LogExceptionAttribute also needs to be modified accordingly:

[AttributeUsage (AttributeTargets. Class, Inherited = true, AllowMultiple = false)] public class LogExceptionAttribute: HandleErrorAttribute {public override void OnException (ExceptionContext filterContext) {if (! FilterContext. exceptionHandled) {string controllerName = (string) filterContext. routeData. values ["controller"]; string actionName = (string) filterContext. routeData. values ["action"]; string msgTemplate = "An exception occurs when the controller [{0}] action [{1}] is executed."; LogManager. getLogger ("LogExceptionAttribute "). error (string. format (msgTemplate, controllerName, actionName), filterContext. exception);} if (filterContext. result is JsonResult) {// when the Result is in json format, the filterContext has been processed due to an exception. exceptionHandled = true;} else {// otherwise, the original base is called. onException (filterContext );}}}View Code

Note that before and after comparison, LogExceptionAttribute does not directly call base. OnException, but first checks whether the current returned result is JsonResult. If the returned result is JsonResult, it indicates that JsonExceptionAttribute has been processed. In this case, you need to set filterContext. exceptionHandled = true. The base class HandleErrorAttribute is no longer processed. If the returned result is not JsonResult, the base is called. onException: continue to execute the logic of the base class HandleErrorAttribute and switch to the error page.

To extend other types of exception handling, you only need to add the corresponding exception filter and perform corresponding transformation in LogExceptionAttribute.

Vi. Postscript

Add the above filters and make changes in the configuration file. All the exception handling requirements are fulfilled. If there is not much change, such a processing mode can be applied to the MVC site. In this mode, if there are no special requirements, no additional exception handling code is required for our controllers, logic layers, and data access layers. If exceptions are generated, they are directly thrown out, eventually, it will be intercepted and processed by the exception filter.

Even for specific requirements, you may need to add try {} catch {} to some code blocks for exception capture and handling, we also recommend that you still use the throw statement to throw an exception after the catch statement is processed. LogExceptionAttribute is used to capture and process the exception. This will greatly reduce the recurrence of try {} catch {} statements. Of course, the specific handling of exceptions will be adjusted based on the actual situation.




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.