I'm using http://immutables.github.io in my SpringBoot project. I added Javax validation annotations in order to validate class but it did not work.
I tried to do this way:
#Value.Immutable
#JsonDeserialize(builder = UserSignUpRequest.Builder.class)
#JsonSerialize(as = UserSignUpRequest.class)
#JsonIgnoreProperties(ignoreUnknown = true)
#ImmutableJsonModel
interface AbstractUserSignUpRequest {
Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
#Value.Parameter
#Pattern(regexp = RegexPatterns.PHONE_REGEX)
String phone();
#Value.Parameter
#NotBlank
String username();
#Value.Parameter
Optional<DeviceRegistrationRequest> device();
#Value.Check
default void check() {
Set<ConstraintViolation<AbstractUserSignUpRequest>> violations = VALIDATOR.validate(this);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
In controller body like this:
#PostMapping(
value = "/sign-up",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<UserCreatedResponse>> signUp(
#RequestBody Mono<#Valid UserSignUpRequest> request) {
return request
.flatMap(authenticationService::signUp)
.map(user -> ResponseEntity.status(CREATED).body(user));
}
My code did not work, I could not validate class with this way.
How should be implemented javax validation annotations in immutables classes?
Related
I have gradle multimodule poject containing:
'api-spec' module which contains java interfaces of rest controllers to describe API specification with SpringDoc generator
'wallet' module which contains java classes of rest-controllers implementing api-spec interfaces
Consider I have:
'api-spec' module -> WalletController.java
#RequestMapping(path = "/wallet", produces = MediaType.APPLICATION_JSON_VALUE)
public interface WalletController {
#Operation(summary = "Create wallet")
#PostMapping(path = "/create-wallet")
ApiResponseContainer<WalletDto> createWallet(#Valid #RequestBody ApiRequestContainer<CreateWalletRequestDto> request);
}
'wallet' module -> WalletControllerImpl.java:
#RestController
#Slf4j
public class WalletControllerImpl implements WalletController {
#Override
public ApiResponseContainer<WalletDto> createWallet(ApiRequestContainer<CreateWalletRequestDto> request) {
/* validation does not perform in this case */
}
}
I expect Spring to validate the controller parameter exactly the same way when I create it in one class:
GoodController.java:
#RequestMapping(path = "/good")
public class GoodController {
#PostMapping
public ApiResponseContainer<SomeDto> someMethod(#Valid #RequestBody SomeBodyDto bodyDto) {
/* request is well validated here */
}
}
}
Does anybody face this issue?
hm... it seems it was an issue with my generic request body.
check the difference:
validation "does not work":
public class ApiRequestContainer<T> {
#Schema(description = "Request content")
#NotNull
T request;
#Schema(description = "Signature of request content")
#NotNull
String signature;
}
and validation id work:
public class ApiRequestContainer<T> {
#Schema(description = "Request content")
#Valid
#NotNull
T request;
#Schema(description = "Signature of request content")
#NotNull
String signature;
}
it started to work when I mark #Valid my generic field of container.
I am validating java beans using spring-boot-starter-validation.
Validation on the controller is working fine,
I want to know whether can we validate the normal methods of a class using #Valid annotation? I have tried it but not working.
My working solution on the controller
#PostMapping("/testMessage")
ResponseEntity<String> testMethod(#Valid #RequestBody InternalMsg internalMsg) {
return ResponseEntity.ok("Valid Message");
}
I want to move the validation to a class method so that when I hit the RestAPI, the validation errors are captured in a new method.
Let's say the method is validateMsg of class MsgValidator and I am calling this method inside controller code
#PostMapping("/testMessage")
ResponseEntity<String> testMethod(#RequestBody InternalMsg internalMsg) { // No #Valid here
MsgValidator msgValidator = new MsgValidator();
Boolean isValid = msgValidator.validateMsg(internalMsg);
// some other processings
return ResponseEntity.ok("Valid Message");
}
public class MsgValidator{
public boolean validateMsg(#Valid InteropMsg interopMsg){
return true;
}
#ResponseStatus(HttpStatus.OK)
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Ack> handleValidationExceptions(MethodArgumentNotValidException ex) {
StringBuilder errorMessages = new StringBuilder("");
ex.getBindingResult().getAllErrors().forEach((error) -> {
errorMessages.append(error.getDefaultMessage()).append(";");
});
log.error("Validation errors : "+errorMessages.toString());
return ResponseEntity.ok().body(ack);
}
}
public class InternalMsg implements Serializable {
#NotNull(message = "Msg Num is a required field")
private String msgNumber;
#NotNull(message = "Activity Name is a required field")
private String activityName;
}
This is not working
Please let me know how to achieve this
Below is an example of how you could use the ValidatorFactory to get a Validator to do the validation rather than using the #Valid annotation.
InternalMsg internalMsg = new InternalMsg();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<InternalMsg>> validate = validator.validate(internalMsg);
See here for more details -> https://www.baeldung.com/javax-validation
The below is just a snippet and not necessarily the recommended way of using the ValidationFactory
In our spring rest controller we would like to use the same mappings with different kind of parameters.
To do this we created additional functions differentiated by mapping params. By doing so we are duplicating the number of functions. To avoid this I would like to use different controllers that should be loaded based on params values.
The question is can we
#RequestMapping(value = "/v1")
#RestController
public class Controller {
#PostMapping(value = "/event-calendar", params = {"externalToken", "event_type"})
public ResponseEntity createEntityOfTypeToken(#RequestHeader(name = "X-Application-Authentication") String externalToken,
#RequestParam(value = "event_type") String eventType) {
MyEntity entity = service.createEntityOfType(
userService.getTokenService(externalToken).getDeviceSerialNumber());
return new ResponseEntity<>(entity, HttpStatus.OK);
}
#PostMapping(value = "/event-calendar", params = {"serialId", "event_type"})
public ResponseEntity createEntityOfTypeSerial(#RequestParam(value = "serialId") String serialId,
#RequestParam(value = "event_type") String eventType) {
MyEntity entity = service.createEntityOfType(serialId);
return new ResponseEntity<>(entity, HttpStatus.OK);
}
}
Please refer
create two method for same url pattern with different arguments
Spring - is possible to give same url in request mapping of post method?
P.S. not enough points to comment
in my application, I enter the values of the three parameters, fromCurrency, toCurrency, and amount into the address bar
and in the controller. I want to check the correctness of the entered data. But I have generated an exception during the test and nothing goes further
Those. I need a code that in the controller will check the correctness of the entered data and, depending on the field in which the error was made, will produce a 400th error with the name of the incorrectly filled field
I'm tried this validation, with
if(!Currency.getAvailableCurrencies().contains(Currency.getInstance(fromCurrency)))
but it's generate exception if Currency doesn't contain fromCurrency
#RestController
class ExchangeController {
private static final Logger logger = Logger.getLogger(ExchangeController.class.getName());
#SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
#Autowired
#Qualifier("dataService")
private CurrencyExchangeService currencyExchangeService;
#SuppressWarnings("SameReturnValue")
#RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/json")
public String start() {
return "input parameters";
}
#RequestMapping(value = "/convert", method = RequestMethod.GET, produces = "application/json")
public ExchangeRateDTO converting(#RequestParam("fromCurrency") String fromCurrency,
#RequestParam("toCurrency") String toCurrency,
#RequestParam("amount") String amount) throws IOException {
if (!Currency.getAvailableCurrencies().contains(Currency.getInstance(fromCurrency))) {
}
BigDecimal convertedAmount = currencyExchangeService.convert(fromCurrency, toCurrency, new BigDecimal(amount));
return new ExchangeRateDTO(fromCurrency, toCurrency, new BigDecimal(amount), convertedAmount);
}
}
You can use Hibernate Validator to validate the #RequestParam of your controller.
Add this dependency to your pom.xml
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.10.Final</version>
</dependency>
Then you have to enable validation for both request parameters and path variables in your controllers by adding the #Validated annotation like this
#RestController
#RequestMapping("/")
#Validated
public class Controller {
// ...
}
Then you can add Annotations like #NotNull #Min #Max to your RequestParam Like
#RequestMapping(value = "/convert", method = RequestMethod.GET, produces = "application/json")
public ExchangeRateDTO converting(#RequestParam("fromCurrency") #NotNull #NotBlank #Size(max = 10) String fromCurrency,
#RequestParam("toCurrency") String toCurrency,
#RequestParam("amount") String amount) throws IOException {
if (!Currency.getAvailableCurrencies().contains(Currency.getInstance(fromCurrency))) {
}
BigDecimal convertedAmount = currencyExchangeService.convert(fromCurrency, toCurrency, new BigDecimal(amount));
You can also define custom annotations for your needs.
There is more detailed and nice article here
I have implemented rest service and want to do validation on it. In my class I have #RequestBody and #RequestParam. I want to validate both objects. I do that like this:
#Controller
#Validated
public class RestApiImpl {
#Autowired
ClassA classA;
#ResponseBody
#RequestMapping(value = "/classA",
method = RequestMethod.POST,
produces = {MediaType.APPLICATION_JSON_VALUE},
consumes = {MediaType.APPLICATION_JSON_VALUE})
public ClassAResponse getList(#Valid #RequestBody ClassARequest request,
#Min(value = 1, message = "At least 1 object is expected")
#RequestParam(value = "quantity",defaultValue = "3",required = false) String quantity) {
return (ClassAResponse) classA.executeService(request);
}
}
public class ClassARequest {
#NotNull
#NotEmpty
#Size(max = 6, message = "tes1")
private String value1;
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
this.value1 = value1;
}
}
And the post return 500 internal server error and log:
09:49:21,240 INFO [STDOUT] 09:49:21,240 ERROR [[dispatcher]] Servlet.service() for servlet dispatcher threw exception
javax.validation.ConstraintDeclarationException: Only the root method of an overridden method in an inheritance hierarchy may be annotated with parameter constraints, but there are parameter constraints defined at all of the following overridden methods:
When I remove #Validated #RequestBody is validated and works fine but validation for #RequestParam is not working. And If I remove #Valid from #RequestBody validation for request is not working. How to configure this that it work for both #RequestBody and #RequestParam. Or there isn't any solution and only way is to move param quantity to request?
spring framework 3.2.18.RELEASE and hibernate-validator 4.2.0.Final (it can't be changed to newer version for spring and hibernate)