科特林公司;Spring MVC-HTTP状态400-空

科特林公司;Spring MVC-HTTP状态400-空,spring,spring-boot,kotlin,Spring,Spring Boot,Kotlin,我有一个Kotlin@RestController,我希望它在@RequestParam参数传递null的情况下返回400错误请求 示例: @RestController class Api() { @PostMapping("endpoint") fun endpoint(@DateTimeFormat(iso = DATE) @RequestParam date: LocalDate) { //do something } } 如果我请求发布/en

我有一个Kotlin
@RestController
,我希望它在
@RequestParam
参数传递
null
的情况下返回
400
错误请求

示例:

@RestController
class Api() {

  @PostMapping("endpoint")
  fun endpoint(@DateTimeFormat(iso = DATE) @RequestParam date: LocalDate) {
    //do something
  }
}
如果我请求发布
/endpoint?date
我会得到一个
500
内部服务器错误,正文如下(缩短):


是否有任何方法可以使用一些附加库、配置或一些自定义代码来修复此问题,这些代码没有其他副作用,除了状态代码将是
400
Bad Request

当验证失败时,默认情况下Spring将抛出IllegalArgumentException。Spring Boot应用程序中的每个异常在默认情况下都将被视为InternalServerError。 您可以通过添加如下函数来修改响应代码:

@RestController
类Api(){
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_请求)
IllegalArgumentException(IllegalArgumentException e)上的公共无效{
}
@后期映射(“端点”)
有趣的端点(@DateTimeFormat(iso=DATE)@RequestParam日期:LocalDate){
//做点什么
}
}

之后,您将在无效请求参数上获得400状态代码。

我已经测试了您的代码,它没有明显的错误-按预期工作

curl -v -d "date=2020-10-01" http://localhost:8080/endpoint
*   Trying ::1:8080...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /endpoint HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Length: 15
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 15 out of 15 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Content-Length: 0
< Date: Tue, 13 Oct 2020 07:04:21 GMT
< 
* Connection #0 to host localhost left intact
curl-v-d“日期=2020-10-01”http://localhost:8080/endpoint
*正在尝试::1:8080。。。
*TCP_节点集
*已连接到本地主机(::1)端口8080(#0)
>POST/endpoint HTTP/1.1
>主机:本地主机:8080
>用户代理:curl/7.68.0
>接受:*/*
>内容长度:15
>内容类型:application/x-www-form-urlencoded
> 
*完全发送上传:15个字节中的15个
*将捆绑包标记为不支持多用途
因此,我想:

  • 验证客户端是否正确格式化了请求-例如,尝试显式地将内容类型标题设置为
    application/x-www-form-urlencoded
    ,检查参数名称是否有拼写错误等
  • 您已将Jackson正确配置为与Kotlin一起使用(com.fasterxml.Jackson.module:Jackson module Kotlin-dependency)

  • Kotlin 1.4.*的以下工作。这不一定是最好的答案,因为不能保证Kotlin不会在将来的版本中更改消息的exoption类型。例如,当我最初问这个问题时,我使用的是1.3.*,例外情况是
    IllegalArgumentException
    。在1.4.*中,它已更改为NullPointerException

    @ControllerAdvice
    class KotlinNonNullParamHandling {
    
        @ExceptionHandler
        protected fun handleKotlinNonNullViolation(
            exception: NullPointerException,
            response: HttpServletResponse
        ) {
            val nullParameter = exception.message?.startsWith("Parameter specified as non-null is null: ") == true
            val restController = exception
                .stackTrace
                .getOrNull(0)
                ?.let { Class.forName(it.className) }
                ?.getAnnotation(RestController::class.java) != null
            val status =
                if (nullParameter && restController) HttpStatus.BAD_REQUEST
                else HttpStatus.INTERNAL_SERVER_ERROR
            response.sendError(status.value(), exception.message)
        }
    }
    
    在假设它是400之前,我们检查异常

    • 是一个
      NullPointerException
    • 具有以“指定为非null的参数为null:”开头的消息:
    • 它是从类is RestController抛出的(否则可能是其他问题,例如通过反射将null发送到与web层无关的方法)

    我的问题是看一个日期为null的情况,这会导致Kotlin抛出异常。这会导致500个错误,但在这种情况下400个更合适。哦,我误解了你的问题,很抱歉。我会更新答案。@joseph-k-strauss,你试过StefanD答案了吗?即使是合法的500个错误,这个答案也会返回400,也就是说,抛出IllegalArgumentException还有其他原因。
    @ControllerAdvice
    class KotlinNonNullParamHandling {
    
        @ExceptionHandler
        protected fun handleKotlinNonNullViolation(
            exception: NullPointerException,
            response: HttpServletResponse
        ) {
            val nullParameter = exception.message?.startsWith("Parameter specified as non-null is null: ") == true
            val restController = exception
                .stackTrace
                .getOrNull(0)
                ?.let { Class.forName(it.className) }
                ?.getAnnotation(RestController::class.java) != null
            val status =
                if (nullParameter && restController) HttpStatus.BAD_REQUEST
                else HttpStatus.INTERNAL_SERVER_ERROR
            response.sendError(status.value(), exception.message)
        }
    }