Unified exception handling, assertion and enumeration combined use cases

In the process of software development, it is inevitable to deal with all kinds of exceptions. For myself, at least more than half of the time is dealing with all kinds of exceptions, so there will be a lot of try {...} in the code catch {...} finally {...} Code blocks not only have a large number of redundant code, but also affect the readability of the code.

Compare the following two figures to see which style of code you are writing now? Then which coding style do you prefer?

Ugly try catch code block:

Elegant Controller:

The above example is only in the Controller layer. If it is in the Service layer, there may be more try catch code blocks. This will seriously affect the readability and "Aesthetics" of the code.

So if it's me, I definitely prefer the second one. I can focus more on the development of business code, and the code will become more concise at the same time.

Since the business code does not explicitly capture and handle exceptions, exceptions must be handled. Otherwise, the system will crash all the time. Therefore, there must be other places to capture and handle these exceptions.

So the question is, how to handle all kinds of exceptions gracefully?

What is unified exception handling

Spring has added an annotation @ ControllerAdvice in version 3.2, which can be used together with @ ExceptionHandler, @ InitBinder, @ ModelAttribute and other annotations.

I won't elaborate on the functions of these annotations here. If you don't understand them, you can refer to the new annotation @ ControllerAdvice of Spring 3.2 for a general understanding.

However, only the annotation @ ExceptionHandler is related to exception handling, which literally means exception handler.

Its actual function is also: if an exception handling method is defined in a Controller class and the annotation is added to the method, the exception handling method will be executed when the specified exception occurs.

It can use the data binding provided by spring MVC, such as injecting HttpServletRequest, and can also accept a Throwable object currently thrown.

However, in this way, a set of such exception handling methods must be defined in each Controller class, because exceptions can be various. In this way, there will be a lot of redundant code, and if you need to add an exception handling logic, you must modify all Controller classes, which is not elegant.

Of course, you might say, then define a base class similar to BaseController.

Although this approach is correct, it is still not perfect, because such code is invasive and coupling. Simple Controller, why do I have to inherit such a class? What if I have inherited other base classes. As we all know, Java can only inherit one class.

Is there a solution that does not need to be coupled with the Controller, and can also apply the defined exception handler to all controllers?

So the @ ControllerAdvice annotation appears. In short, it can apply exception handlers to all controllers instead of a single controller.

With the help of this annotation, we can define a set of handling mechanisms for various exceptions in an independent place, such as a single class, and then add the annotation @ ControllerAdvice to the signature of the class to handle different exceptions at different stages. This is the principle of unified exception handling.

Note that the exceptions are classified by stages, which can be roughly divided into: exceptions before entering the Controller and Service layer exceptions.

Refer to the following figure for details:

Anomalies at different stages

target

Eliminate more than 95% of the try catch code blocks, verify the business exceptions in an elegant Assert way, and only focus on the business logic without spending a lot of energy on writing redundant try catch code blocks.

Unified exception handling practice

Before defining a unified exception handling class, let's introduce how to gracefully determine the exception and throw the exception.

|Replace throw exception with Assert

You must be familiar with assert, such as org. Com of the Spring family springframework. util. Assert is often used when we write test cases.

Using assertions can make us feel very silky when coding, such as:

@Test
    public void test1() {
        ...
        User user = userDao.selectById(userId);
        Assert.notNull(user, "user does not exist.");
        ...
    }

    @Test
    public void test2() {
        //Another way of writing
        User user = userDao.selectById(userId);
        if (user == null) {
            throw new IllegalArgumentException("user does not exist.");
        }
    }

Do you feel that the first way to judge non empty is very elegant, and the second way is relatively ugly if {...} Code block. So magical assert What exactly does notnull () do?

The following is part of the source code of Assert:

public abstract class Assert {
    public Assert() {
    }

    public static void notNull(@Nullable Object object, String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }
}

As you can see, Assert is actually helping us put if {...} Encapsulated, isn't it amazing? Although it is very simple, it is undeniable that the coding experience has improved at least one level.

So can we imitate org springframework. util. Assert also writes an assertion class, but the exceptions thrown after assertion failure are not built-in exceptions such as IllegalArgumentException, but exceptions defined by ourselves.

Let's try:

Assert
public interface Assert {
    /**
     * Create exception
     * @param args
     * @return
     */
    BaseException newException(Object... args);

