Return 每个空响应 404

Return 404 for every null response

我想 return 404 当响应对象为 null 时在 spring 启动时自动为每个响应自动响应。

我需要建议。

我不想检查控制器中的对象是否为空。

在 Spring 中执行此操作的最简单方法是编写您自己的异常 class,如下所示

@ResponseStatus(value = HttpStatus.NOT_FOUND)
class ResourceNotFoundException extends RuntimeException{
}

然后从任何地方抛出 ResourceNotFoundException。

if (something == null) throw new ResourceNotFoundException();

更多 -> Read

您需要多个 Spring 模块才能完成此任务。基本步骤是:

  1. 声明一个异常 class,可用于在存储库方法未 return 预期值时抛出异常。
  2. 添加一个 @ControllerAdvice 来捕获自定义异常并将其转换为 HTTP 404 状态代码。
  3. 添加拦截 return 存储库方法值的 AOP 建议,并在发现值不符合预期时引发自定义异常。

Step 1: Exception class

public class ResourceNotFoundException extends RuntimeException {}

Step 2: Controller advice

@ControllerAdvice
public class ResourceNotFoundExceptionHandler
{
  @ExceptionHandler(ResourceNotFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  public void handleResourceNotFound() {}
}

Step 3: AspectJ advice

@Aspect
@Component
public class InvalidRepositoryReturnValueAspect
{
  @AfterReturning(pointcut = "execution(* org.example.data.*Repository+.findOne(..))", returning = "result")
  public void intercept(final Object result)
  {
    if (result == null)
    {
      throw new ResourceNotFoundException();
    }
  }
}

示例应用程序可用 on Github 来演示所有这些操作。使用像 Postman 这样的 REST 客户端为 Google Chrome 添加一些记录。然后,尝试通过其标识符获取现有记录将 return 正确记录,但尝试通过不存在的标识符获取记录将 return 404.

类似于@manish 的回答 (),但没有 AspectJ 切入点,而是使用另一个 @ControllerAdvice

Step 1: NotFoundException class:

public class NotFoundException extends RuntimeException {
    public NotFoundException(String msg) {
        super(msg);
    }
    public NotFoundException() {}
}

Step 2: Check if body returned in endpoint is null and throw NotFoundException:

@ControllerAdvice
public class NotFoundAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body == null) {
            throw new NotFoundException("resource not found");
        }
        return body;
    }
}

Step 3: handle NotFoundException and make the response have a status of 404

@ControllerAdvice
public class GlobalExceptionAdvice {

    @Data
    public class ErrorDetails {
        private Date timestamp;
        private String message;
        private String details;

        public ErrorDetails(Date timestamp, String message, String details) {
            super();
            this.timestamp = timestamp;
            this.message = message;
            this.details = details;
        }
    }

    @ExceptionHandler(NotFoundException.class)
    public final ResponseEntity<ErrorDetails> notFoundHandler(Exception ex, WebRequest request) {
        ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
                request.getDescription(false));
        return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
    }
}

Alternative to Step 3:

您可以只用 @ResponseStatus 注释您的 NotFoundException 并覆盖 fillInStackTrace()(来自 ),以便它具有与 GlobalExceptionAdvice 类似的效果并且不会'像这样显示堆栈跟踪:

@ResponseStatus(value = HttpStatus.NOT_FOUND,reason =  "resource not found")
public class NotFoundException extends RuntimeException {
    public NotFoundException(String msg) {
        super(msg);
    }
    public NotFoundException() {}

    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

不需要抛出异常,现在 ResponseBodyAdvice 就可以了:

@ControllerAdvice
public class NullTo404 implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            ServerHttpResponse response) {
        if (body == null) {
            response.setStatusCode(HttpStatus.NOT_FOUND);
        }
        return body;
    }   
}

同样,您可以实现ResponseBodyAdvice<Optional<?>>,并在设置响应状态之前检查Optional.isEmpty()。它具有与 CrudRepository 很好地协同工作的额外好处。大多数控制器方法最终都是这样结束的:

public Optional<Product> getProductBySku(@PathVariable String sku) {
    // logic goes here...
    return productRepository.findBySku(sku);
}