Spring request params to accept only Integers - java

I am working on an spring restful endpoint which accepts page range(start & end page number). I want my request params- pageStart and pageEnd to accept only integers. When I pass 'pageStart = a' through postman I get below error:
#RequestMapping(value = "/{accNumber}/abc/xyz", method = RequestMethod.GET)
#Loggable
#ResponseBody
public RestResponse<Class1> getData(
#Loggable #PathVariable(value = "accNumber") String accNumber,
#RequestParam(value = "pageStart", required = false, defaultValue = "0") Integer pageStart,
#RequestParam(value = "pageEnd", required = false, defaultValue = "10") Integer pageEnd,
HttpServletResponse response) throws Exception {
Class1 class1 = new Class1();
class1 = retrieveData(accNumber, pageStart, pageEnd);
RestResponse<Class1> restResponse = new RestResponse<Class1>(
class1);
return restResponse;
}
The request is not valid [Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: \"a\"]
How do I handle this exception and let the user know that he should pass only integers?

You can handle it in two ways
1) Using exception handler method
Have a method in the controller
#ExceptionHandler({Exception.class})
public ModelAndView handleException(Exception ex) {
ModelAndView model = new ModelAndView("Exception");
model.addObject("exception", ex.getMessage());
return model;
}
http://www.codejava.net/frameworks/spring/how-to-handle-exceptions-in-spring-mvc
2) Use String parameter
Use String as the type for all #PathVariable and #RequestParameter parameters then do the parsing inside the handler method.

Related

Spring Boot - request body validation also for parameter TYPE

I wonder how to add validation for parameter type: I have endpoint:
#PostMapping(consumes = "application/json", path = "/v2/tw")
#ResponseStatus(HttpStatus.CREATED)
public TwDto add(
Authentication auth,
#Validated(TwDto.New.class) #RequestBody TwDto twDto,
BindingResult bindingResult) {
TwDto has inside
#Valid
#PositiveOrZero
#Pattern(regexp = "[0-9]", message = "id must be a number")
private Long storeId;
When I set storeId = "123someString" then I get alwasy 400 error without any message.
I want to send to the client eg. 404 eror code with some message - how to do that? I tried many optiosn and I get always 400 BAD REQUEST...
Or maybe I should do custom validation, eg go through some dto fields and in try catch check if storeId is number or not and then throw some error that will be catched in ExceptionHandler?
#Pattern(regexp = "[0-9]", message = "id must be a number") doesn't work, I dont see any message that should be returned with the error
If you want to maintain automatic validation you may need to change the type of storeId to String:
#Valid
#Pattern(regexp = "[0-9]*", message = "id must be a number")
private String storeId;
and then validation will work:
public class TwDtoTest {
#Test
void invalid_store_test() {
TwDto twDto = new TwDto();
twDto.setStoreId("aaaa");
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<TwDto>> violations = validator.validate(twDto);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(violation -> assertThat(
violation.getMessage()).isEqualTo("id must be a number")
);
}
#Test
void valid_store_test() {
TwDto twDto = new TwDto();
twDto.setStoreId("10");
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<TwDto>> violations = validator.validate(twDto);
assertThat(violations.size()).isEqualTo(0);
}
}
Of course, down the line, if you need to save or use the storeId as a Long you would need to convert it: Long.parseLong(storeId)

to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)

