Backend Development 19 min read

Comprehensive Guide to Interface Exception Handling in Spring Boot

This article explains how to classify interface exceptions, implement unified handling using Spring Boot's @ControllerAdvice and custom ErrorController, and provides practical code examples for business, system, client, and filter exceptions, including redirect and error page strategies.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Comprehensive Guide to Interface Exception Handling in Spring Boot

Preface

Hello everyone! I am sum墨, a frontline backend developer who enjoys researching and documenting technical issues. With six years of experience developing Java backend systems, I have designed multi‑tenant architectures, integrated various open platforms, and built complex message centers, while never encountering a production‑critical crash.

Bug handling is familiar to programmers; a bug often leads to an exception, but not every exception originates from a bug. This article focuses on how to handle interface‑related exceptions.

1. Classification of Interface Exceptions

In interface design, avoid using exceptions for control flow; return clear error codes and messages instead.

1. Business Exception

These are expected errors such as parameter validation failures or insufficient permissions. They are marked as expected and can provide specific error codes and messages to the caller.

2. System Exception

These are unexpected errors like database or network failures. They are marked as unknown , should be logged, and wrapped into a generic response rather than exposing raw details to the user.

3. Client Exception

These arise from the caller, e.g., wrong request parameters or timeouts. They are indicated by client error ; the interface itself is fine and may optionally return an error message.

2. Common Handling Methods

1. Exception Capture and Processing

Use try‑catch in the implementation code, convert exceptions into appropriate error codes and messages, and optionally log the exception and notify system administrators.

2. Global Exception Handler

Define a global handler with @ControllerAdvice and @ExceptionHandler in Spring Boot. This centralizes exception handling, converting them into unified error responses.

3. Throw Custom Exceptions

Define custom exception classes (e.g., extending RuntimeException ) and throw them where business logic requires specific handling.

4. Return Error Codes and Messages

Establish a convention of error codes and messages; the client can use them to display appropriate UI, such as a popup.

Example popup illustration:

5. Redirect to Custom Error Page

Customize error pages for status codes like 401, 404, 500, and guide users back to correct operations.

Incorrect default error page example:

3. Unified Interface Exception Handling

To centralize exception reception and processing, use Spring Boot's @ControllerAdvice as a global exception handler. However, @ControllerAdvice cannot handle exceptions thrown in Filter layers.

For @ControllerAdvice , only exceptions occurring in the controller layer are intercepted. Exceptions from Filters occur before the controller, so a separate handling mechanism is required.

1. Using @ControllerAdvice Global Exception Handler

(1) Custom Business Exception

Define business error codes in an enum:

package com.summo.demo.model.response;

public enum ResponseCodeEnum {
    /**
     * Request succeeded
     */
    SUCCESS("0000", ErrorLevels.DEFAULT, ErrorTypes.SYSTEM, "请求成功"),
    /**
     * Login related exception
     */
    LOGIN_USER_INFO_CHECK("LOGIN-0001", ErrorLevels.INFO, ErrorTypes.BIZ, "用户信息错误"),
    /**
     * Permission related exception
     */
    NO_PERMISSIONS("PERM-0001", ErrorLevels.INFO, ErrorTypes.BIZ, "用户无权限"),
    /**
     * Business related exception
     */
    BIZ_CHECK_FAIL("BIZ-0001", ErrorLevels.INFO, ErrorTypes.BIZ, "业务检查异常"),
    BIZ_STATUS_ILLEGAL("BIZ-0002", ErrorLevels.INFO, ErrorTypes.BIZ, "业务状态非法"),
    BIZ_QUERY_EMPTY("BIZ-0003", ErrorLevels.INFO, ErrorTypes.BIZ, "查询信息为空"),
    /**
     * System error
     */
    SYSTEM_EXCEPTION("SYS-0001", ErrorLevels.ERROR, ErrorTypes.SYSTEM, "系统出错啦,请稍后重试"),
    ;

    private final String code;
    private final String errorLevel;
    private final String errorType;
    private final String description;

    ResponseCodeEnum(String code, String errorLevel, String errorType, String description) {
        this.code = code;
        this.errorLevel = errorLevel;
        this.errorType = errorType;
        this.description = description;
    }

    public String getCode() { return code; }
    public String getErrorLevel() { return errorLevel; }
    public String getErrorType() { return errorType; }
    public String getDescription() { return description; }

    public static ResponseCodeEnum getByCode(Integer code) {
        for (ResponseCodeEnum value : values()) {
            if (value.getCode().equals(code)) {
                return value;
            }
        }
        return SYSTEM_EXCEPTION;
    }
}

a. Custom Business Exception Class

