Validating request parameter before controller - java

I want to validate the request parameter which is just a string value. Most of the examples on the net talks about validating a domain object with custom validator. But I want to write a validator just for String value. How to achieve that?
#Controller
#RequestMapping("/base")
class MyController{
//value needs to be validated.
#RequestMapping("/sub")
public String someMethod(#RequestParam String value, BindingResult result){
if(result.hasErrors()){
return "error";
}
//do operation
return "view";
}
}
I want to use the Validator interface that is already available in Spring, not AOP or any IF conditions

Your controller cannot validate the param, because BindingResult shall follow a ModelAttribute, not a RequestParam.
So if you want to use the Spring MVC automatic validation, you should use a class containing your string as a ModelAttribute :
class MyController {
#RequestMapping("/sub")
public String someMethod(#ModelAttribute Params params, BindingResult result){
if(result.hasErrors()){
return "error";
}
//do operation with params.value
return "view";
}
public static class Params {
// add eventual JSR-303 annotations here
String value;
// getter and setter ommited for brievety
}
}
Of course, this assumes you put a validator into the WebBinder for example through an #InitBinder annotated method of your controller.

//value needs to be validated.
#RequestMapping("/sub")
public String someMethod(#RequestParam String value, BindingResult result) throws SomeException {
if (!ValidationUtils.isValid(value)) {
throw new SomeException("Some text");
}

Related

When we must use #ModelAttribute, and how it works

Hello I have question about #ModelAttribute annotation. As i understand, we use #ModelAttribute in method arguments to get data from the model. But it's quite hard to understand clearly when and how its used.
(Code samples are from Spring in Action 5 book)
Why in this case in the code below in public String processOrder() method we do not use #ModelAttribute annotation on #Valid Order order
#Controller
#RequestMapping("/orders")
#SessionAttributes("order")
public class OrderController {
private OrderRepository orderRepo;
public OrderController(OrderRepository orderRepo) {
this.orderRepo = orderRepo;
}
#GetMapping("/current")
public String orderForm(#AuthenticationPrincipal User user,
#ModelAttribute Order order) {
if (order.getDeliveryName() == null) {
order.setDeliveryName(user.getFullname());
}
//following conditions
return "orderForm";
}
#PostMapping
public String processOrder(#Valid Order order, Errors errors, // <<< Here
SessionStatus sessionStatus,
#AuthenticationPrincipal User user) {
if (errors.hasErrors()) {
return "orderForm";
}
order.setUser(user);
orderRepo.save(order);
sessionStatus.setComplete();
return "redirect:/";
}
}
but in this case, DesignTacoController class, #ModelAttribute on a method processDesign() is used on #Valid Taco taco:
#Slf4j
#Controller
#RequestMapping("/design")
public class DesignTacoController {
#PostMapping
public String processDesign(#Valid #ModelAttribute("design") Taco design, // <<< Here
Errors errors, Model model) {
if (errors.hasErrors()) {
return "design";
}
// Save the taco design...
// We'll do this in chapter 3
log.info("Processing design: " + design);
return "redirect:/orders/current";
}
And then in the next chapter author removes #ModelAttribute from processDesign() method from the same DesignTacoController class.
#Controller
#RequestMapping("/design")
#SessionAttributes("order")
#Slf4j
public class DesignTacoController {
#ModelAttribute(name = "order")
public Order order() {
return new Order();
}
#ModelAttribute(name = "design")
public Taco design() {
return new Taco();
}
#PostMapping
public String processDesign(
#Valid Taco taco, Errors errors, // <<< Here
#ModelAttribute Order order) {
log.info(" --- Saving taco");
if (errors.hasErrors()) {
return "design";
}
Taco saved = tacoRepo.save(taco);
order.addDesign(saved);
return "redirect:/orders/current";
}
And in this code snippet(from the code above):
#PostMapping
public String processDesign(
#Valid Taco taco, Errors errors, // <<< Here
#ModelAttribute Order order) {
....
}
quote from book: "The Order parameter is annotated with #ModelAttribute to indicate that its
value should come from the model and that Spring MVC shouldn’t attempt to bind
request parameters to it."
This I don't understand what author meant here, because in all tutorials it is said that when #ModelAttribute is used as a method arguments,it binds request parameters to it. Binds the form data with a POJO bean, model attribute is populated with data from a form submitted.
The documentation is pretty clear on this:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
#ModelAttribute
For access to an existing attribute in the model (instantiated if not
present) with data binding and validation applied. See #ModelAttribute
as well as Model and DataBinder.
Note that use of #ModelAttribute is optional (for example, to set its
attributes). See “Any other argument” at the end of this table.
.
Any other argument
If a method argument is not matched to any of the earlier values in
this table and it is a simple type (as determined by
BeanUtils#isSimpleProperty, it is a resolved as a #RequestParam.
Otherwise, it is resolved as a #ModelAttribute.
So essentially it is optional. You may wish to use just to make it explicit that that is how the argument is resolved or you may need to use if binding should not happen (by specifying binding = false) See futher: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html. It is normally my preference to specify it regardless.
This wasn't clear to me either.
Here we need specify the name if the model attribute.
Because in our view we assume it is named "design" and not "taco".
#PostMapping
public String processDesign(#Valid #ModelAttribute("design") Taco design, Errors errors) {
If we rename the Taco class to Design ...
We don't need to specify the name if the model attribute.
It will be deduced from the simple name of the class.
com.example.Design -> "design"
#PostMapping
public String processDesign(#Valid Design design, Errors errors) {
See the javadoc for ModelAttribute:
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name: e.g. "orderAddress" for class
"mypackage.OrderAddress", or "orderAddressList" for
"List".

Does ordering of parameters in a method annotated by Spring MVC #RequestMapping matter? [duplicate]

I was struggling to get my Spring MVC validation to return to the page submitted page when I had errors. I finally solved the problem by noticing that BindingResult needs to be next to form parameter I'm validating.
For example if I amend the checkPersonInfo method in the spring.io tutorial(http://spring.io/guides/gs/validating-form-input/) to -
#RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(#Valid Person person, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
Then it will work and redirect to the form page, but if I change it to -
#RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(#Valid Person person, Model model, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
Then it redirects to /errors
What is the cause of this?
The BindingResult has to follow the object that is bound. The reason is that if you have more objects that are bound you must know which BindingResult belongs to which object.
Yeah, Today I took a long time to check why cannot back to the submitted page but goes to a default whitelable error page.
After debugging got the source code
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
if BindingResult does not follow #Valid , causes isBindExceptionRequired(binder, parameter) return true and then directly throw exception so cannot execute code in controller method.
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor#isBindExceptionRequired
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
int i = methodParam.getParameterIndex();
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
You can potentially have multiple model attributes in your request handler, each with their own binding result. To accomodate this, Spring decided to bind binding result parameters to the previous paramater.

Spring : Configure xml to make a controller return a view depending on a parameter

I have a spring MVC based application and I want to add a functionality in which some of my controllers will return the same view depending on the value of a parameter.
#RequestMapping("/someView")
public String returnView(Model model, HttpServletRequest request, String param){
if(param.equals("condition")){
return "commonView";
}
// do stuff
return "methodSpecificView";
}
Is there a way in which the first if condition can be configured in an xml? Since similar functionality needs to implemented in many controllers and I don't want to write boilerplate code an xml configuration can make things simpler.
Furthermore, if the first one is possible, can it be extended to eliminate the parameter param from request mapping method signature and put that in xml too?
You can use #RequestMapping:
#RequestMapping(value = {"/someView", "/anotherView", ...}, params = "name=condition")
public String returnCommonView(){
return "commonView";
}
In Spring 3.2 which is annotation based the below code snippet will give you an idea for your problem:
#RequestMapping("formSubmit.htm")
public String onformSubmit(#ModelAttribute("TestBean") TestBean testBean,BindingResult result, ModelMap model, HttpServletRequest request) {
String _result = null;
if (!result.hasErrors()) {
_result = performAction(request, dataStoreBean);//Method to perform action based on parameters recieved
}
if(testBean.getCondition()){
_result = "commonView";
}else{
_result = "methodSpecificView";
}
return _result;
}
TestBean//Class to hold all the required setters and getters
Explanation:
As the request from your view comes to this method the ModelAttribute reference will hold all the values from view if the condition is obtained from the view than you can directly obtain it from model attribute and return the corresponding view.
If your condition is obtained after applying certain logic than you can set the condition in the testBean and again get it to return the corresponding view.
You should consider implementing this via AOP - Around advice something like below.
#Around("#annotation(RequestMapping)") // modify the condition to include / exclude specific methods
public Object aroundAdvice(ProceedingJoinPoint joinpoint) throws Throwable {
Object args[] = joinpoint.getArgs();
String param = args[2]; // change the index as per convenience
if(param.equals("condition")){
return "commonView";
} else {
return joinpoint.proceed(); // this will execute the annotated method
}
}

Atributes for annotation ModelAtribute and PreAuthorize in Generic classes

In my spring project, I am trying set up generic classes for my controllers and service classes, with commons methods used by that classes. In my generic controller, each possible action the entities could receive (like insert, update, delete, select), are implemented with a pair of methods like that:
#RequestMapping(value="cadastra")
#PreAuthorize("hasPermission(#user, 'permission')")
public ModelAndView cadastra() throws InstantiationException, IllegalAccessException {
return new ModelAndView("privado/"+this.entity_name+"/cadastra", "command", this.entity.getClass().newInstance());
}
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
public String cadastra(#ModelAttribute("object") E object, BindingResult result) {
if(service.cadastra(object))
return "yes";
else
return "not";
}
and in my generic service class, this same action have related methods like that:
#PreAuthorize("hasPermission(#user, 'permissao')")
public boolean cadastra(E object) {
return dao.persist(object);
}
My question is which value I should use as atribute replacing permission and object above. The value for permssion follow this scheme:
<name_of_action>_<name_of_entity>
and the value for object is the name of each entity.
I try use the same structure I use inside the method (+this.entity_name+), but this cause an compilation error, because this annotations only accept constant arguments.
It was sugested to me use a generic class for my entities, but I can't figure out how to use that in my case.
Anyone can give a direction of how to accomplish what I want?
UPDATE
After some sugestions from other users from stackoverflow, I get to solve my problem with the ModelAtribute annotation. The final solution was this:
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
public String cadastra(#ModelAttribute("object") E object, BindingResult result) {
if(serv.cadastra(object))
return "yes";
else
return "not";
}
and I add this new method to my controller:
#ModelAttribute("object")
public E createCommandObject() throws InstantiationException, IllegalAccessException {
return (E) this.entityClass.newInstance();
}
Now I need only a solution for the PreAuthorize annotation, which uses the instruction #this.
So, I solve this issue with this approach:
1) For annotation PreAuthorize:
1.1) Adding a new method to my generic controller, where I return the name of the class:
public String getName() {
String expressao = entityClass.getName();
String nome_classe = new String();
StringTokenizer st = new StringTokenizer(expressao, ".");
while (st.hasMoreTokens()) {
nome_classe = st.nextToken();
}
return nome_classe;
}
1.2) Inside the annotation, I use the returned value by this method and concatenate the result with the constant string (using the notation described by the user #pgjecek in this topic):
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
and now it1s working perfectly.
2) For annotation ModelAtribute:
I keep using the form #ModelAttribute("object") E object, but add the method:
#ModelAttribute("object")
public E createCommandObject() throws InstantiationException, IllegalAccessException {
return (E) this.entityClass.newInstance();
}
which return a new instance of the desired object.

Removing duplication from Spring controllers

I have been looking for a way to somehow reduce the amount of code that is duplicated with subtle variance in my Spring MVC controllers, but searching through the SO questions so far has only yielded some questions without any satisfactory answers.
One example of duplication that I want to remove is this, where the user creation page and the role creation page share similarities:
#RequestMapping(value = "user/create", method = RequestMethod.GET)
public String create(#ModelAttribute("user") User user, BindingResult errors) {
LOG.debug("Displaying user creation page.");
return "user/create";
}
#RequestMapping(value = "role/create", method = RequestMethod.GET)
public String create(#ModelAttribute("role") Role role, BindingResult errors) {
LOG.debug("Displaying role creation page.");
return "role/create";
}
A slightly more involved variant of duplication that I would like to remove is the one for posting the create form:
#RequestMapping(value = "user/create", method = RequestMethod.POST)
public String save(#ModelAttribute("user") User user, BindingResult errors) {
LOG.debug("Entering save ({})", user);
validator.validate(user, errors);
validator.validatePassword(user, errors);
validator.validateUsernameAvailable(user, errors);
String encodedPassword = encoder.encode(user.getPassword());
user.setPassword(encodedPassword);
if (errors.hasErrors()) {
return create(user, errors);
} else {
service.save(user);
}
return "redirect:/user/index/1";
}
#RequestMapping(value = "role/create", method = RequestMethod.POST)
public String save(#ModelAttribute("role") Role role, BindingResult errors) {
LOG.debug("Entering save({})", role);
validator.validate(role, errors);
if (errors.hasErrors()) {
return create(role, errors);
} else {
service.save(role);
}
return "redirect:/index";
}
This example includes a validate then save if correct and a redirect to the error page if things don't go as planned.
How to remove this duplication?
Spring uses your handler method parameter types to create class instances from the request parameters or body. As such, there is no way to create a handler (#RequestMapping) method that could take an Object and check if it is either a Role or a User. (Technically you could have both parameters and just check which one isn't null, but that is terrible design).
Consequently, you need a handler method for each. This makes sense since, even through the logic is similar, it is still specific to the exact type of model object you are trying to create. You perform different validation, call a different service method, and return a different view name.
I say your code is fine.
Thought I would provide the solution that I settled on in the hope that it might help someone. My gf suggested that I use the name of the entity as a path variable for the controller, and this has proved to provide a very nice solution for the problem at hand.
The two methods now look like this:
#RequestMapping(value = "{entityName}/create", method = RequestMethod.GET)
public String create(#PathVariable("entityName") String entityName, #ModelAttribute("entity") BaseEntity entity, BindingResult errors) {
LOG.debug("Displaying create page for entity named: [{}]", entityName);
return handlerFactory.getHandler(entityName).getCreateView();
}
#RequestMapping(value = "{entityName}/create", method = RequestMethod.POST)
public String save(#PathVariable("entityName") String entityName, #ModelAttribute("entity") BaseEntity entity, BindingResult errors) {
LOG.debug("Saving entity of type {}", entityName);
CrudHandler handler = handlerFactory.getHandler(entityName);
handler.getCreateValidator().validate(entity, errors);
if (errors.hasErrors()) {
return create(entityName, entity, errors);
}
handler.preSave(entity);
handler.getService().save(entity);
return "redirect:" + DASHBOARD_URL;
}
The CrudHandler interface has implementations for each entity, and provides the controller with the entity specific classes that it needs, such as service and validator. A sample CrudHandler implementation looks like this for me:
#Component
public class RoleCrudHandler implements CrudHandler {
private static final String ENTITY_NAME = "role";
public static final String CREATE_VIEW = "role/create";
public static final String EDIT_VIEW = "role/edit";
#Resource
private RoleService roleService;
#Resource
private RoleValidator validator;
#Resource
private CrudHandlerFactory handlerFactory;
#PostConstruct
public void init() {
handlerFactory.register(ENTITY_NAME, this);
}
#Override
public GenericService getService() {
return roleService;
}
#Override
public Validator getCreateValidator() {
return validator;
}
#Override
public Validator getUpdateValidator() {
return validator;
}
#Override
public BaseEntity createEntity() {
return new Role();
}
#Override
public void preSave(BaseEntity entity) {
}
#Override
public String getCreateView() {
return CREATE_VIEW;
}
#Override
public String getUpdateView() {
return EDIT_VIEW;
}
}
If someone sees some ways to improve this, feel free to share. Hope this will be of use for someone.

Categories

Resources