    /**
     * Create exception
     * @param t
     * @param args
     * @return
     */
    BaseException newException(Throwable t, Object... args);

    /**
     * <p>Assert that the object < code > obj < / code > is not empty. If the object < code > obj < / code > is empty, an exception is thrown
     *
     * @param obj Object to be judged
     */
    default void assertNotNull(Object obj) {
        if (obj == null) {
            throw newException(obj);
        }
    }

    /**
     * <p>Assert that the object < code > obj < / code > is not empty. If the object < code > obj < / code > is empty, an exception is thrown
     * <p>Exception message < code > message < / code > supports parameter passing to avoid string splicing before judgment
     *
     * @param obj Object to be judged
     * @param args message Parameter list corresponding to placeholder
     */
    default void assertNotNull(Object obj, Object... args) {
        if (obj == null) {
            throw newException(args);
        }
    }
}

 

The above Assert assertion method is defined using the default method of the interface. Then, do you find that the exception thrown after the assertion fails is not a specific exception, but provided by two newException interface methods.

Because the exceptions in the business logic basically correspond to specific scenarios. For example, the user information is obtained according to the user id, and the query result is null. At this time, the exception thrown may be UserNotFoundException, with specific exception code (such as 7001) and exception information "user does not exist".

Therefore, the specific exception thrown is determined by the implementation class of Assert.

See here, you may have such a question. According to the above statement, it is not necessary to define the same number of assertion classes and exception classes for how many exceptions there are. This is obviously anti-human, which is not as smart as expected. Don't worry, just listen to me carefully.

|Understanding Enum

The custom exception BaseException has two attributes, namely code and message. Is it conceivable that any class generally defines these two attributes?

Yes, enumeration class. Let's see how I combine Enum and Assert. I believe I will brighten your eyes.

As follows:

public interface IResponseEnum {
    int getCode();
    String getMessage();
}
/**
 * <p>Business exception</p>
 * <p>When an exception occurs during business processing, the exception can be thrown</p>
 */
public class BusinessException extends  BaseException {

    private static final long serialVersionUID = 1L;

    public BusinessException(IResponseEnum responseEnum, Object[] args, String message) {
        super(responseEnum, args, message);
    }

    public BusinessException(IResponseEnum responseEnum, Object[] args, String message, Throwable cause) {
        super(responseEnum, args, message, cause);
    }
}
public interface BusinessExceptionAssert extends IResponseEnum, Assert {

    @Override
    default BaseException newException(Object... args) {
        String msg = MessageFormat.format(this.getMessage(), args);

        return new BusinessException(this, args, msg);
    }

    @Override
    default BaseException newException(Throwable t, Object... args) {
        String msg = MessageFormat.format(this.getMessage(), args);

        return new BusinessException(this, args, msg, t);
    }

}
@Getter
@AllArgsConstructor
public enum ResponseEnum implements BusinessExceptionAssert {

    /**
     * Bad licence type
     */
    BAD_LICENCE_TYPE(7001, "Bad licence type."),
    /**
     * Licence not found
     */
    LICENCE_NOT_FOUND(7002, "Licence not found.")
    ;

    /**
     * Return code
     */
    private int code;
    /**
     * Return message
     */
    private String message;
}

See here, do you have a bright feeling? Two enumeration instances are defined in the code example:

  • BAD_LICENCE_TYPE

  • LICENCE_NOT_FOUND

They correspond to badlicensetype exception and licensenotfoundexception respectively.

You don't need to enumerate an exception, and you only need to add one exception class for each exception. Then let's see how to use it. Suppose that the licenseservice has a method to verify whether the license exists, as follows:

/**
     * Verify that {@ link} license} exists
     * @param licence
     */
    private void checkNotNull(Licence licence) {
        ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
    }

If you do not use assertions, the code might be as follows:

private void checkNotNull(Licence licence) {
        if (licence == null) {
            throw new LicenceNotFoundException();
            //Or so
            throw new BusinessException(7001, "Bad licence type.");
        }
    }

Using enumeration classes in combination with (inheriting) Assert, you only need to define different enumeration instances according to specific exceptions, such as bad above_ LICENCE_ TYPE,LICENCE_NOT_FOUND, you can throw specific exceptions for different situations (this refers to carrying specific exception codes and exception messages).

