Unified Exception Handling in Spring Cloud with @RestControllerAdvice

This article demonstrates how to consolidate exception handling in a Spring Cloud microservice using @RestControllerAdvice, @Slf4j, and @ExceptionHandler, defines a unified error code enum, and provides test scenarios with sample code and API requests.

Wukong Talks Architecture
Wukong Talks Architecture
Wukong Talks Architecture
Unified Exception Handling in Spring Cloud with @RestControllerAdvice

Unified Exception Handling

1. Background

When writing code we often add try‑catch blocks inside methods, which leads to a lot of duplicated code; therefore we can integrate a unified exception handling mechanism to improve code structure.

2. Custom Exception Handler Class

Add the unified exception handling class annotation @RestControllerAdvice Add logging annotation @Slf4j Add exception handling method annotation

@ExceptionHandler
package com.jackson0714.passjava.question.exception;

/*
 * Centralized handling of all exceptions
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.jackson0714.passjava.question.controller")
public class PassjavaExceptionControllerAdvice {

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e) {
        log.error("Validation error: {}, exception type: {}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach(fieldError -> {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(), BizCodeEnum.VALID_EXCEPTION.getMsg())
                .put("data", errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable) {
        log.error("Unknown exception: {}, exception type: {}", throwable.getMessage(), throwable.getClass());
        return R.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(), BizCodeEnum.UNKNOWN_EXCEPTION.getMsg());
    }
}

3. Recommended System Error Codes

1. Error code and message definition

Length of error code: 5 digits

First two digits: business scenario

Last three digits: specific error

10: General business
    001: Parameter format validation error (10001)
11: Member business
12: Question business
13: Content business
14: Learning business

2. Error code enum class

Defines two exception enums: unknown system exception and parameter format validation failure.

package com.jackson0714.common.exception;

public enum BizCodeEnum {
    UNKNOWN_EXCEPTION(10000, "System unknown exception"),
    VALID_EXCEPTION(10001, "Parameter format validation failed");

    private int code;
    private String msg;
    BizCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public int getCode() { return code; }
    public String getMsg() { return msg; }
}

4. Test Code

Test scenario 1: Validate that the parameter displayOrder must be a positive integer; if it is not, an exception is thrown.

1. Add validation annotation @Positive on the entity field.

/**
 * Order
 */
@Positive
private Integer displayOrder;

2. Add a save method in the controller and annotate the parameter with @Valid.

/**
 * Save
 */
@RequestMapping("/save")
public R save(@Valid @RequestBody QuestionEntity question) {
    questionService.save(question);
    return R.ok();
}

Test with Postman:

Request URL:

http://192.168.10.160:8060/api/question/v1/admin/question/save

Request body:

{
    "displayOrder": 0.2
}

Response:

{
    "msg": "Parameter format validation failed",
    "code": 10001,
    "data": {
        "displayOrder": "must be a positive number"
    }
}
Test scenario 2: Directly thrown exceptions in code can also be handled by the unified handler.

1. Add an info method in the controller that throws Exception.

/**
 * Info
 */
@RequestMapping("/info/{id}")
public R info(@PathVariable("id") Long id) throws Exception {
    QuestionEntity question = questionService.getById(id);
    throw new Exception("test");
    // return R.ok().put("question", question);
}

Test with Postman (same URL as above) returns:

{
    "msg": "System unknown exception",
    "code": 10000
}

This proves that the @ExceptionHandler for Throwable is invoked:

@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable) {
    return R.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(), BizCodeEnum.UNKNOWN_EXCEPTION.getMsg());
}

5. Code Repository

Source code is available at https://github.com/Jackson0714/PassJava-Platform

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaMicroservicesException HandlingSpring Cloud
Wukong Talks Architecture
Written by

Wukong Talks Architecture

Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.