The fun of refactoring is the streamlined code, modular design, decoupling capabilities ... And the reconstruction of exception processing just meet the above three aspects, here is my little bit of experience.
I. Related learning
In the article "Streamlining your own 20% code", the uniform processing of exceptions is discussed, and the encapsulation of exception handling is achieved by simplifying the code. There are two ways to handle this:
- Method 1: Encapsulate a method class that contains exception handling logic for try{}catch{}finally{}, pass another method as a parameter to the method, call the encapsulated method within the page, and the approximate code is as follows:
public class process{public static bool Execute (Action action) { Try { action}. Invoke (); return true; } catch (Exception ex) { return false; } Finally { }}}
- Method 2: Using attribute, for the method plus the characteristics of the unified interception of processing (the article does not seem to be detailed description)
Second, the application on the MVC site
Let's take a brief look at the previous knowledge:
In the standard three-tier structure, MVC corresponds to the presentation layer, where the action in the controller collects the data and passes the data to the business logic layer, and the logical layer gets the data and handles the corresponding business logic by invoking the data access layer, eventually returning the processed data to the model. Finally, the model is combined with the view to form a page to be viewed by the end user. Such as:
In accordance with the idea of Method 1, when invoking the logic layer method in action, wrap it with the exception handling method encapsulated, the code is as follows:
View Code
We can also use lambda to wrap more complex logic and not necessarily return a bool value:
View Code
This will include a large number of calls to the Process.execute method in the action, and if it is moved to the logical layer for encapsulation, there will be a lot of duplicated code in the logical layer, or not enough to be streamlined. And this code not only manually repeated writing, and the function of the logic layer is not decoupled, not the implementation of modularity. It is therefore necessary to continue refactoring.
Third, MVC filter
The idea of using attribute from Method 2 can easily think of the MVC filter, using the filter's interception function to implement exception handling in accordance with the AOP idea, and decouple the module from the logic layer. About the MVC filter introduction, many articles on the internet, recommended "MVC filter detailed". Here, the order of execution of the filter is emphasized.
- General Filter Execution Order
- Iauthorizationfilter->onauthorization (Authorized)
- Iactionfilter->onactionexecuting (behavior)
- Action
- Iactionfilter->onactionexecuted (behavior)
- Iresultfilter->onresultexecuting (Result)
- View
- Iresultfilter->onresultexecuted (Result)
- *iexceptionfilter->onexception (Exception), this method is not executed in the order above, there is an exception occurs when the execution, a bit similar to the interrupt
- When filters are set in both the Controller and the action, the order of execution is generally from outside to inside, i.e. "global", "Controller", "behavior"
- Controller->iauthorizationfilter->onauthorization
- Action->iauthorizationfilter->onauthorization
- Controller->iactionfilter->onactionexecuting
- Action->iactionfilter->onactionexecuting
- Action
- Action->iactionfilter->onactionexecuted
- Controller->iactionfilter->onactionexecuted
- Controller->iresultfilter->onresultexecuting
- Action->iresultfilter->onactionexecuting
- Action->iresultfilter->onactionexecuted
- Controller->iresultfilter->onactionexecuted
- Because the exception is thrown out from inside, because the order of the second exception is just the opposite, usually from inside to outside, that is, "behavior", "Controller", "global"
- Action->iexceptionfilter->onexception
- Controller->iexceptionfilter->onexception
Iv. system-brought exception handling
The filter we used to use is either adding attribute to the action or adding attribute to the controller. What's going on with the global filter mentioned above? First look at the code in Gloabal:
View Code
It is known that the global filter has been registered when the application is started, and Handleerrorattribute is the exception filter that comes with the system. In this registered global filter, you can not go to each controller or each action to declare, directly acting on the global, that is, you can catch all the exceptions of the entire site. See how its source code handles exceptions:
View Code
In Handleerrorattribute's exception handling logic, a model of the Handleerrorinfo class is generated and the returned result is set to a newly generated viewresult. The default viewname for this view is error, which corresponds to the error view in the share folder. The error view is not used in the Handleerrorinfo model, so the information disclosed is not many, can be modified according to the specific needs. For example:
View Code
This filter will work and need to be configured in the configuration file:
<customerrors mode= "on"/>
Five, custom exception unified processing
Before implementing the uniform processing of exceptions, make a clear request:
- All pages in the site after the exception, you need to record the Exception log and turn to the error page (the level of detailed slightly of the exception is determined by the specific requirements)
- All asynchronous requests that return JSON data require not only logging the Exception log, but also returning the JSON-formatted error message to the client instead of turning to the error page (the asynchronous request may not be able to turn to the error prompt page)
- Using AOP to understand the decoupling of anomalies
- Try to streamline the duplicate code that declares attribute
Implementations 1 and 3:
Because the Exception log is required for the entire site, it is necessary to design an exception logging filter (Logexceptionattribute) for interception, which embodies both the AOP idea and the need to understand the decoupling. The code is as follows:
View Code
Logexceptionattribute inherits the Handleerrorattribute, overriding the Onexception method after logging the exception log by calling base. Onexception back to the system default exception handling, the implementation of the error page to jump.
Logexceptionattribute set its own AttributeUsage feature, AttributeTargets.Class specifies that the filter can only be used for class one level, that is, controller;allowmultiple = False setting does not allow multiple executions, which are performed only once at the controller level.
Implementation 4:
Obviously, because the requirements for logging the Exception Log are global, using a registered global filter will satisfy the need to streamline the code as much as possible. Add the following code when registering the filter Gloabal:
View Code
Implementation 2:
The error message returned in JSON format is not global, only required for certain actions, so it is necessary to design an exception filter that specifically returns the JSON information for the exception. This filter should only be used for action. According to the previous exception processing order, the principle of the first and the outside, in handling the exception, the JSON exception filter is processed first, and then processed the previously defined logexceptionattribute, so that the return of the JSON error message, and log the Exception log. The code is as follows:
View Code
A new Jsonresult object is generated in the Jsonexceptionattribute and assigned to the return result (and, of course, it is also necessary to unify the JSON return format for the entire site) At the same time, it is specified by AttributeTargets.Method that the filter can only be used at the method level, which corresponds to action.
It is important to note that you do not need to call base. Onexception, otherwise it skips Logexceptionattribute first to execute Handleerrorattribute's processing logic, thus returning the result is no longer jsonresult, but Viewresult, The client will not be able to handle non-JSON results.
There is also no need to set filtercontext.exceptionhandled = True, otherwise in logexceptionattribute processing, because the!filtercontext.exceptionhandled judgment condition , the Logexceptionattribute logic does not execute, and the exception log is not logged.
When used, you only need to declare this feature on the action. The code is as follows:
View Code
In order to cope with the normal operation of Jsonexceptionattribute, Logexceptionattribute also need to make corresponding changes:
View Code
Note Before and after comparison, in the Logexceptionattribute will not directly call Base.onexception, but first to determine whether the current return is not jsonresult. The returned result is Jsonresult, which indicates that the Jsonexceptionattribute was previously processed and that the setting filtercontext.exceptionhandled = True is required. And does not continue the processing of base class Handleerrorattribute; The return result is not jsonresult, then base is called. Onexception, continue to execute the logic of the base class Handleerrorattribute and turn to the error page.
If you need to extend other types of exception handling, you only need to add the corresponding exception filter, and the corresponding transformation in the Logexceptionattribute.
Vi. PostScript
Adding the above filter and changing it with the configuration file, our exception handling a few requirements are complete. If there is not much change, such a processing mode can be common to the MVC site. In this mode, if there is no special need, in our controller, logic layer, data access layer, etc., there is no need to add additional exception handling code, resulting in an exception after the direct extrapolation, will eventually be blocked by the exception filter and processed.
Even for specific requirements, it may be necessary to catch and handle some code blocks with try{}catch{}, and it is also recommended that the throw statement be used to throw exceptions after processing in the catch statement. Unified by the Logexceptionattribute to carry out the final capture and processing. This will massively reduce the repetition of the try{}catch{} statement. Of course, the specifics of how the exception is handled will be adjusted as appropriate.
MVC filter Use Case: uniformly handling exceptions by streamlining code