In this way, there is no need to define a large number of exception classes, and at the same time, it has good readability of assertions. Of course, the benefits of this scheme are far more than these. Please continue to read the following articles and experience it slowly.

Note: the above examples are for specific services, and some exceptions are common, such as server busy, network exception, server exception, parameter verification exception, 404, etc. Therefore, there are CommonResponseEnum, ArgumentResponseEnum and ServletResponseEnum, of which ServletResponseEnum will be described in detail later.

|Define a unified exception handler class

@Slf4j
@Component
@ControllerAdvice
@ConditionalOnWebApplication
@ConditionalOnMissingBean(UnifiedExceptionHandler.class)
public class UnifiedExceptionHandler {
    /**
     * production environment 
     */
    private final static String ENV_PROD = "prod";

    @Autowired
    private UnifiedMessageSource unifiedMessageSource;

    /**
     * Current environment
     */
    @Value("${spring.profiles.active}")
    private String profile;

    /**
     * Get internationalization message
     *
     * @param e abnormal
     * @return
     */
    public String getMessage(BaseException e) {
        String code = "response." + e.getResponseEnum().toString();
        String message = unifiedMessageSource.getMessage(code, e.getArgs());

        if (message == null || message.isEmpty()) {
            return e.getMessage();
        }

        return message;
    }

    /**
     * Business exception
     *
     * @param e abnormal
     * @return Abnormal result
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public ErrorResponse handleBusinessException(BaseException e) {
        log.error(e.getMessage(), e);

        return new ErrorResponse(e.getResponseEnum().getCode(), getMessage(e));
    }

    /**
     * Custom exception
     *
     * @param e abnormal
     * @return Abnormal result
     */
    @ExceptionHandler(value = BaseException.class)
    @ResponseBody
    public ErrorResponse handleBaseException(BaseException e) {
        log.error(e.getMessage(), e);

        return new ErrorResponse(e.getResponseEnum().getCode(), getMessage(e));
    }

    /**
     * Controller Related abnormality of upper layer
     *
     * @param e abnormal
     * @return Abnormal result
     */
    @ExceptionHandler({
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            // BindException.class,
            // MethodArgumentNotValidException.class
            HttpMediaTypeNotAcceptableException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            MissingServletRequestPartException.class,
            AsyncRequestTimeoutException.class
    })
    @ResponseBody
    public ErrorResponse handleServletException(Exception e) {
        log.error(e.getMessage(), e);
        int code = CommonResponseEnum.SERVER_ERROR.getCode();
        try {
            ServletResponseEnum servletExceptionEnum = ServletResponseEnum.valueOf(e.getClass().getSimpleName());
            code = servletExceptionEnum.getCode();
        } catch (IllegalArgumentException e1) {
            log.error("class [{}] not defined in enum {}", e.getClass().getName(), ServletResponseEnum.class.getName());
        }

        if (ENV_PROD.equals(profile)) {
            //When it is a production environment, it is not suitable to display specific exception information to users, such as 404
            code = CommonResponseEnum.SERVER_ERROR.getCode();
            BaseException baseException = new BaseException(CommonResponseEnum.SERVER_ERROR);
            String message = getMessage(baseException);
            return new ErrorResponse(code, message);
        }

        return new ErrorResponse(code, e.getMessage());
    }


    /**
     * Parameter binding exception
     *
     * @param e abnormal
     * @return Abnormal result
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public ErrorResponse handleBindException(BindException e) {
        log.error("Parameter binding verification exception", e);

        return wrapperBindingResult(e.getBindingResult());
    }

    /**
     * Parameter verification exception, which combines all exceptions that failed verification into an error message
     *
     * @param e abnormal
     * @return Abnormal result
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public ErrorResponse handleValidException(MethodArgumentNotValidException e) {
        log.error("Parameter binding verification exception", e);

        return wrapperBindingResult(e.getBindingResult());
    }

    /**
     * Packaging binding exception result
     *
     * @param bindingResult Binding result
     * @return Abnormal result
     */
    private ErrorResponse wrapperBindingResult(BindingResult bindingResult) {
        StringBuilder msg = new StringBuilder();

        for (ObjectError error : bindingResult.getAllErrors()) {
            msg.append(", ");
            if (error instanceof FieldError) {
                msg.append(((FieldError) error).getField()).append(": ");
            }
            msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());

        }

