处理来自WebSocket消息的MethodArgumentNotValidException。

huangapple go评论100阅读模式
英文:

Handle a MethodArgumentNotValidException from a WebSocket message

问题

我正在使用Spring WebSocket,我有一个使用验证注解注释的Model,希望在保存之前捕获其验证消息。考虑我正在测试的这个@Controller

@Controller
public class MessageThreadController {
    /* 与MessageThread匹配的带有JSON主体的普通XHR post请求。 */
    @RequestMapping(
        headers={"Accept=application/hal+json", "Content-Type=application/json", "X-Requested-With=XMLHttpRequest"},
        method={RequestMethod.POST},
        produces="application/hal+json",
        value="/api/unsecure/thread/create")
    public HttpEntity createPost(@Valid @RequestBody MessageThread messageThread) throws Exception {
        return ResponseEntity.ok("valid");
    }

    /* 与上面相同的请求,但适用于WebSocket。*/
    @MessageMapping("/api/secure/thread/create")
    @SendToUser("/api/secure/broadcast")
    public HttpEntity createMessage(@Valid @RequestBody MessageThread messageThread) throws Exception {
        return ResponseEntity.ok("valid");
    }
}

我有一个@ControllerAdvice来捕获可能发生的Exception

@ControllerAdvice
public class MessageThreadControllerAdvice {
    private static final Logger log = LoggerFactory.getLogger(MessageThreadControllerAdvice.class);

    /* 处理MessageThreadController.createPost()验证异常。 */
    @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)
    public void methodArgumentNotValidExceptionHandler(
        org.springframework.web.bind.MethodArgumentNotValidException e) {
        log.trace("methodArgumentNotValidExceptionHandler");
    }

    /* 处理MessageThreadController.createMessage()验证异常。 */
    @ExceptionHandler(org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException.class)
    @SendToUser("/api/secure/broadcast")
    public void methodArgumentNotValidWebSocketExceptionHandler(
        org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException e) {
        log.trace("methodArgumentNotValidWebSocketExceptionHandler");
    }

    /* 在这里处理所有异常。 */
    @ExceptionHandler
    @SendToUser("/api/secure/broadcast")
    public void exceptionHandler(Exception e) {
        log.trace("exceptionHandler");
    }
}

以下是涉及的对象:

@Getter @Setter
public class MessageThread {
    @Size(min=1, message="{validation.userIds.empty}")
    @NotNull(message="{validation.userIds.null}")
    private Set<BigInteger> userIds;
}

如果我在MessageThreadController.createPost()MessageThreadController.createMessage()中发送以下JSON主体:

{"userIds":[]}

则会触发验证。问题是只有MessageThreadController.createPost()的异常得到处理,而MessageThreadController.createMessage()的异常未被处理:

2020-08-21 11:54:00.651 ERROR 4444 --- [nboundChannel-4] .WebSocketAnnotationMethodMessageHandler : Unhandled exception from message handler method

org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException: Could not resolve method parameter at index 0 in public void controller.MessageThreadController.createMessage(model.form.MessageThread) throws java.lang.Exception: 1 error(s): [Field error in object 'messageThread' on field 'userIds': rejected value [[]]; codes [Size.messageThread.userIds,Size.userIds,Size.java.util.Set,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [messageThread.userIds,userIds]; arguments []; default message [userIds],2147483647,1]; default message [size must be between 1 and 2147483647]]

我如何处理在WebSocket消息上抛出的MethodArgumentNotValidException

英文:

Im using Spring WebSocket and I have a Model annotated with validation annotations and want to capture the validation message it has before saving. Consider this @Controller I am testing:

@Controller
public class MessageThreadController {
    /* A plain XHR post request with a JSON body to match MessageThread. */
    @RequestMapping(
        headers={&quot;Accept=application/hal+json&quot;, &quot;Content-Type=application/json&quot;, &quot;X-Requested-With=XMLHttpRequest&quot;},
        method={RequestMethod.POST},
        produces=&quot;application/hal+json&quot;,
        value=&quot;/api/unsecure/thread/create&quot;)
    public HttpEntity createPost(@Valid @RequestBody MessageThread messageThread) throws Exception {
        return ResponseEntity.ok(&quot;valid&quot;);
    }

    /* The same request above but for WebSocket.*/
    @MessageMapping(&quot;/api/secure/thread/create&quot;)
    @SendToUser(&quot;/api/secure/broadcast&quot;)
    public HttpEntity createMessage(@Valid @RequestBody MessageThread messageThread) throws Exception {
        return ResponseEntity.ok(&quot;valid&quot;);
    }
}

And I have a @ControllerAdvice to capture the Exceptions that may happen:

@ControllerAdvice
public class MessageThreadControllerAdvice {
    private static final Logger log = LoggerFactory.getLogger(MessageThreadControllerAdvice.class);