I am trying to route HttpServletRequest to another microservice where request may contain multi-part request or any normal request. but while sending I am getting the below error.
Note: I dont want to modify the request as I am trying to write some generic method.
public Object doPostCall(HttpServletRequest request, String requestURL, String rootURL)
throws URISyntaxException, IOException, ServletException {
RestTemplate restTemplate = new RestTemplate();
final String url = rootURL + requestURL;
uri = new URI(url);
try {
result2 = restTemplate.postForEntity(uri, request, Object.class);
System.out.println("after service call" + result2);
} catch (RestClientException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result2;
}
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No serializer found for class java.util.Collections$3 and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest["request"]->org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper["request"]->org.apache.catalina.connector.RequestFacade["attributeNames"])
Even I have tried setting this in property file. spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false, but it didn't work
My Consume controller is like below:
#PostMapping(value = "/v1/upload/{moduleName}/{fileType}", produces = "application/json")
public ResponseEntity<Object> uploadFiles(#RequestPart("file") List<MultipartFile> inputFileList,
#RequestParam(value = "createdBy", required = false) String createdBy, #PathVariable String moduleName,
#RequestParam(value = "catalogId", required = false) String catalogId,
#RequestParam(value = "catalogName", required = false) String catalogName, #PathVariable String fileType) {
Try annotating your entity class with #JsonIgnoreProperties("hibernateLazyInitializer")

Swagger example post body - how to show JSON Body- Swagger-annotations

Requirement: I have a POST method which takes the input JSON as a String and passes it to another microservice. I don't want to create an Object (Bean) of this input JSON.
method:
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
Problem:
The generated Swagger doesn't show the input as a JSON model where all the JSON attributes are displayed.
Expectation:
I want to display my Swagger Something like this :
Definately I am missing the key thing. Any thoughts?
If changing from String to a concrete object is not okay (although that's what I would recommend you to do since it's cleaner), you can try using #ApiImplicitParams (check out their documentation)
#ApiOperation(notes = "example" value = "/example", consumes = ".." , method= "..")
#ApiImplicitParams({
#ApiImplicitParam(name = "Object", value = "Object to be created", required = true, dataType = "your.package.BodyClass", paramType = "body")
})
#RequestMapping(name = "xxx" value ="/hello" ..)
#ApiResponses(..)
public #ResponseBody String getXXX (#Apiparam(name="JSONrequest", required = true) #RequestBody String JSONrequest){
}
(not sure if you still need the #Apiparam(name="JSONrequest", required = true) bit from the method parameter)
It's an old question but since I haven't found a solution online here how I to customized the example value in the swagger documentation produce automatically by the java annotations.
I use swagger 2.0 and springfox.version 2.10.5.
The Idea is documenting the class of the request parameter that has the #RequestBody annotation. for example my method is
#ApiOperation(
value = "Start ListBuilder extraction",
response = ExtractionLogEntity.class,
produces = "application/json"
)
#PostMapping("/extraction/start")
public ExtractionLogEntity startTask(
#RequestBody(required = true) ExtractionRequest request,
In order to expose request json object example I added a #ApiModelProperty(example = "...") annotation to the properties of ExtractionRequest .
#ApiModelProperty(example = "[{ 'field':'value'}]")
#NotNull
private List<ListBuilderFieldEntity> fields;
#ApiModelProperty(example = "1000")
private String ied;
#ApiModelProperty(example = "US")
private String codebase;
And that's the result
I had the similar issue. My Service Class takes #RequestBody argument in String.
So, what I did :
Created a POJO and used #RequestBody annotation with it instead of inputString.
#RequestMapping(value = "/api/entity/{entityId}/user/query", method = {RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
ResponseEntity<String> queryUser(#PathVariable("entityId") String entityId,
#RequestBody QueryUserJsonSchemaPOJO queryUserJsonSchemaPOJO, String inputString,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
return userService.queryUserService(inputString, entityId, request);
}
Created an AOP with #Around annotation which update the inputString argument.
#Around(value = "execution(* com.athmin.rest.UserController.*(..)) || execution(* com.athmin.rest.CityController.*(..)), and args(..) " +
" && #annotation(com.athmin.annotations.JSONSchemaFileName) ")
public Object validateRequestBodyAgainstJsonSchema(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] modifiedArgs = proceedingJoinPoint.getArgs();
for (Object o : proceedingJoinPoint.getArgs()) {
if (o instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) o;
requestBody = httpServletRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
});
for (int i = 0; i < modifiedArgs.length; i++) {
if (modifiedArgs[i] == null) { // Only inputString is null in my case
modifiedArgs[i] = requestBody;
}
}
proceedingJoinPoint.proceed(modifiedArgs);
}

Error 400--Bad Request

I have a project based in Spring Web model-view-controller (MVC) framework. The version of the Spring Web model-view-controller (MVC) framework is 3.2.8
I have this controller
#SuppressWarnings("unchecked")
#RequestMapping(value = { "/books/store/product",
"/books/store/product/",
"/books/store/product/{productId}",
"/books/store/product/{productId}/" }, method = { RequestMethod.POST })
public String saveProduct(#ModelAttribute("productForm") ProductForm productForm,
#PathVariable Long productId,
HttpServletRequest request, Model model) throws Exception {
..
}
Everything is fine for this URL : /books/store/product/232
but for this one /books/store/product/
I got this error:
Error 400--Bad Request
From RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1:
10.4.1 400 Bad Request
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.
I've tried to put this #PathVariable(required = false), but I got a compilation error: The attribute required is undefined for the annotation type PathVariable
This is because the service is always waiting for the path variable productId
Because you're using Spring 3 I suggest you to create 2 methods. One with the path variable and the other without it.
#RequestMapping(value = { "/books/store/product",
"/books/store/product/"}, method = { RequestMethod.POST })
public String saveProduct(#ModelAttribute("productForm") ProductForm productForm,
HttpServletRequest request, Model model) throws Exception {
..
}
#RequestMapping(value = { "/books/store/product/{productId}",
"/books/store/product/{productId}/" }, method = { RequestMethod.POST })
public String saveProduct(#ModelAttribute("productForm") ProductForm productForm,
#PathVariable Long productId,
HttpServletRequest request, Model model) throws Exception {
..
}
If you're using Spring 4 and Java 8 I suggest you to use optional.
#PathVariable Optional<Long> productId
If you do not always need productId. Try using query parameter and make it optional. required=false
This url will now look like:
http://localhost:8080/books/store/product?productId=232
http://localhost:8080/books/store/product
Like this:
#SuppressWarnings("unchecked")
#RequestMapping(value = { "/books/store/product",
}, method = { RequestMethod.POST })
public String saveProduct(#ModelAttribute("productForm") ProductForm productForm,
#RequestParam(value = "productId", required = false) Long productId,
HttpServletRequest request, Model model) throws Exception {
..
}
Hope it helps.