        return new ErrorResponse(ArgumentResponseEnum.VALID_ERROR.getCode(), msg.substring(2));
    }

    /**
     * Undefined exception
     *
     * @param e abnormal
     * @return Abnormal result
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ErrorResponse handleException(Exception e) {
        log.error(e.getMessage(), e);

        if (ENV_PROD.equals(profile)) {
            //When it is a production environment, it is not suitable to display specific exception information to users, such as database exception information
            int code = CommonResponseEnum.SERVER_ERROR.getCode();
            BaseException baseException = new BaseException(CommonResponseEnum.SERVER_ERROR);
            String message = getMessage(baseException);
            return new ErrorResponse(code, message);
        }

        return new ErrorResponse(CommonResponseEnum.SERVER_ERROR.getCode(), e.getMessage());
    }

}

As you can see, exceptions are divided into several categories above. In fact, there are only two categories: ServletException and ServiceException.

Remember the classification by stage mentioned above, that is, corresponding to the exceptions before entering the Controller and Service layer exceptions; Then ServiceException is divided into custom exception and unknown exception.

The corresponding relationship is as follows:

  • Exceptions before entering the Controller: handleServletException, handleBindException, handlevallexception

  • Custom exception: handlebusinesexception, handleBaseException

  • Unknown exception: handleException

Next, these exception handlers are described in detail.

 

Exception handler description

| handleServletException

Before an http request reaches the Controller, a series of checks will be made between the request information of the request and the information of the target Controller.

Here is a brief introduction:

NoHandlerFoundException: first, find out whether there is a corresponding controller according to the request Url. If not, the exception will be thrown, that is, the familiar 404 exception.

HttpRequestMethodNotSupportedException: if it matches (the matching result is a list, but the http method is different, such as Get, Post, etc.), try to match the requested http method with the controller of the list. If there is no controller corresponding to the http method, throw the exception.

HttpMediaTypeNotSupportedException: then compare the request header with that supported by the controller.

For example, in the content type request header, if the parameter signature of the controller contains the annotation @ RequestBody, but the value of the requested content type request header does not contain application/json, this exception will be thrown (of course, this exception will be thrown in more than this case).

MissingPathVariableException: no path parameters detected. For example, the url is: / licence/{licenceId}, and the parameter signature contains @ PathVariable("licenceId").

When the requested url is / license, if the url is not clearly defined as / license, it will be determined that the path parameter is missing.

MissingServletRequestParameterException: missing request parameter. For example, if the parameter @ RequestParam("licenceId") String licenceId is defined, but the parameter is not carried when the request is initiated, the exception will be thrown.

Typemismatch exception: parameter type matching failed. For example: if the receiving parameter is Long, but the value passed in is a string, the type conversion will fail, and this exception will be thrown.

HttpMessageNotReadableException: it is completely opposite to the example of HttpMediaTypeNotSupportedException above.

That is, if the request header carries "content type: application / json; charset = UTF-8", but the receiving parameter does not add the annotation @ RequestBody, or the json string carried by the request body fails in the process of reverse sequencing into pojo, this exception will also be thrown.

HttpMessageNotWritableException: if the returned pojo fails in the process of serialization into json, throw the exception.

| handleBindException

Parameter verification is abnormal, which will be described in detail later.

| handleValidException

Parameter verification is abnormal, which will be described in detail later.

| handleBusinessException,handleBaseException

Handle custom business exceptions, except that handleBaseException handles all business exceptions except business exception. At present, these two can be combined into one.

| handleException

Handle all unknown exceptions, such as those that failed to operate the database.

Note: the above handleServletException and handleException processors may return different exception information in different environments. They think that these exception information is the exception information of the framework, which is generally in English and is not easy to show directly to users, so they return server uniformly_ Error stands for exception information.

404 different from ordinary people

As mentioned above, when the request does not match the controller, a NoHandlerFoundException exception will be thrown, but this is not the case by default. By default, a page similar to the following will appear:

Whitelabel Error Page

How does this page appear? In fact, when 404 occurs, the default is not to throw exceptions, but to jump forward to the / error controller.

Spring also provides a default error controller, as follows:

Then, how to make 404 throw exceptions? Just add the following configuration in the properties file:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

In this way, it can be captured in the exception handler, and then as long as the front end captures a specific status code, it can immediately jump to page 404.

Catch the exception corresponding to 404

|Unified return results

Before verifying the unified exception handler, by the way, the unified return result. To put it bluntly, it is actually to unify the data structure of the returned results.

code and message are necessary fields in all returned results. When data needs to be returned, another field data is required to represent it.

Therefore, first define a BaseResponse as the base class of all returned results; Then define a general return result class CommonResponse, which inherits BaseResponse and has the field data.

In order to distinguish the return results of success and failure, an ErrorResponse is defined.

Finally, there is a common return result, that is, the returned data has paging information. Because this interface is common, it is necessary to define a return result class QueryDataResponse separately.

This class inherits from CommonResponse and only limits the type of data field to QueryDdata. QueryDdata defines the corresponding fields of paging information, namely totalCount, pageNo, pageSize and records.

Among them, only CommonResponse and QueryDataResponse are commonly used, but the name is long. Why not define two super simple classes to replace it?

So r and QR were born. When returning results in the future, you only need to write: new R < > (data) and new QR < > (querydata).

The definitions of all return result classes will not be posted here.

|Verify unified exception handling

Because this set of unified exception handling can be said to be universal, all can be designed into a common package. In the future, each new project / module only needs to introduce this package. Therefore, in order to verify, you need to create a new project and introduce the common package.

The following is the main source code for verification:

@Service
public class LicenceService extends ServiceImpl<LicenceMapper, Licence> {

    @Autowired
    private OrganizationClient organizationClient;

    /**
     * Query {@ link} license} details
     * @param licenceId
     * @return
     */
    public LicenceDTO queryDetail(Long licenceId) {
        Licence licence = this.getById(licenceId);
        checkNotNull(licence);

        OrganizationDTO org = ClientUtil.execute(() -> organizationClient.getOrganization(licence.getOrganizationId()));
        return toLicenceDTO(licence, org);
    }

    /**
     * Paging acquisition
     * @param licenceParam Paging query parameters
     * @return
     */
    public QueryData<SimpleLicenceDTO> getLicences(LicenceParam licenceParam) {
        String licenceType = licenceParam.getLicenceType();
        LicenceTypeEnum licenceTypeEnum = LicenceTypeEnum.parseOfNullable(licenceType);
        //Assertion, non empty
        ResponseEnum.BAD_LICENCE_TYPE.assertNotNull(licenceTypeEnum);

        LambdaQueryWrapper<Licence> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Licence::getLicenceType, licenceType);
        IPage<Licence> page = this.page(new QueryPage<>(licenceParam), wrapper);
        return new QueryData<>(page, this::toSimpleLicenceDTO);
    }

    /**
     * Add {@ link} license}
     * @param request Request body
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public LicenceAddRespData addLicence(LicenceAddRequest request) {
        Licence licence = new Licence();
        licence.setOrganizationId(request.getOrganizationId());
        licence.setLicenceType(request.getLicenceType());
        licence.setProductName(request.getProductName());
        licence.setLicenceMax(request.getLicenceMax());
        licence.setLicenceAllocated(request.getLicenceAllocated());
        licence.setComment(request.getComment());
        this.save(licence);

        return new LicenceAddRespData(licence.getLicenceId());
    }

    /**
     * entity -> simple dto
     * @param licence {@link Licence} entity
     * @return {@link SimpleLicenceDTO}
     */
    private SimpleLicenceDTO toSimpleLicenceDTO(Licence licence) {
        //Omit
    }

    /**
     * entity -> dto
     * @param licence {@link Licence} entity
     * @param org {@link OrganizationDTO}
     * @return {@link LicenceDTO}
     */
    private LicenceDTO toLicenceDTO(Licence licence, OrganizationDTO org) {
        //Omit
    }

    /**
     * Verify that {@ link} license} exists
     * @param licence
     */
    private void checkNotNull(Licence licence) {
        ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
    }

}

