I'using Spring 4.1.1 with Hibernate Validator 5.1.3. I'm trying to validate method parameter with the JSR303 validation, and It can't retrieve the parameter's name of the validated method because spring apply validation on the interface, but the parameter's names are in the compiled implementation class.
Interface :
Code:
#Validated
public interface NomenclatureVenteRest {
public Response recupererArborescenceNomenclatureVente(
#FieldValidation(required="dateAppli") StimeDate dateAppli,
#FieldValidation(check = GENE_M010_PAYS) String codePays,
#FieldValidation(check = GENE_M010_ENSEIGNE) List<String> listeCodeEns) throws StimeFunctionalException;
}
Implementation :
Code:
#Path("/")
#Service(value = "nomenclatureVenteRestImpl")
public class NomenclatureVenteRestImpl implements NomenclatureVenteRest {
#GET
#Path("/")
#Produces(RestUtils.UTF8_ENCODED_JSON)
#Override
public Response recupererArborescenceNomenclatureVente(
#QueryParam("dateAppli") StimeDate dateAppli,
#QueryParam("codePays")String codePays,
#QueryParam("listeCodeEns")List<String> listeCodeEns) throws StimeFunctionalException{
...Business logic...
}
}
#FieldValidation is my custom constraint validation
#Target({ PARAMETER, ANNOTATION_TYPE ,FIELD})
#Retention(RUNTIME)
#Constraint(validatedBy = FieldValidator.class)
#Documented
public #interface FieldValidation {
String message() default "";
Class<?>[] groups() default {};
boolean required() default false;
FieldTypeValidator[] check() default {};
Class<? extends Payload>[] payload() default {};
}
I want tu use the parameter name in my validator :
public class FieldValidator implements ConstraintValidator<FieldValidation, Object>, ApplicationContextAware {
ApplicationContext context;
private boolean fieldRequired;
private FieldTypeValidator[] fieldCheck;
#Override
public final void initialize(final FieldValidation annotation) {
fieldCheck = annotation.check();
fieldRequired = annotation.required();
}
#SuppressWarnings("unchecked")
#Override
public boolean isValid(final Object value, ConstraintValidatorContext constraintValidatorContext) {
//If I take look a the constraintValidatorContext.methodParameterNames contains ["arg0", "arg1", "arg2"] , but I need the real parameter names ["dateAppli","codePays","listeCodeEns"]
return true;
}
Jsr303 configuration :
<!-- JSR 303 validation -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean
class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator" />
</bean>
<bean
class="org.springframework.validation.beanvalidation.BeanValidationPostProcessor">
<property name="validator" ref="validator" />
</bean>
Regards ,
Régis LIMARE
Related
I've been using Hibernate Validator in my Spring project. I'm about to validate my JUser Object automatically. i.e, I want Spring to validate the Object and set errors in BindigResult. But It doesn't work.
pom.xml
<properties>
<spring.version>4.3.5.RELEASE</spring.version>
<spring.security.version>4.0.2.RELEASE</spring.security.version>
<hibernate.version>4.3.11.Final</hibernate.version>
<validation-api.version>1.1.0.Final</validation-api.version>
<hibernate-validator.version>5.4.0.Final</hibernate-validator.version>
</properties>
....
applicationContext.xml
...
<tx:annotation-driven transaction-manager="hibernateTransactionManager"/>
<context:annotation-config />
<context:component-scan base-package="my.project.controller" />
<mvc:annotation-driven validator="validator">
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages"/>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator"/>
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
JUser.java
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
#Entity
public class JUser implements Officeable {
#Id
private Long id;
#Column(unique = true, nullable = false)
private String username;
private String password;
#NotEmpty
private String firstName;
#NotEmpty
private String lastName;
private String tel;
}
UserController.java
import javax.validation.ConstraintViolationException;
....
#RequestMapping(value = "/update", method = RequestMethod.POST)
public String update2(HttpServletRequest request, Model model, #ModelAttribute("user") #Valid JUser user, BindingResult result) {
if (!result.hasErrors()) {
System.out.println("binding result has no errors for user ");
try {
JUser updated = userService.update(user);
model.addAttribute("user", updated);
} catch (MessageException | DataIntegrityViolationException ex) {
result.reject("user", ex.getMessage());
} catch (ConstraintViolationException cvex) {
for (ConstraintViolation cv : cvex.getConstraintViolations()) {
result.rejectValue(cv.getPropertyPath().toString(),cv.getMessageTemplate() , cv.getMessage());
}
}
}
return "user/manage";
}
As you see in the above controller method I want Spring to validate the user Object and set errors in BindigResult. But It does not work.
For example when user has empty firstName I face the output:
output:
binding result has no errors for user
and I have to catch hibernate thrown exceptions by hand:
ConstraintViolationException: may not be empty ...
more description. I've used String #Validated annotation and It did not work as well. I've read more than ten related stackoverflow questions and they didn't solved my problem.
First thing, can you test if validate is working after adding below code?
pom.xml
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
#Bean // in configuration
public Validator validator() {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
return validatorFactory.getValidator();
}
#Autowired //in controller
private Validator validator;
public <T> void validate(T t) {
Set validate = this.validator.validate(t);
if(!validate.isEmpty()) {
throw new RuntimeException();
}
}
If this works, then can suggest you further to simplify it.
As per spring-mvc-4.3.xsd
The bean name of the Validator that is to be used to validate
Controller model objects. This attribute is not required, and only
needs to be specified if a custom Validator needs to be configured. If
not specified, JSR-303 validation will be installed if a JSR-303
provider is present on the classpath.
I don't see you wrote any custom validator so you can change
<mvc:annotation-driven validator="validator">
to support the default JSR-303
<mvc:annotation-driven />
Example: Spring 3 MVC and JSR303 #Valid example
Update 1
Could you also try removing validation-api.version
This transitively pulls in the dependency to the Bean Validation API
(javax.validation:validation-api:1.1.0.Final).
You can use the ExceptionHandler approach. Just add this method in your controller class. I haven't tested this with the #ModelAttribute although it should work, I know for sure that it works with #RequestBody.
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ErrorDTO processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
// your own custom error dto class
ErrorDTO errorDto = constructErrors(fieldErrors);
return errorDto;
}
If you are using HibernateValidator you must tell to use the HibernateValidator class
By looking the LocalValidatorFactoryBean javadoc
When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces, you'll be talking to the default Validator of the underlying ValidatorFactory. This is very convenient in that you don't have to perform yet another call on the factory, assuming that you will almost always use the default Validator anyway. This can also be injected directly into any target dependency of type Validator!
So you should use the setProviderClass method in order to specify what class to use
Here it's what I did (i'm using annotation based config but it's the same):
WebMvcConfig
#Override
public Validator getValidator() {
LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
lvfb.setProviderClass(HibernateValidator.class);
return lvfb;
}
Model:
#Entity
#Table(name = "CANDIDATO")
public class Candidato extends AbstractModel {
private static final long serialVersionUID = -5648780121365553697L;
.
.
.
private String corsoLaurea;
.
.
.
#Column(name="CORSO_LAUREA", nullable=true)
#NotEmpty
public String getCorsoLaurea() {
return corsoLaurea;
}
}
controller method
#RequestMapping(method = { RequestMethod.PUT }, value = { "/salvaModificheCandidato" })
public ResponseEntity<BaseResponse<String>> modificaCandidato(#RequestBody #Valid ModificaCandidatoDto dto, BindingResult bindResult) throws Exception
{
BaseResponse<String> result = null;
HttpStatus status = null;
try
{
this.candidatoSvc.modificaCandidato(dto);
result = new BaseResponse<String>();
status = HttpStatus.OK;
result.setDescrizioneOperazione("Aggiornamento candidato terminato correttamente");
result.setEsitoOperazione(status.value());
result.setPayload(Collections.EMPTY_LIST);
}
catch (Exception e)
{
result = new BaseResponse<String>();
status = HttpStatus.INTERNAL_SERVER_ERROR;
String message = "Errore nella modifica del candicato con ID "+dto.getIdCandidato()+"; "+e.getMessage();
logger.error(message, e);
result.setDescrizioneOperazione(message);
result.setEsitoOperazione(status.value());
}
return new ResponseEntity<BaseResponse<String>>(result, status);
}
With this configuration I find in bindinresult errors for both the DTO and the Model
I hope this can be useful
EDITED PART
I saw that your issue is to have the bindingresult not empty when you try to persist your object; I changed my code in this way
No change to the model (I used the hibernate validation NotEmpty annotation)
I changed my service method in this way:
#Override
#Transactional(transactionManager = "hibTx", rollbackFor = CandidatiDbException.class, readOnly = false)
public void modificaCandidato(ModificaCandidatoDto dto, BindingResult brErrors) throws CandidatiDbException {
try
{
dao.modificaCandidato(dto, brErrors);
} catch (Exception e)
{
String message = "Errore nella modifica del candidato con ID "+dto.getIdCandidato()+"; "+e.getMessage();
logger.error(message, e);
throw new CandidatiDbException(message);
}
}
As you can see I passed the BindingResult object to the method
Then I changed my DAO impl in this way:
public class CandidatoDaoImpl<T> implements ICandidatoDao<T> {
#Autowired
#Qualifier("candValidator")
Validator validator;
public void modificaCandidato(ModificaCandidatoDto dto, BindingResult brErrors) {
Session sessione = getSession();
sessione.setCacheMode(CacheMode.IGNORE);
Candidato candidato = sessione.load(Candidato.class, dto.getIdCandidato());
.
.
.
validator.validate(candidato, brErrors);
if( !brErrors.hasErrors() )
{
sessione.saveOrUpdate(candidato);
}
}
}
Finally I updated my WebMvcConfig in this way:
#Configuration
#EnableWebMvc
#Import(SharedSpringConfig.class)
#PropertySource( value={"classpath:configuration.properties"}, encoding="UTF-8", ignoreResourceNotFound=false)
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean(name="candValidator")
public Validator validator()
{
LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
lvfb.setProviderClass(HibernateValidator.class);
return lvfb;
}
#Override
public Validator getValidator() {
return validator();
}
}
In this way when I have some error on the object I want to persist I have the BindingResult object not empty and no exception is raised
I hope this can be useful
Angelo
hi I have a problem while Implementing custom validation using ConstraintValidator.
I am implementing some bean validations using JSR303 and constraint validator and upto now I have following implementation as
#ProductId// this is the problem
#NotEmpty(message = "{Domain.NotEmpty.productid}")
protected String productId;
#Pattern(regexp = "[A-Za-z]+", message = "{Domain.Pattern.firstName}")
....
#ProductId
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Documented
#Constraint(validatedBy = ProductIdValidator.class)
public #interface ProductId {
String message() default "{ProductId.validaton.Error}";
Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
ProductIdValidator
public class ProductIdValidator implements ConstraintValidator<ProductId, String> {
#Autowired
private RepositiryObject repo;
#Override
public void initialize(ProductId productId) {
}
#Override
public boolean isValid(String string, ConstraintValidatorContext constraintValidatorContext) {
Domain domain;
try {
domain = repo.getProductById(string);
} catch (ProdctNotFoundException ex) {
return true;
}
if (domain != null) {
return false;
}
return true;
}
}
StackTrace
Request processing failed; nested exception is javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.] with root cause
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.model.domain.controller.repo.RepositiryObject.getProductById(RepositiryObject.java:45)
at com.java.spring.custom.validation.annotations.constraints.ProductIdValidator.isValid(ProductIdValidator.java:28)
at com.java.spring.custom.validation.annotations.constraints.ProductIdValidator.isValid(ProductIdValidator.java:1)
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:448)
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:127)
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:87)
at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:73)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:617)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:580)
dispatcherservlet
<mvc:annotation-driven validator="validator" />
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
......
EditSince exception says that no such element is present but when I ran test its working fine as
public RepositiryObject() {
domainList = new ArrayList<>();
Domain domain = new Domain();
domain.setProductId("P1234");
domainList.add(domain);
}
#Test
public void checkIfProductExists() {
Assert.assertEquals("P1234", domainList.stream().filter(product -> product.getProductId().equals("P1234"))
.map(Domain::getProductId).findAny().get());
}
Now the problem is when I remove that #ProductId it works fine not sure what I am missing here
I've created a custom constraint validator that works on a list of objects. Unfortunately it doesn't seem to be getting invoked, it worked when I had a wrapper class containing the list with the annotation on the list.
This is the code that worked fine
public class wrapper {
#ValidMyObjectList
List<MyObject> myObjects;
...
}
But now I've got rid of the wrapper class and added the annotation to the parameter in the controller method.
Here's the controller
#RequestMapping(value = "", method = RequestMethod.POST)
public List<MyObject> stopCheque(
#ValidMyObjectList #RequestBody final List<MyObject> myObjects,
final HttpServletResponse httpServletResponse) {
....
}
Here's the constraint annotation
#Constraint(validatedBy = MyObjectListValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface MyObjectList {
Class<?>[] groups() default {};
String message() default "";
Class<? extends Payload>[] payload() default {};
}
And part of the validator itself
public class MyObjectListValidator implements
ConstraintValidator<MyObjectList, List<MyObject>> {
#Override
public void initialize(final MyObjectList myObjectList) {
}
#Override
public boolean isValid(final List<MyObjectList> myObjectLists, final ConstraintValidatorContext cxt) {
...
}
Would greatly appreciate any help. Thanks
Add to your Spring config class:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
and add #Validated to controller class.
I having trouble in showing Invalid value in the error message
I have messages.properties file as follows
ProductId.exists.custom.validator.validation = A product already exists with product id ${validatedValue}.
and following is my custom validtor interface
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = ProductIdValidator.class)
#Documented
public #interface ProductId {
String message() default "{ProductId.exists.custom.validator.validation}";
Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
Here is the implementation
#Component
public class ProductIdValidator implements ConstraintValidator<ProductId, String>{
#Autowired
private ProductService productService;
#Override
public void initialize(ProductId constraintAnnotation) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Product product = productService.getProductById(value);
if(product!= null) {
return false;
}
return true;
}
}
When I am running my application I am getting error message like
A product already exists with product id ${validatedValue}. but I am expecting A product already exists with product id P1234. as error message
How to get the validatedValue in error message?
Additional details
I have used hibernate-validator:4.3.1.Final version and spring-webmvc:3.2.0.RELEASE
And I am triggering the validation in the context as follows
<mvc:annotation-driven validator="validator"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
I’m not sure what was the main reason for this solution I’ve used in my project – if to make interpolation work, or just use ReloadableResourceBundleMessageSource (that supports properties in UTF-8 and runtime reloading!) instead of the default one. However, this should work for you.
<mvc:annotation-driven validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" primary="true"
p:messageInterpolator-ref="messageInterpolator" />
<!-- Hibernate Validator which can interpolate the value being validated in the constraint message -->
<bean id="messageInterpolator" class="ValueFormatterMessageInterpolatorFactoryBean"
p:messageSource-ref="validatorMessageSource" />
<bean id="validatorMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:basename="classpath:/config/i18n/validator-messages"
p:defaultEncoding="utf-8"
p:cacheSeconds="0" />
And the custom ValueFormatterMessageInterpolatorFactoryBean class:
/**
* {#linkplain FactoryBean} that creates {#link ValueFormatterMessageInterpolator}
* with underlying {#link ResourceBundleMessageInterpolator} that uses the given
* {#link MessageSource}.
*/
public class ValueFormatterMessageInterpolatorFactoryBean implements FactoryBean<MessageInterpolator> {
private MessageSource messageSource;
public MessageInterpolator getObject() throws Exception {
ResourceBundleLocator resourceBundleLocator = new MessageSourceResourceBundleLocator(messageSource);
return new ValueFormatterMessageInterpolator(
new ResourceBundleMessageInterpolator(resourceBundleLocator));
}
public Class<?> getObjectType() {
return ValueFormatterMessageInterpolator.class;
}
public boolean isSingleton() {
return true;
}
#Required
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
}
Note: I’m using Hibernate Validator 4.3.0.Final.
I know struts2 default config will trim all strings obtained from forms.
For example:
I type " whatever " in a form and submit, I will get "whatever" The string has been auto trimmed
Does spring mvc have this function too? THX.
Using Spring 3.2 or greater:
#ControllerAdvice
public class ControllerSetup
{
#InitBinder
public void initBinder ( WebDataBinder binder )
{
StringTrimmerEditor stringtrimmer = new StringTrimmerEditor(true);
binder.registerCustomEditor(String.class, stringtrimmer);
}
}
Testing with an MVC test context:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ControllerSetupTest
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup ( )
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void stringFormatting ( ) throws Exception
{
MockHttpServletRequestBuilder post = post("/test");
// this should be trimmed, but only start and end of string
post.param("test", " Hallo Welt ");
ResultActions result = mockMvc.perform(post);
result.andExpect(view().name("Hallo Welt"));
}
#Configuration
#EnableWebMvc
static class Config
{
#Bean
TestController testController ( )
{
return new TestController();
}
#Bean
ControllerSetup controllerSetup ( )
{
return new ControllerSetup();
}
}
}
/**
* we are testing trimming of strings with it.
*
* #author janning
*
*/
#Controller
class TestController
{
#RequestMapping("/test")
public String test ( String test )
{
return test;
}
}
And - as asked by LppEdd - it works with passwords too as on the server side there is no difference between input[type=password] and input[type=text]
register this property editor:
org.springframework.beans.propertyeditors.StringTrimmerEditor
Example for AnnotionHandlerAdapter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
...
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="propertyEditorRegistrar">
<bean class="org.springframework.beans.propertyeditors.StringTrimmerEditor" />
</property>
</bean>
</property>
...
</bean>
You can also use Spring's conversion service, which has the added benefit of working with <mvc:annotation-driven/> and with Spring Webflow. As with the other answers, the major downside is that this is a global change and can't be disabled for certain forms.
You'll need a converter to do the trimming
public class StringTrimmingConverter implements Converter<String, String> {
#Override
public String convert(String source) {
return source.trim();
}
}
Then define a conversion service that knows about your converter.
<bean id="applicationConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="mypackage.util.StringTrimmingConverter"/>
</list>
</property>
</bean>
and tie that in to mvc.
<mvc:annotation-driven conversion-service="applicationConversionService"/>
If you use Spring Webflow then it require a wrapper
<bean id="defaultConversionService" class="org.springframework.binding.convert.service.DefaultConversionService">
<constructor-arg ref="applicationConversionService"/>
</bean>
and a setting on your flow builder
<flow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" development="true" validator="validator" />
Just customized the above code in order to adjust to Spring Boot, if you want to explicit trim function for some fields in the form, you can show them as below:
#Component
#ControllerAdvice
public class ControllerSetup {
#InitBinder({"dto", "newUser"})
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
binder.registerCustomEditor(String.class, "userDto.username", new StringTrimmerEditor(false));
binder.registerCustomEditor(String.class, "userDto.password", new DefaultStringEditor(false));
binder.registerCustomEditor(String.class, "passwordConfirm", new DefaultStringEditor(false));
}
}
You can user a Spring-MVC Interceptor
public class TrimInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration<String> e = request.getParameterNames();
while(e.hasMoreElements()) {
String parameterName = e.nextElement();
request.setParameter(parameterName, request.getParameter(parameterName).trim());
}
return true;
}
And set up your HandlerMapping interceptors property
<bean id="interceptorTrim" class="br.com.view.interceptor.TrimInterceptor"/>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" p:interceptors-ref="interceptorTrim"/>
}
Or use a Servlet Filter
first,trim requestparam which is String,you can create a class and implimplements WebBingdingInitializer
#ControllerAdvice
public class CustomWebBindingInitializer implements WebBindingInitializer {
#InitBinder
#Override
public void initBinder(WebDataBinder webDataBinder, WebRequest webRequest) {
webDataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
}
please use componentScan make this Class to be a Spring Bean.
But, I don't know how to trim the String value in requestBody JSON data.