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)
Related
I'm learning java and spring boot and I am trying to validate a controller parameter which was bound from json.
I've got simple Entity:
#Getter
#Setter
class Resource {
#NotBlank
String uri;
}
Which I want to persist through the following controller:
#BasePathAwareController
public class JavaResourcePostController {
private final ResourceRepository repository;
public JavaResourcePostController(ResourceRepository repository) {
this.repository = repository;
}
#RequestMapping(value = "/resources", method = RequestMethod.POST)
ResponseEntity<Resource> create(
#Valid #RequestBody Resource resource
) {
repository.save(resource);
return ResponseEntity.ok(resource);
}
}
My understanding is that the resource argument should be valid when entering the method. But posting an empty uri field does not trigger the validation of the method. it does however get triggered in the hibernate validation in repository.save()
Why does the #Valid annotation on the argument not ensure I get a validated entity?
You need to add #Validated to your controller class.
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
how to validate a spring boot rest endpoint with a String parameter, here is the sample of my endpoint.
#ResponseBody
#RequestMapping(value = "/getProfile", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getProfile(#RequestBody #Valid #NotEmpty String profileId){}
even thought i added #valid and #NonEmpty constraint its not getting validated.
but its working for a other classes. for eg,
#ResponseBody
#RequestMapping(value = "/saveProfiles", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> saveProfiles(#RequestBody #Valid PersistProfilesRequest request){}
here all the fields in PersistProfilesRequest class which have a constrain applied is validating properly.
validation is not happening for method parameter with java class like String and Map.
i am using spring boot 1.3.0 with hibernate validator.
how can i add validation to a rest endpoint with a string parameter?
EDIT
i am using junit and mockmvc to test the endpoint, below is the test case
#Test
public void testGetProfile() throws Exception{
String profileId = " ";
this.mockMvc.perform(post("/getProfile").content(profileId).accept(MediaType.APPLICATION_JSON).contentType("application/json"))
.andDo(print())
.andExpect(status().isInternalServerError());
}
You can try to annotate your controller class with #Validated. In my case it helped.
#Validated
#RestController
#RequestMapping(path = "/api/somethin")
public class LegacyRestController {
I have a controller annotated with #RestController and it implements an interface:
public interface ContratEndpoint {
String ROOT = "/api/contrats";
String GET_CONTRAT = "";
String GET_CONTRAT_PER_PK = "/{idContrat}";
#RequestMapping(value = GET_CONTRAT)
Contrat getContrat(#RequestParam(value = "contratId")Long contratId);
#RequestMapping(value = GET_CONTRAT_PER_ID)
ExtContrat getContratById(#PathVariable("idContrat") Long idContrat);
}
The controller:
#RestController
#RequestMapping(value = ContratEndpoint.ROOT)
public class ContratController implements ContratEndpoint {
//Injecting Services....
#Resource
private Mapper mapper;
#Override
public Contrat getContrat(Long contratId) {
return mapper.map(contratService.get(contratId),Contrat.class);
}
#Override
public ExtContrat getContratById(#PathVariable("idContrat") Long idContrat){
Preconditions.checkArgument(idContrat !=null);
return mapper.map(contratService.get(idContrat),ExtContrat.class);
}
.The above Code works just fine.
. But For the first inherited method , I didn't have to annotate arguments with #RequestParam and it worked just fine.
As for the second method I tried at first :
#Override
public ExtContrat getContratById(Long idContrat){
Preconditions.checkArgument(idContrat !=null);
return mapper.map(contratService.get(idContrat),ExtContrat.class);
}
. I expected the same behaviour Like the first Method, But i was wrong and the code ended up firing an IllegalArgumentException because of the check in ligne Preconditions.checkArgument(idContrat!=null).
My question is what is so specific about #PathVariable that i've missed ?
Or is it just something is wrong with my approach?
Thanks.
There is difference between Request param and path variable,seee below post that you can confirm with your uri the cause for the exception :
#PathVariable is to obtain some placeholder from the uri (Spring call it an URI Template) — see Spring Reference Chapter 16.3.2.2 URI Template Patterns
#RequestParam is to obtain an parameter — see Spring Reference Chapter 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Assume this Url http://localhost:8080/SomeApp/user/1234/invoices?date=12-05-2013 (to get the invoices for user 1234 for today)
#RequestMapping(value="/user/{userId}/invoices", method = RequestMethod.GET)
public List<Invoice> listUsersInvoices(
#PathVariable("userId") int user,
#RequestParam(value = "date", required = false) Date dateOrNull) {
...
}
I have the following Rest controller:
#RestController
public class DocumentSearchController_global
{
#InitBinder//("TestCustomAnotation")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new ChekAtleastOneValueValidator());
}
#RequestMapping(value = "/validator", method = RequestMethod.POST, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
protected DocumentSearchResponse validatortest(#Valid #RequestBody TestCustomAnotation objDMSRequest, Errors e, BindingResult br) throws AppException
{
if(br.hasErrors())
System.out.println("ERRor");
if (e.hasErrors())
{
System.out.println("Got Error: "+ e.getFieldError());
}
DocumentSearchResponse objDocSearchResponse = null;
return objDocSearchResponse;
}
#ExceptionHandler
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public String handleMethodArgumentNotValidException(
MethodArgumentNotValidException error) {
System.out.println("ERROR-->>>>>>>>>>>>>>>>>>>>>>>>" +error.getMessage());
return "Bad request: " + error.getMessage();
}
}
And this is the bean where the request will be cast:
public class TestCustomAnotation
{
#ValidDocumentModifiedDate({"7Days", "30Days","60Days"})
String docModifiedDate;
#NotNull
String objectId;
#NotNull
String jobId;
Setter and GEtter
}
In the controller if I specify binder.setValidator(new
ChekAtleastOneValueValidator()); the contol will only go to
ChekAtleastOneValueValidator it will not check for #notnull
#ValidDocumentModifiedDate`
If I don't have binder.setValidator(new
ChekAtleastOneValueValidator()); then the control will check for
#notnull#ValidDocumentModifiedDate validation but not
ChekAtleastOneValueValidator.
My question is: is there a way in Spring to use Spring validation, custom annotation and #notnull annotation and get all the error of all the validation or spring allows to use only Spring validators?
Actually the question itself was wrong. I got the answer I use a Spring Validator class to validate all the request comming in and then use #validated in stead of #valid. I don't use annotation at the request anymore and let the class be a POJO. thats it problem solved