PS: the DAO framework used here is mybatis plus.

At startup, the data automatically inserted is:

-- licence
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (1, 1, 'user','CustomerPro', 100,5);
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (2, 1, 'user','suitability-plus', 200,189);
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (3, 2, 'user','HR-PowerSuite', 100,4);
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (4, 2, 'core-prod','WildCat Application Gateway', 16,16);

-- organizations
INSERT INTO organization (id, name, contact_name, contact_email, contact_phone)
VALUES (1, 'customer-crm-co', 'Mark Balster', 'mark.balster@custcrmco.com', '823-555-1212');
INSERT INTO organization (id, name, contact_name, contact_email, contact_phone)
VALUES (2, 'HR-PowerSuite', 'Doug Drewry','doug.drewry@hr.com', '920-555-1212');

Start validation

|Catch custom exception

① Get license details that do not exist: http://localhost:10000/licence/5

Successfully responded requests:

licenceId=1

Inspection is not empty:

Catch license not found exception:

Licence not found:

② Get the license list according to the nonexistent license type: http://localhost:10000/licence/list?licenceType=ddd . The optional license types are user and core prod.

Check is not empty:

Bad license type exception caught:

Bad licence type:

|Catch exceptions before entering the Controller

① Access interface that does not exist: http://localhost:10000/licence/list/ddd