    /* MessageThreadController.createPost() validation exception are handled here. */
    @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)
    public void methodArgumentNotValidExceptionHandler(
        org.springframework.web.bind.MethodArgumentNotValidException e) {
        log.trace(&quot;methodArgumentNotValidExceptionHandler&quot;);
    }

    /* MessageThreadController.createMessage() validation exception are handled here. */
    @ExceptionHandler(org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException.class)
    @SendToUser(&quot;/api/secure/broadcast&quot;)
    public void methodArgumentNotValidWebSocketExceptionHandler(
        org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException e) {
        log.trace(&quot;methodArgumentNotValidWebSocketExceptionHandler&quot;);
    }

    /* All exceptions are handled here. */
    @ExceptionHandler
    @SendToUser(&quot;/api/secure/broadcast&quot;)
    public void exceptionHandler(Exception e) {
        log.trace(&quot;exceptionHandler&quot;);
    }
}

And here is the object in question:

@Getter @Setter
public class MessageThread {
    @Size(min=1, message=&quot;{validation.userIds.empty}&quot;)
    @NotNull(message=&quot;{validation.userIds.null}&quot;)
    private Set&lt;BigInteger&gt; userIds;
}

If I were to send the following JSON body in both MessageThreadController.createPost() and MessageThreadController.createMessage():

{&quot;userIds&quot;:[]}

the validation kicks in. The problem is that only the exception from MessageThreadController.createPost() gets handled while the exception from MessageThreadController.createMessage() is not handled:

>2020-08-21 11:54:00.651 ERROR 4444 --- [nboundChannel-4] .WebSocketAnnotationMethodMessageHandler : Unhandled exception from message handler method
>
> org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException: Could not resolve method parameter at index 0 in public void controller.MessageThreadController.createMessage(model.form.MessageThread) throws java.lang.Exception: 1 error(s): [Field error in object 'messageThread' on field 'userIds': rejected value [[]]; codes [Size.messageThread.userIds,Size.userIds,Size.java.util.Set,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [messageThread.userIds,userIds]; arguments []; default message [userIds],2147483647,1]; default message [size must be between 1 and 2147483647]]

How can I handle the MethodArgumentNotValidException that is thrown on a websocket message?

答案1

得分: 2

你可以在你的@ControllerAdvice方法上加上@MessageExceptionHandler注解。

@ControllerAdvice
public class MessageThreadControllerAdvice {
    private static final Logger log = LoggerFactory.getLogger(MessageThreadControllerAdvice.class);

    /* 处理MessageThreadController.createPost()的验证异常。 */
    @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)
    public void methodArgumentNotValidExceptionHandler(
        org.springframework.web.bind.MethodArgumentNotValidException e) {
        log.trace("methodArgumentNotValidExceptionHandler");
    }

    /* 处理MessageThreadController.createMessage()的验证异常。 */
    @MessageExceptionHandler(org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException.class)
    @SendToUser("/api/secure/broadcast")
    public void methodArgumentNotValidWebSocketExceptionHandler(
        org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException e) {
        log.trace("methodArgumentNotValidWebSocketExceptionHandler");
    }

    /* 所有异常在此处理。 */
    @ExceptionHandler
    @SendToUser("/api/secure/broadcast")
    public void exceptionHandler(Exception e) {
        log.trace("exceptionHandler");
    }
}

这个注解也可以应用在@Controller级别上。

请注意,自Spring 4.2起,这个注解在@ControllerAdvice级别也是支持的。

英文:

You can annotate your @ControllerAdvice method with the @MessageExceptionHandler annotation.

@ControllerAdvice
public class MessageThreadControllerAdvice {
    private static final Logger log = LoggerFactory.getLogger(MessageThreadControllerAdvice.class);

    /* MessageThreadController.createPost() validation exception are handled here. */
    @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)
    public void methodArgumentNotValidExceptionHandler(
        org.springframework.web.bind.MethodArgumentNotValidException e) {
        log.trace(&quot;methodArgumentNotValidExceptionHandler&quot;);
    }

    /* MessageThreadController.createMessage() validation exception are handled here. */
    @MessageExceptionHandler(org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException.class)
    @SendToUser(&quot;/api/secure/broadcast&quot;)
    public void methodArgumentNotValidWebSocketExceptionHandler(
        org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException e) {
        log.trace(&quot;methodArgumentNotValidWebSocketExceptionHandler&quot;);
    }

    /* All exceptions are handled here. */
    @ExceptionHandler
    @SendToUser(&quot;/api/secure/broadcast&quot;)
    public void exceptionHandler(Exception e) {
        log.trace(&quot;exceptionHandler&quot;);
    }
}

This annotation could be also applied at the @Controller level.

Please, be aware that this annotation is supported at @ControllerAdvice level since Spring 4.2.

huangapple
  • 本文由 发表于 2020年8月21日 12:57:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63516743.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定