Define a runtime exception that carries the enum and a custom message:

package com.summo.demo.exception.biz;

import com.summo.demo.model.response.ResponseCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class BizException extends RuntimeException {
    /** Error code */
    private ResponseCodeEnum errorCode;
    /** Custom error message */
    private String errorMsg;
}

(2) Global Exception Handler

Handle BizException globally and map error codes to views:

package com.summo.demo.exception.handler;

import javax.servlet.http.HttpServletResponse;
import com.summo.demo.exception.biz.BizException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

@RestControllerAdvice(basePackages = {"com.summo.demo.controller", "com.summo.demo.service"})
public class BizGlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    public ModelAndView handler(BizException ex, HttpServletResponse response) {
        ModelAndView modelAndView = new ModelAndView();
        switch (ex.getErrorCode()) {
            case LOGIN_USER_INFO_CHECK:
                // redirect to login page
                modelAndView.setViewName("redirect:/login");
                break;
            case NO_PERMISSIONS:
                modelAndView.addObject("errorMsg", ex.getErrorMsg());
                modelAndView.addObject("errorCode", ex.getErrorCode().getCode());
                modelAndView.setViewName("403");
                break;
            default:
                modelAndView.addObject("errorMsg", ex.getErrorMsg());
                modelAndView.addObject("errorCode", ex.getErrorCode().getCode());
                modelAndView.setViewName("error");
        }
        return modelAndView;
    }
}

(3) Test Cases

Demonstrates normal business exception handling, permission‑denied (403) handling, etc.

4. Custom Filter Exception Handling

Since @ControllerAdvice cannot capture exceptions from custom Filters, implement ErrorController to handle them.

1. ErrorController Principle

Implementing ErrorController allows custom error pages, logging, and redirection based on status codes.

Custom error pages improve user experience.

Logging aids troubleshooting.

Redirection or forwarding can be performed according to error type.

2. Implementation

Example CustomErrorController.java :

package com.summo.demo.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class CustomErrorController implements ErrorController {

    @RequestMapping("/error")
    public ModelAndView handleError(HttpServletRequest request, HttpServletResponse response) {
        int statusCode = response.getStatus();
        if (StringUtils.isNotBlank(response.getHeader("statusCode"))) {
            statusCode = Integer.valueOf(response.getHeader("statusCode"));
        }
        if (statusCode == HttpServletResponse.SC_FOUND) {
            String redirectLocation = response.getHeader("Location");
            return new ModelAndView("redirect:" + redirectLocation);
        } else if (statusCode == HttpServletResponse.SC_UNAUTHORIZED) {
            return new ModelAndView("redirect:/login");
        } else if (statusCode == HttpServletResponse.SC_FORBIDDEN) {
            return new ModelAndView("403");
        } else if (statusCode == HttpServletResponse.SC_NOT_FOUND) {
            return new ModelAndView("404");
        } else if (statusCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
            ModelAndView modelAndView = new ModelAndView("500");
            modelAndView.addObject("errorMsg", response.getHeader("errorMsg"));
            modelAndView.addObject("errorCode", response.getHeader("errorCode"));
            return modelAndView;
        } else {
            return new ModelAndView("error");
        }
    }
}

Note: The status code may come from response.getStatus() or a custom header "statusCode" set by a Filter, because Filter‑thrown exceptions always set the status to 500.

3. Preserving Original Request URL for Post‑Login Redirection

In a Filter, capture the full request URL (including query parameters):

/**
 * Get the full request URL with parameters
 */
private String getRequestURL(HttpServletRequest httpServletRequest) {
    String url = httpServletRequest.getRequestURL().toString();
    String query = httpServletRequest.getQueryString();
    if (query != null) {
        url += "?" + query;
    }
    return url;
}

When throwing a 401 error, set a header with the encoded URL:

httpServletResponse.setHeader("redirectURL", URLEncoder.encode(getRequestURL(httpServletRequest), "utf-8"));

In CustomErrorController , read this header and redirect to the login page with the original URL as a parameter:

if (StringUtils.isNotBlank(response.getHeader("redirectURL"))) {
    return new ModelAndView("redirect:/login?redirectURL=" + response.getHeader("redirectURL"));
}

Finally, after a successful login, redirect back to the saved URL:

if (StringUtils.isNotBlank(redirectURL)) {
    httpServletResponse.sendRedirect(redirectURL);
    return;
}
httpServletResponse.sendRedirect("/index");

This completes the solution for handling interface exceptions uniformly across controllers, services, and filters in a Spring Boot application.

Javabackend developmentexception handlingSpring BootControllerAdviceErrorController
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login 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.