spring-projects / spring-projects/spring-framework

@RequestBody and JAXB with Errors injected lack ValidationEventHandler

Open
#36,992 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

A @RestController with a @PostMapping accepts application/xml. First argument of the method is a JAXB-annotated class with @RequestBody and @Valid, too. According to the documentation[1] it injects Errors as second parameter.

The JAXB-class/ first argument has at least one field that is of a number type (Long, BigInteger, …) but allowed to be null. If this field is parsed from XML where the value is non-numeric the following happens (in the preferred HttpMessageConverter):

  1. non-numeric value causes a NumberFormatException (NFE)
  2. default ValidationEventHandler is called
  3. it drops the ValidationEvent and does not trigger an exception
  4. field stays null
  5. controller method is called with instance yielded from XML and Errors instance which is empty (no other errors occured)

It works to a certain extent with fields that also have @NotNull. In this case the Errors object contains an error complaining that the fields may not be null. Yet it does not explain the reason being a NFE.

Currently I see only two very complex strategies, contradicting the ease of use of Spring: First is to pass @RequestBody as String and do the parsing by hand, providing a ValidationEventHandler to the Unmarshaller. Spring already has all this but it is hard to look up and implement.

Second option is the hard way consisting of:

  • find or determine the responsible HttpMessageConverter (in my case there are at least 3, standard Spring flavor, Jackson and Jaxb2RootElementHttpMessageConverter, there may also be a 4th and a custom one in case of IBM staff doing things)
  • inject a ValidationEventHandler that holds a bean with @RequestScope, collecting the events/ turning them into Error instances
  • inject the same @RequestScope-d bean into the @RestController
  • check the request scoped bean in addition to the also injected Errors object for errors

Why is this so hard to achieve? All the message converters I inspected have some sort of instrumentation for the unmarshaller they use. All of these are empty/ no operation. Also @Configuration happens during startup of ApplicationContext and most of the classes involved act as singleton (request scope/ multi threading in web context needs some thourough thinking). Configuration of JAXB/ Jakarta undermines Spring to a certain degree being not thread safe and leaning more towards prototype scope. (Message converters usually create JAXB contexts per mapped class trading isolation for memory.)

I politely ask to mitigate at least the variant with Errors-object being injected into the controller's endpoint method, so that Spring validation also provides a (prototype?) ValidationEventHandler to any JAXB mapping.

It is not a solution to let @Valid cause the HTTP.400 Bad Request by throwing an exception (caused by a custom ValidationEventHandler either returning false stopping parsing at all or hooking something similar into afterUnmarshal-handlers/ post actions). An optional numeric value being null is perfectly fine according to @Valid, yet the handling code (insed the controller's method) must get a chance to check the reason why the field is null. (For example a sort of file descriptor has a @NotNull name, path and long id as well as a long originId which holds an optional reference to another file descriptor it was copied from or symlinks to. A null originId means a standalone file, which could be caused by an originId-value being a hex number or errornous UUID that could not be parsed.)

EDIT: The JAXB-mapped class is a generated one from a schema which also includes plenty of constraints. All of them mapped to the appropriate validation annotations. Thus it is also not an option to relax the number type to String and do the parsing later. Recent IDEs explicitely warn not to edit generated classes. And an adapter layer for JAXB generators is possible but not in this common case of number parsing failures.

[1] https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/requestbody.html

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with Jaxb2RootElementHttpMessageConverter and the MVC @RequestBody validation path described in the issue. Trace how JAXB ValidationEventHandler events and the injected Errors object are currently handled. Done means conversion failures for nullable numeric fields are made available to the controller's Errors handling without forcing an HTTP 400 response.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.