Catch 404 exception:

② HTTP method does not support: http://localhost:10000/licence

PostMapping:

Catch Request method not supported exception:

Request method not supported:

③ Verification exception 1: http://localhost:10000/licence/list?licenceType=

getLicences:

LicenceParam:

Capture parameter binding verification exception:

licence type cannot be empty:

④ Verification exception 2: post request. postman simulation is used here

addLicence:

LicenceAddRequest:

The request url is the result:

Capture parameter binding verification exception:

Note: because the method of obtaining the exception information of parameter binding verification exception is different from other exceptions, the exceptions in these two cases are separated from the exceptions before entering the Controller. The following is the collection logic of exception information.

Collection of abnormal information:

|Catch unknown exception

Let's add a new field to the license table without modifying the structure of the database: http://localhost:10000/licence/1 .

Add test field:

Catch database exception:

Error querying database:

Summary

It can be seen that the exceptions of the test can be caught and returned in the form of code and message.

For each project / module, when defining business exceptions, you only need to define an enumeration class, then implement the interface BusinessExceptionAssert, and finally define the corresponding enumeration instance for each business exception, instead of defining many exception classes. It is also very convenient to use. The usage is similar to assertion.

extend

In the production environment, if an unknown exception or ServletException is caught, because it is a long string of exception information. If it is directly displayed to the user, it is not professional enough. Therefore, we can do this: when it is detected that the current environment is the production environment, we can directly return the "network exception".

The production environment returns "network exception":

You can modify the current environment in the following ways:

summary

With the combination of assertion and enumeration classes and unified exception handling, most exceptions can be caught.

Why do we say most exceptions? When spring cloud security is introduced, there will be authentication / authorization exceptions, service degradation exceptions of gateway, cross module call exceptions, remote call exceptions of third-party services, etc. the capture methods of these exceptions are different from those described in this article, but they are limited to space. There will be a separate article in the future.

In addition, when internationalization needs to be considered, the exception information after exception capture generally cannot be returned directly and needs to be converted into the corresponding language. However, this paper has considered this. Internationalization mapping has been done when obtaining messages. The logic is as follows:

Get internationalization message

Finally, I conclude that the global anomaly belongs to the topic of long-term discussion. I hope this mobile phone project can provide some guiding learning for you, and you can modify it according to the actual situation.

The following jsonResult object can also be used for processing, and the code can also be posted:

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * not logged on
     * @param request
     * @param response
     * @param e
     * @return
     */
    @ExceptionHandler(NoLoginException.class)
    public Object noLoginExceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
    {
        log.error("[GlobalExceptionHandler][noLoginExceptionHandler] exception",e);
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode(JsonResultCode.NO_LOGIN);
        jsonResult.setMessage("User login failure or login timeout,Please login first");
        return jsonResult;
    }

    /**
     * Business exception
     * @param request
     * @param response
     * @param e
     * @return
     */
    @ExceptionHandler(ServiceException.class)
    public Object businessExceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
    {
        log.error("[GlobalExceptionHandler][businessExceptionHandler] exception",e);
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode(JsonResultCode.FAILURE);
        jsonResult.setMessage("Business exception,Please contact the administrator");
        return jsonResult;
    }

    /**
     * Global exception handling
     * @param request
     * @param response
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Object exceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
    {
        log.error("[GlobalExceptionHandler][exceptionHandler] exception",e);
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode(JsonResultCode.FAILURE);
        jsonResult.setMessage("System error,Please contact the administrator");
        return jsonResult;
    }
}

-------------  END  -------------

Keywords: Java Spring RESTful

Added by Ammar on Sat, 05 Mar 2022 00:21:16 +0200