We have a case where a controller must always be executed in every request to set some default values in the model next we execute the required controller. How to set a master controller to be always executed including other controller in the same request?
You can make a base controller class, with a #ModelAttribute annotated method which getscalled on every request, before an actual handler method, e.g
#ModelAttribute
public void everyRequest(WebRequest request, Model model) {
model.addAttribute("default", true);
}
all thats left is to extend this base controller class from your actual controllers
If you're using Spring 4.x, a better approach is to use a #ControllerAdvice which assist all, or a selected group of components and can be used for either adding the model attribute, apply common init binding or error handling. An example
#ControllerAdvice
class Advice {
#ModelAttribute
public void everyRequest(Model model) {
model.addAttribute("default", true);
}
}
Use Spring HandlerInterceptor #preHandle, it provides you access to the Handler which may be useful and also provides you with the power to exclude calling of some of the controllers.
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/admin/**"/>
<bean class="com.test.yourInterceptorClass" />
</mvc:interceptor>
Related
I have custom validator and I register it in my controller
#Controller
public class MyController {
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new FooValidator());
}
#RequestMapping("/foo", method=RequestMethod.POST)
public void processFoo(#Valid Foo foo) { ... }
}
but I want to register in other controllers also,so to be able just to write #Valid and the Foo object to be validated. From what I see I understand that I can use #ControllerAdviced class which to register the validator on every controller, or to use
<mvc:annotation-driven validator="globalValidator"/>
But how to register my validator, how Spring understand which Validator I want to make global one? Scans for every implementing Validator class? Can I do it with xml configuration? How to use this approach?
I do not understand the Spring's description:
The alternative is to call setValidator(Validator) on the global
WebBindingInitializer. This approach allows you to configure a
Validator instance across all annotated controllers. This can be
achieved by using the SpringMVC namespace:
xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xss
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:annotation-driven validator="globalValidator"/>
The documentation is quite clear on the Validation section:
In Spring MVC you can configure it for use as a global Validator
instance, to be used whenever an #Valid or #Validated controller
method argument is encountered, and/or as a local Validator within a
controller through an #InitBinder method. Global and local validator
instances can be combined to provide composite validation
If I understand correctly in your example the FooValidator you want to use it upon every validation as global Validator so define it as a bean and inject it as you show directly in the mvc:annotation-driven XML entry as you are showing already.
On top of that per-Controller you can have custom (applied on top only on that Controller-responsible forms) via the #InitBinder annotation.
As a side note, in your #RequestMapping method receiving the POST request where your #Valid parameter is: You can have a BindingResult entry right after that to take decisions on routes etc. In your example:
#RequestMapping("/foo", method=RequestMethod.POST)
public String processFoo(#Valid Foo foo, BindingResult result) {
if(result.hasErrors()) {
return "go/that/way";
}
//..
}
Is it possible to create a Java annotations equivalent to a Spring MVC #RequestMapping with a predefined set of parameters?
For instance, something that allows me to simply use:
#PostJson("/input")
public Output myMethod(Input input) {
instead of:
#RequestMapping(value = "/input",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Output myMethod(Input input) {
UPDATE:
Let me try to dig a little deeper. AFAIK, Spring seems to be able to handle meta-annotations when scanning for beans. For instance, if I create an annotation, lets say #MyComponent, and annotate it with #Component:
#Component
public #interface MyComponent {
String value() default "";
}
Spring seems able to find beans with #MyComponent and to recognize the parameters (value in this case) in #MyComponent as if they were from #Component
#MyComponent("MyBean")
public class SomeClass {
I've tryed similar tactic with #RequestMapping
#RequestMapping(method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public #interface PostJson {
String value() default "";
}
The fixed parameters (method an produces) seems to be correct, nonetheless, the variable parameter (value) is ignored.
I am hoping that this is not a #Component specific feature and that I could use it with #RequestMapping.
Not easily, no. The #RequestMapping annotation is tied to RequestMappingHandlerMapping and RequestMappingHandlerAdapter. These are two elements of the MVC stack registered by default when you provide <mvc:annotation-driven /> or #EnabledWebMvc. They only scan for #Controller and #RequestMapping, nothing else.
To make your own annotation work, you'd have to override (and register) these two or create new ones from scratch which provide the scanning and handling of handler methods. You can get some inspiration from those classes and other HandlerMapping implementations, but it really isn't a simple task.
You might, alternatively, want to look into Java Restful Web Services which can integrate quite well with Spring (not necessarily Spring MVC though). It can provide some less bloated mapping annotations when you know exactly what you want.
While this isn't currently supported out of the box but will be (thanks for creating https://jira.spring.io/browse/SPR-12296!), it isn't hard to do. If you look in RequestMappingHandlerMapping the protected method getMappingForMethod accepts a method and returns a RequestMappingInfo populated with information from the type + method-level #RequestMapping annotations. You can however populate this RequestMappingInfo from anything, e.g. your own annotation, or some other source (external routes configuration). Just follow the example of the code there.
I have an existing Service layer of Java code that I'd like to use in some REST calls. The way I'd like to do this is to have a user pass in a service ID in the URL, and then on the backend lookup the service and method (in a DB or config file) and call it. For example:
http://foobar.com/rest/car
When this URL is called, I would take the serviceId of "car" and call the CarService. I imagine I'd have a simple configuration:
car=com.foobar.services.CarService
house=com.foobar.services.HouseService
etc..
Is there a way to do this using Spring? One concern I have is not calling the service, but figuring out which method to call. If I had a call to http://foobar.com/services/car/red - how would I pass in the method parameter of 'red' and decide which method to call?
Here's an example of what this would look like in Java:
#RequestMapping(value = "{serviceId}")
#ResponseBody
public Object getMarshalledObject(#PathVariable String serviceId) {
if ("car".equals(serviceId)) {
return getCar();
}
throw new ServiceNotFoundException("Service ID not found.");
}
I would make separate controllers for each service, and have each controller delegate to its corresponding service after it extracted the relevant information from the request.
Due to the nature of #RequestMapping on controllers and their methods, this should be pretty easy:
#RequestMapping("/car")
class CarController {
#Autowired
private CarService service;
#RequestMapping("/{color}")
public Object getCarsByColor(#PathVariable String carColor) {
return service.getCarsByColor(houseColor);
}
}
#RequestMapping("/house")
class HouseController {
#Autowired
private HouseService service;
#RequestMapping("/{houseId}")
public Object getHouseById(#PathVariable int houseId) {
return service.getHouseById(houseId);
}
}
What we have here is two different controllers, with different services, that are mapped by the #RequestMapping that is applied to the class. Further, the controller methods are called by the remaining path elements from the url.
Instead of a simple properties file where you have this...
car=com.foobar.services.CarService
house=com.foobar.services.HouseService
...configure Spring (in the appropriate dispatch configuration file) to manage those beans for you:
<bean id="car" class="com.foobar.services.CarService" />
<bean id="house" class="com.foobar.services.HouseService" />
Assuming your service classes implement a common interface (for example, com.foobar.services.BaseService), in your controller you can autowire them up like so:
#Autowired
#Qualifier("car")
private BaseService _carService;
#Autowired
#Qualifier("house")
private BaseService _houseService;
I'm trying to protect a Controller with the #PreAuthorize annotation at type level and try to override that behavior by annotating some methods with a different #PreAuthorize. The Problem is however, that Spring is evaluating the method annotation first (grants access) and is then evaluating the class annotation (denies access).
Is there any way to reverse that order? I couldn't figure it out yet.
Edit:
On the method level, I want to grant access to non-registered Users only:
#PreAuthorize("isAnonymous()")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String renderCreateEntity(ModelMap model) {
return userService.renderCreateEntity(model);
}
The standard for this Controller however, should be to allow fully authenticated users only:
#Controller
#RequestMapping(value = "/user")
#PreAuthorize("isFullyAuthenticated()")
public class UserController { [...] }
When debug-stepping through the app, I see that isAnonymous() is evaluated first and then isFullyAuthenticated() thus resulting in an grant of access right and immediately denying access again.
Thanks for all your replys.
The answer however, was something totally different :)
I put this here in case anyone else has the same problems.
I registered a custom validator in an #InitBinder annotated method. This binding method is called AFTER the method call requested on the controller. And since this binding method was not annotated with #PreAuthorize, the request was denied.
The solution was to annotate the binding method like this:
#InitBinder
#PreAuthorize("permitAll")
public void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
And then, the method calls from my OP evaluated like expected.
The problem is not that you need to change the order of grant and deny. The problem is simple that that method level annotations override the class level annotations.
PrePostAnnotationSecurityMetadataSource Java Doc:
Annotations may be specified on classes or methods, and method-specific annotations will take precedence.
The concrete implementation of this logic is done in the method findAnnotation of class PrePostAnnotationSecurityMetadataSource. (Unfortunately this method is private.)
So you can write your own MethodSecurityMetadataSource, if you have a look at the code of PrePostAnnotationSecurityMetadataSource, you will see how easy it is.
But one warning at the end: the end: difficult task is not rewriting the method, the difficult task is to "inject" the new MethodSecurityMetadataSource into the security system. I belive you can not do it with the spring security namespace configuration, so you need to replace spring security namespace by explicit bean declaration.
We are working on a Spring 3.0.5 Web MVC-based application. In our code we quite often do something like this:
#ModelAttribute(ModelKeys.SOME_BEAN)
public SomeBean newSomeBean() {
return new SomeBean();
}
I think this is not necessary. But if it really wasn't, then I wonder how has this managed to slip through so many code reviews? In my understanding, if a controller method wanted a NEW SomeBean, then annotating a parameter in that method with #ModelAttribute should be enough? Spring would then use the default constructor to new up the required bean for invoking the controller method, e.g.:
#RequestMapping(method = RequestMethod.POST)
public String doIt(
#ModelAttribute(ModelKeys.SOME_BEAN) final SomeBean bean,
final BindingResult bindingResult)
{
...
}
here, Spring would new up an instance of SomeBean and try to data-bind into it from the POSTed data, right? There's no need for the method as shown in the first code snippet? Please can you confirm this or provide me with your thoughts on this? Would I be introducing a risk if I just went ahead and removed all these methods that do nothing other than new up an empty bean?
#ModelAttribute annotation on a method does not bind the bean attributes with HTTP request parameters. This is the key difference from the same annotation on a method parameter. Such approach is useful to populate some data that does not depend on request parameters in the model, for instance, values of a comboboxes taken from dictionaries. This is especially helpful, if you have several handler methods in a controller, e.g. to view/change/delete the same type of object and you need the same set of model attributes in all of them.