I have a simple request call in a spring mvc controller
#ResponseBody
#RequestMapping(value = "/url", method = RequestMethod.GET)
public SomeDTO getSth(#RequestParam("paramA") Integer paramA, #RequestParam("paramB") Integer paramB) {
// ...
}
and I want to have either the paramA or the paramB otherwise a normal http response as it currently happens if I do not provide both parameters.
I know there is a required parameter available, but I do not see a way to connect both. Any idea?
I can't think of a very good solution, but the straightforward one seems to be normal, isn't it?
#ResponseBody
#RequestMapping(value = "/url", method = RequestMethod.GET)
public String getSth(#RequestParam(value = "paramA", required = false) Integer paramA,
#RequestParam(value = "paramB", required = false) Integer paramB) {
if (paramA == null ^ paramB == null) {
return "body";
} else {
throw new BadRequestException();
}
}
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public static class BadRequestException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
Related
I am not getting this. I have a #RestController which is supposed to handle requests like /foo?latitude=15.12345. Of course there should be more parameters, but it's not even working for one.
This is the controller:
#RestController
public class GooglePlaceController {
private final static Logger LOGGER = LogManager.getLogger(GooglePlaceController.class);
#Autowired
private GooglePlaceService googlePlaceService;
#RequestMapping(value = "/foo", method = RequestMethod.GET)
public List<GooglePlaceEntity> doNearbySearch(#PathVariable Float latitude) {
LOGGER.trace("Searching for places nearby.");
return null;
}
}
and this is the request I am building:
public ResponseEntity<List<PlaceDto>> nearbySearch(Float lat, Float lng, Integer radius, Boolean googleSearch) {
String href = UriComponentsBuilder.fromHttpUrl("http://localhost:8081/foo")
.queryParam("latitude", lat)
.build().encode().toString();
ResponseEntity<Object> forEntity = this.oauthRestTemplate.getForEntity(href, null, Object.class);
return null;
}
However, I am getting this exception below, unless I remove #PathVariable Float latitude in which case the request gets handled correctly.
org.springframework.web.client.HttpServerErrorException: 500 null
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:97)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:79)
at org.springframework.security.oauth2.client.http.OAuth2ErrorHandler.handleError(OAuth2ErrorHandler.java:84)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:730)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:686)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:361)
at mz.api.client.Client.nearbySearch(Client.java:191)
But here is the thing:
In another controller I am having this:
#RequestMapping(value = "/groups/{groupId}/places", method = RequestMethod.GET)
public List<PlaceEntity> getGooglePlaces(#PathVariable Long groupId) {
return this.userGroupService.getPlaces(groupId);
}
#RequestMapping(value = "/groups/{groupId}/google-places", method = RequestMethod.POST)
public void addGooglePlace(#PathVariable Long groupId, #RequestParam String googlePlaceId) {
this.userGroupService.addGooglePlace(groupId, googlePlaceId);
}
and those requests are working without any problems.
#PathVariable is for parameters which are part of the URL like: /groups/{groupId}/google-places.
If parameter is after the ? you should use #RequestParam annotation.
#RequestMapping(value = "/foo", method = RequestMethod.GET)
public List<GooglePlaceEntity> doNearbySearch(#RequestParam Float latitude) {
LOGGER.trace("Searching for places nearby.");
return null;
}
#Controller
#EnableWebMvc
#Validated
public class ChildController extends ParentController<InterfaceController> implements InterfaceController{
#Override
#RequestMapping(value = "/map/{name}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public List<Friends> getAllFriendsByName(
#Valid
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable("name") String name,
#RequestParam(value="pageSize", required=false) String pageSize,
#RequestParam(value="pageNumber", required=false) String pageNumber,
HttpServletRequest request) throws BasicException {
//Some logic over here;
return results;
}
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
Hi, I am trying to do pretty basic validation for a spring request parameter but it just doesn't seem to call the Exception handler, could someone point me into the right direction
P.S. I keep getting NoHandlerFoundException
Spring doesn't support #PathVariable to be validated using #Valid. However, you can do custom validation in your handler method or if you insist on using #Valid then write a custom editor, convert your path variable value to an object, use JSR 303 bean validation and then use #Valid on that object. That might actually work.
Edit:
Here's a third approach. You can actually trick spring to treat your path variable as a model attribute and then validate it.
1. Write a custom validator for your path variable
2. Construct a #ModelAttribute for your path variable and then use #Validator (yes not #Valid as it doesn't let you specify a validator) on that model attribute.
#Component
public class NameValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
String name = (String) target;
if(!StringUtils.isValidName(name)) {
errors.reject("name.invalid.format");
}
}
}
#RequestMapping(value = "/path/{name}", method = RequestMethod.GET)
public List<Friend> getAllFriendsByName(#ModelAttribute("name") #Validated(NameValidator.class) String name) {
// your code
return friends;
}
#ModelAttribute("name")
private String nameAsModelAttribute(#PathVariable String name) {
return name;
}
How can i validate my path variable in spring. I want to validate id field, since its only single field i do not want to move to a Pojo
#RestController
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(#PathVariable String id) {
/// Some code
}
}
I tried doing adding validation to the path variable but its still not working
#RestController
#Validated
public class MyController {
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity method_name(
#Valid
#Nonnull
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable String id) {
/// Some code
}
}
You need to create a bean in your Spring configuration:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
You should leave the #Validated annotation on your controller.
And you need an Exceptionhandler in your MyController class to handle theConstraintViolationException :
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
After those changes you should see your message when the validation hits.
P.S.: I just tried it with your #Size validation.
To archive this goal I have apply this workaround for getting a response message equals to a real Validator:
#GetMapping("/check/email/{email:" + Constants.LOGIN_REGEX + "}")
#Timed
public ResponseEntity isValidEmail(#Email #PathVariable(value = "email") String email) {
return userService.getUserByEmail(email).map(user -> {
Problem problem = Problem.builder()
.withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
.withTitle("Method argument not valid")
.withStatus(Status.BAD_REQUEST)
.with("message", ErrorConstants.ERR_VALIDATION)
.with("fieldErrors", Arrays.asList(new FieldErrorVM("", "isValidEmail.email", "not unique")))
.build();
return new ResponseEntity(problem, HttpStatus.BAD_REQUEST);
}).orElse(
new ResponseEntity(new UtilsValidatorResponse(EMAIL_VALIDA), HttpStatus.OK)
);
}
This is what I am trying to achieve:
I have an update request object and user is allowed to do Partial Updates. But I want to validate the field only if it is in the request body. Otherwise, it is OK to be null. To achieve this, I am using GroupSequenceProvider to let the Validator know what groups to validate. What am I doing wrong here? If there is a blunder, how do I fix it?
Documentation: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html#example-implementing-using-default-group-sequence-provider
#GroupSequenceProvider(UpdateUserRegistrationGroupSequenceProvider.class)
public class UpdateUserRegistrationRequestV1 {
#NotBlank(groups = {EmailExistsInRequest.class})
#Email(groups = {EmailExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {EmailExistsInRequest.class})
private String email;
#NotNull(groups = {PasswordExistsInRequest.class})
#Size(min = 8, max = 255, groups = {PasswordExistsInRequest.class})
private String password;
#NotNull(groups = {FirstNameExistsInRequest.class})
#Size(max = 255, groups = {FirstNameExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {FirstNameExistsInRequest.class})
private String firstName;
// THERE ARE GETTERS AND SETTERS BELOW
}
Group Sequence Provider Code:
public class UpdateUserRegistrationGroupSequenceProvider implements DefaultGroupSequenceProvider<UpdateUserRegistrationRequestV1> {
public interface EmailExistsInRequest {}
public interface PasswordExistsInRequest {}
public interface FirstNameExistsInRequest {}
#Override
public List<Class<?>> getValidationGroups(UpdateUserRegistrationRequestV1 updateUserRegistrationRequestV1) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add(Default.class);
defaultGroupSequence.add(UpdateUserRegistrationRequestV1.class);
if(StringUtils.hasText(updateUserRegistrationRequestV1.getEmail())) {
defaultGroupSequence.add(EmailExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getPassword())) {
defaultGroupSequence.add(PasswordExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getFirstName())) {
defaultGroupSequence.add(FirstNameExistsInRequest.class);
}
return defaultGroupSequence;
}
}
I am using Spring MVC, so this is how my controller method looks,
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable("userId") Long userId,
#RequestBody #Valid UpdateUserRegistrationRequestV1 request) {
logger.info("Received update request = " + request + " for userId = " + userId);
registrationService.updateUser(userId, conversionService.convert(request, User.class));
}
Now the problem is, the parameter "updateUserRegistrationRequestV1" in the UpdateUserRegistrationGroupSequenceProvider.getValidationGroups method is null. This is the request object that I am sending in the request body and I am sending email field with it.
What am I doing wrong?
I too went through the same issue ,and hopefully solved it
You just have to check the object is null and put all your conditions inside it.
public List<Class<?>> getValidationGroups(Employee object) {
List<Class<?>> sequence = new ArrayList<>();
//first check if the object is null
if(object != null ){
if (!object.isDraft()) {
sequence.add(Second.class);
}
}
// Apply all validation rules from default group
sequence.add(Employee.class);
return sequence;
}
I'm working on REST API based on Spring 3 MVC. In each call I'm adding to JSON response two variables: 'description' and 'result'.
For example:
#RequestMapping(value = "entity.htm", method = RequestMethod.GET)
public ModelAndView get() {
ModelAndView mav = new ModelAndView(JSON_VIEW);
mav.addObject("description", "entity list");
mav.addObject("result", someService.getAll());
return mav;
}
Does it make sense for performance of the app to create a pool of private static final strings and use them every time I need?
I mean like this:
#Controller
public class MyController {
private static final String JSON_VIEW = "jsonView";
private static final String VAR_DESCRIPTION = "description";
private static final String VAR_RESULT = "result";
private static final String DESC_CREATED = "entity created";
private static final String DESC_ENTITY_LIST = "entity list";
private static final String DESC_ACCESS_DENIED = "forbidden";
#RequestMapping(value = "entity.htm", method = RequestMethod.PUT)
public ModelAndView put(HttpServletResponse response) {
ModelAndView mav = new ModelAndView(JSON_VIEW);
if (!entityService.someChecking()) {
mav.addObject(VAR_DESCRIPTION, DESC_ACCESS_DENIED);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
mav.addObject(VAR_DESCRIPTION, DESC_CREATED);
mav.addObject(VAR_RESULT, entityService.save(new Entity()));
response.setStatus(HttpServletResponse.SC_CREATED);
}
return mav;
}
#RequestMapping(value = "entity.htm", method = RequestMethod.GET)
public ModelAndView get(HttpServletResponse response) {
ModelAndView mav = new ModelAndView(JSON_VIEW);
if (!entityService.someChecking()) {
mav.addObject(VAR_DESCRIPTION, DESC_ACCESS_DENIED);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
mav.addObject(VAR_DESCRIPTION, DESC_ENTITY_LIST);
mav.addObject(VAR_RESULT, entityService.getAll());
}
return mav;
}
// and so on
}
Someone of these statuses I use only once, but DESC_ACCESS_DENIED I use up to 10 times in one REST controller.
Your get is not returning json, it returns a view.
I prefer using an enum instead of static final ints - easier to add functionality later.
Yes, it does make sense. It's a good pratice. It save's you time and effort if you ever need to change this values. It's quite insignificant in terms of memory use or process time, but it's better.
If you intend to use those strings more than once, then it is a good pratice to turn then into static final. But notice your methods aren't returning JSON responses. A JSON response is something like that:
#RequestMapping(value = "/porUF", method = RequestMethod.GET)
public #ResponseBody List<Municipio> municipios(
#RequestParam(value = "uf", required = true) String uf) {
if ( uf.length() != 2) {
return null;
}
return municipioBO.findByUf(uf);
}
The #ResponseBody annotation will transform the List into a JSON object, and the response of a HTTP GET for that is something like that:
[{"codigo":9701,"uf":{"uf":"DF","nome":"DISTRITO FEDERAL"},"nome":"BRASILIA "}]
This is a JSON response.