【问题标题】:Spring Post Method with Unit return type returns 200 instead of 204具有 Unit 返回类型的 Spring Post 方法返回 200 而不是 204
【发布时间】:2020-02-16 09:31:46
【问题描述】:

当执行返回类型为 Unit 的方法时,我希望 spring 将 204 NO CONTENT 放在响应中的状态代码上,但它总是返回 200 OK。有没有办法在全球范围内改变这种行为?我不想为每个 Unit 方法添加 ResponseStatus 注释。

@RestController()
@RequestMapping("/{role:(?:veterinarian\\b|admin\\b)}")
class EmployeeController(
        private val userService: UserService
) {

    @PostMapping(consumes = [MediaType.APPLICATION_JSON_UTF8_VALUE])
    fun hire(@RequestBody employeeDTO: EmployeeDTO, @PathVariable role: String): Unit {
        if (employeeDTO.user_role != role)
            throw ResponseStatusException(HttpStatus.BAD_REQUEST)
        else
            userService.addNewUser(EmployeeDAO.build(employeeDTO))
    }
}

更新:

当状态响应为 200 OK 并且它似乎工作时,我最终使用过滤器检查内容类型是否未定义。

这是过滤代码

@Component
public class NoContentFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(httpServletRequest, httpServletResponse);
        if (httpServletResponse.getContentType() == null ||
                httpServletResponse.getContentType().equals("")) {
            if ( httpServletResponse.getStatus() == 200 ) {
                httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
            }
        }
    }
}

【问题讨论】:

  • 请参考这篇文章:stackoverflow.com/questions/32396884/… 有几个建议可能对你有帮助
  • 我将尝试使用过滤器调整答案。我添加了一项检查以确保响应状态为 200,这样它就不会干扰正文可能为空的其他响应(如错误)

标签: spring spring-boot kotlin


【解决方案1】:

Spring 允许通过@ControllerAdvice 为控制器配置全局行为

使用@ControllerAdvice 注释的类可以包含@ExceptionHandler、@InitBinder 和@ModelAttribute 注释方法,这些方法将应用于所有控制器层次结构中的@RequestMapping 方法,而不是在其中声明它们的控制器层次结构。

通常,我们使用它通过@ExceptionHandler 全局处理异常,但这不是您的情况。 (@InitBinder@ModelAttribute 也是)

ResponseBodyAdvice,但没有ResponseStatusAdvice

看来没有直接的解决办法

解决方法:

这是一个非常肮脏的技巧,我真的建议你每次都使用@ResponseStatus,但如果你真的想在全球范围内解决这个问题,那么:

你可以结合错误处理和ResponseBodyAdvice

你应该创建一个异常类

class NoContentException: RuntimeException()

为它创建一个处理程序。它应该在@ControllerAdvice 之后调用,这将抛出 NoContentException 所以它有@Order(1)

@ControllerAdvice
@Order(1)
class NoContentErrorHandler {

    @ExceptionHandler(NoContentException::class)
    fun handleNoContent() = ResponseEntity.noContent().build<Unit>()
}

最后创建一个引发实际异常的响应正文建议:

@ControllerAdvice
@Order(0)
class ThrowingNoContentExceptionResponseBodyAdvice : ResponseBodyAdvice<Unit> {
    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
        return returnType.parameterType == Void.TYPE
    }

    override fun beforeBodyWrite(
        body: Unit?,
        returnType: MethodParameter,
        selectedContentType: MediaType,
        selectedConverterType: Class<out HttpMessageConverter<*>>,
        request: ServerHttpRequest,
        response: ServerHttpResponse
    ): Unit? {
        throw NoContentException()
    }
}

所以它的工作方式如下: 请求 -> 带有单元响应调用的方法 -> 抛出异常的建议 -> 处理异常并将其转换为 NO_CONTENT 状态的建议

【讨论】:

  • 我希望有一个使用@ControllerAdvice 的解决方案。我会重新考虑使用@ResponseStatus 或尝试使用OncePerRequestFilter,因为我同意你的观点,这是一种非常肮脏的解决方法。谢谢你的回答
猜你喜欢
  • 2022-01-22
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 2019-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多