How do I retrieve query parameters in a Spring Boot controller?

I am developing a project using Spring Boot. I've a controller which accepts GET requests.
Currently I'm accepting requests to the following kind of URLs:
http://localhost:8888/user/data/002
but I want to accept requests using query parameters:
http://localhost:8888/user?data=002
Here's the code of my controller:
#RequestMapping(value="/data/{itemid}", method = RequestMethod.GET)
public #ResponseBody
item getitem(#PathVariable("itemid") String itemid) {
item i = itemDao.findOne(itemid);
String itemname = i.getItemname();
String price = i.getPrice();
return i;
}
Use #RequestParam
#RequestMapping(value="user", method = RequestMethod.GET)
public #ResponseBody Item getItem(#RequestParam("data") String itemid){
Item i = itemDao.findOne(itemid);
String itemName = i.getItemName();
String price = i.getPrice();
return i;
}
While the accepted answer by afraisse is absolutely correct in terms of using #RequestParam, I would further suggest to use an Optional<> as you cannot always ensure the right parameter is used. Also, if you need an Integer or Long just use that data type to avoid casting types later on in the DAO.
#RequestMapping(value="/data", method = RequestMethod.GET)
public #ResponseBody
Item getItem(#RequestParam("itemid") Optional<Integer> itemid) {
if( itemid.isPresent()){
Item i = itemDao.findOne(itemid.get());
return i;
} else ....
}
To accept both #PathVariable and #RequestParam in the same /user endpoint:
#GetMapping(path = {"/user", "/user/{data}"})
public void user(#PathVariable(required=false,name="data") String data,
#RequestParam(required=false) Map<String,String> qparams) {
qparams.forEach((a,b) -> {
System.out.println(String.format("%s -> %s",a,b));
}
if (data != null) {
System.out.println(data);
}
}
Testing with curl:
curl 'http://localhost:8080/user/books'
curl 'http://localhost:8080/user?book=ofdreams&name=nietzsche'
In Spring boot: 2.1.6, you can use like below:
#GetMapping("/orders")
#ApiOperation(value = "retrieve orders", response = OrderResponse.class, responseContainer = "List")
public List<OrderResponse> getOrders(
#RequestParam(value = "creationDateTimeFrom", required = true) String creationDateTimeFrom,
#RequestParam(value = "creationDateTimeTo", required = true) String creationDateTimeTo,
#RequestParam(value = "location_id", required = true) String location_id) {
// TODO...
return response;
#ApiOperation is an annotation that comes from Swagger api, It is used for documenting the apis.
To accept both Path Variable and query Param in the same endpoint:
#RequestMapping(value = "/hello/{name}", method = RequestMethod.POST)
public String sayHi(
#PathVariable("name") String name,
#RequestBody Topic topic,
//#RequestParam(required = false, name = "s") String s,
#RequestParam Map<String, String> req) {
return "Hi "+name +" Topic : "+ topic+" RequestParams : "+req;
}
URL looks like : http://localhost:8080/hello/testUser?city=Pune&Pin=411058&state=Maha
I was interested in this as well and came across some examples on the Spring Boot site.
// get with query string parameters e.g. /system/resource?id="rtze1cd2"&person="sam smith"
// so below the first query parameter id is the variable and name is the variable
// id is shown below as a RequestParam
#GetMapping("/system/resource")
// this is for swagger docs
#ApiOperation(value = "Get the resource identified by id and person")
ResponseEntity<?> getSomeResourceWithParameters(#RequestParam String id, #RequestParam("person") String name) {
InterestingResource resource = getMyInterestingResourc(id, name);
logger.info("Request to get an id of "+id+" with a name of person: "+name);
return new ResponseEntity<Object>(resource, HttpStatus.OK);
}
See here also

Categories