I am refactoring a legacy application to use Spring MVC. All of my controllers (legacy) return an object of type Model and my legacy dispatcher write the output of model.getContent(), the method getContent does internal processing and returns a json string. I have hundreds of controllers and do not want to rewrite them. Is it possible to write a custom view handler and include it in the spring servlet config?
Sample Controller:
public UserList extends BasicAction {
#Autowired
UserService userService;
#Autowired
UserCommand userCommand;
#Override
public Model getModel(Request req, Response resp)
throws ServletException, IOException {
Model model = new Model();
List<User> users;
try {
users = userService.getUsers((UserCriteria)userCommand.getResult());
model.addCollection(users);
model.setWrapper(new UserWrapper());
} catch (ValidationException e) {
e.printStackTrace();
} catch (WebCommandException e) {
e.printStackTrace();
}
return model;
}
}
I'm planning to annotate as #Controller. Specify the #RequestMapping or in the xml config, remove the base class BasicAction (legacy mvc). I've recently introduced spring to this project and refactored to use Dependency Injection and Request Scoped command objects (request wrappers)
The most straightforward is to implement View interface on your Model class. Then your legacy controllers can return this class directly (as they are now) and it will get rendered by DispatcherServlet via calling its render method.
Another possibility is to implement your own HandlerMethodReturnValueHandler, where the handler can actually render the response and mark the response as handled (mavContainer.setRequestHandled(true);) so that DispatcherServlet will not try to render any view.
I think what you want to do is create a custom ViewResolver that outputs your JSON response. This would be configured in Spring MVC to set the ViewResolver list, placing yours up top to have more precedence. The way it is supposed to work (from my recollection) is that Spring MVC will start at the top of the list, and try each ViewResolver until it finds one that returns the one that handles the return type. You will have to google how to make custom ViewResolvers, as I have only used them, never created one, but I know it's an interface so it should be do-able. I believe this would be the only way to do this that would not require any code changes in your controllers.
The more "preferred" method to do JSON, however, is to have Jackson in your classpath and simply return the object you want to serialize to JSON. Spring will auto-magically convert that to JSON, I believe using a ViewResolver they provide. But, I can surely relate to not wanting to refactor lots of working code.
Related
I am trying to set a default model value for all of my controllers, which is interpreted by my HTML template's layout (this is to add a top banner to all pages, such as to warn about upcoming maintenance). I wrote an #ControllerAdvice class with an #ModelAttribute method, and this works correctly on all of my custom controllers.
However, it does nothing when I visit a mapping registered directly with the ViewControllerRegistry; the method is simply never called, and ParameterizableViewController seems to bypass the normal binding and model generation.
Is there a way to write advice that will get applied to view controllers as well as custom controllers?
Is there a way to write advice that will get applied to view
controllers as well as custom controllers?
For your particular use case, I recommend to register a HandlerInterceptor and add your common model attributes to ModelAndView instance in postHandle method. Something like following:
public class CommonModelInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
modelAndView.addObject("Favorite Quote", "Welcome to the real world");
// Go crazy with modelAndView
}
}
Also, don't forget to register your interceptor:
public class WebConfig extends WebMvcConfigurerAdapter {
// Usual stuff
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CommonModelInterceptor());
}
}
#ControllerAdvice and other related annotations are handled by a RequestMappingHandlerAdapter registered automatically when you use #EnableWebMcv or
Other handling methods e.g resources or view controllers are handled by HttpRequestHandler and SimpleControllerHandlerAdapter which have no knowlege of #ControllerAdvice.
You can write a custom HandlerInterceptor which is application wide to handle the common logic and use the addInterceptors method of WebMvcConfigurer to register the interceptor
I'm assuming this is very easy to do, but I've been searching for a couple hours on Google and StackOverflow with no luck.
For starters, I have a jsp page using Dojo. I use Dojo's request module to make AJAX calls to Spring controllers for all sorts of things. The controllers call services, the services call DAOs, etc etc.
Originally, I had a properties file that contained several configuration settings for my site. These were injected into various Spring components using #Value. I wanted to make these dynamically configurable from my admin page, so I added a domain object called "SiteSettings" that holds the values from my properties file. When my webapp starts up, if it can't fine a SiteSettings object in persistence, it creates a new one, populates it with the settings from my properties file, and persists it. If one is already in persistence, it just uses it.
All of the site properties are used on the back-end except for one, a boolean called "authenticationRequired". I need to access this value in a javascript value on the front-end. This would be very easy to obtain via AJAX after the page loads, but I thought it's kind of silly to have to make an additional request, when I'm sure I should just be able to get the value from my SiteSettingsService (which uses the DAO to get the domain object).
Right now, I'm getting the value directly from the properties file:
<fmt:bundle basename="swtc">
<fmt:message key="swtc.authenticationRequired" var="authenticationRequired"/>
</fmt:bundle>
<script type="text/javascript">
window.authenticationRequired = <c:out value="${authenticationRequired}"/>;
</script>
How can I modify this get the value from my service/dao/domain object instead of directly from my properties file? Here are my controller and service files... nothing fancy:
Controller:
#RequestMapping(value = "/getSiteSettings", method = RequestMethod.GET)
#ResponseBody
public ModelMap getSiteSettings(ModelMap model) {
try {
SiteSettings siteSettings = siteSettingsService.getSiteSettings();
model.addAttribute("siteSettings", siteSettings);
model.addAttribute("success", true);
} catch (Exception ex) {
logger.error("There was an error getting the site settings data. ", ex);
model.addAttribute("exceptionMessage", ex.getLocalizedMessage());
model.addAttribute("success", false);
}
return model;
}
Service:
#Override
public SiteSettings getSiteSettings(){
List<SiteSettings> siteSettings = siteSettingsDao.findAll();
if (siteSettings != null && !siteSettings.isEmpty()){
return siteSettingsDao.findAll().get(0);
}
return null;
}
In the controller method that is responsible for rendering the overall jsp that requires the property, you should just be able to put the authenticationRequired attribute on the Model so it becomes available in the page for output into the Javascript. You'd need to autowire an instance of the SiteSettingsService into that controller:
#Controller
public class MainPageController { // or whatever it happens to be called
#Autowired
private SiteSettingsService siteSettingsService;
#RequestMapping(value="/mainPage", method=RequestMethod.GET)
public String displayMainPage(Model model) {
SiteSettings siteSettings = siteSettingsService.getSiteSettings();
// Make the property available to the view
model.addAttribute("authenticationRequired", siteSettings.isAuthenticationRequired());
return "mainPage";
}
}
Let say we have an API endpoint configured using Spring MVC and Spring Security. We would like to be able to handle pairs of #RequestMapping and #Secured annotations where the only #Secured annotation values differ from pair to pair. This way, we would be able to return a different response body depending on security rules for the same request.
This may allow our code to be more maintainable by avoiding to check for security rules directly into the method body.
With a not working example, here is what we would like to do :
#Controller
#RequestMapping("/api")
public class Controller {
#Secured ({"ROLE_A"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid) {
// Returns something for users having ROLE_A
}
#Secured ({"ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomethingDifferent(#PathVariable("uid") String uid) {
// Returns something different for users having ROLE_B
}
}
How can we achieve this ?
And if this can be done: How the priority should be managed for a user who has both ROLE_A and ROLE_B ?
Assuming you are using Spring 3.1 (or up) together with the RequestMappingHandlerMapping (and RequestMappingHandlerAdapter) you can extend the request mapping mechanism. You can do this by creating your own implementation of the RequestCondition interface and extend the RequestMappingHandlerMapping to construct this based on the #Secured annotation on your method.
You would need to override the 'getCustomMethodCondition' method on the RequestMappingHandlerMapping and based on the Method and the existence of the #Secured annotation construct your custom implementation of the RequestCondition. All that information is then taken into account when matching incoming requests to methods.
Related answers (although not specific for #Secured annotations but the mechanism is the same) is also to be found here or here
I don't think you can do this in spring-mvc, since both routes have exactly the same #RequestMapping (#Secured) is not taken into account by the route engine of spring-mvc. The easiest solution would be to do this:
#Secured ({"ROLE_A", "ROLE_B"})
#RequestMapping(value="{uid}", method=RequestMethod.GET)
#ResponseBody
public Response getSomething(#PathVariable("uid") String uid, Principal p) {
// Principal p gets injected by spring
// and you need to cast it to check access roles.
if (/* p.hasRole("ROLE_A") */) {
return "responseForA";
} else if (/* p.hasRole("ROLE_B") */) {
return "responseForB";
} else {
// This is not really needed since #Secured guarantees that you don't get other role.
return 403;
}
}
However, I would change your design, since the response is different per role, why not have 2 separate request mappings with slightly different URLs? If at some point you have users with role A and B at the same time, you can't let the user choose what response to get (think, for example, of the public and private profiles of LinkedIn)
This is the code example from Spring 3.1 Spring Source Blog: From XML to #Configuration I'm trying to implement in my application (which was done in Spring 2.0 not by me so it's lot of learning).
#FeatureConfiguration
class MvcFeatures {
#Feature
public MvcAnnotationDriven annotationDriven(ConversionService conversionService) {
return new MvcAnnotationDriven().conversionService(conversionService)
.argumentResolvers(new CustomArgumentResolver());
}
// ...
}
However, I can't understand the point of .argumentResolvers(new CustomArgumentResolver()) and their CustomArgumentResolver looks like bellow. What's the point of it?
public class CustomArgumentResolver implements WebArgumentResolver {
#Override
public Object resolveArgument(MethodParameter param, NativeWebRequest request) throws Exception {
RequestAttribute attr = param.getParameterAnnotation(RequestAttribute.class);
if (attr != null) {
return request.getAttribute(attr.value(), WebRequest.SCOPE_REQUEST);
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
To add to #GaryF's answer, and to clarify some points, Spring 2.5 introduced annotated controllers, which replaced the old interface-style controllers of Spring 2.0. These new controllers have methods with no fixed parameters - the method declares the parameters that it needs to do its job, and nothing more.
For example, say a controller method needed one thing to do its job - a request parameter that contains the ID of an object from the database. In Spring 2.0, you would need to implement something like AbstractController.handleRequestInternal(), e.g
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id");
MyThing obj = getObjById(id);
//... do stuff and return ModelAndView
}
Spring 2.5 made that easier:
#RequestMapping
public ModelAndView handle(String id) {
MyThing obj = getObjById(id);
//... do stuff and return ModelAndView
}
Here, we only declare parameters for the stuff we need.
So far so good, but this is where a custom WebArgumentResolver comes in. Say I want to remove the getObjById from my controller altogether, because maybe I think it clutters up the code, and maybe it's used across many other controller methods. Instead, I want to do this:
#RequestMapping
public ModelAndView handle(MyThing id) {
//... do stuff and return ModelAndView
}
It's even simpler, and has a bare minimum of boilerplate code. A custom WebArgumentResolver can be registered with the app-context which recognises parameters of type MyThing, and knows how to extract the information from the request. Spring invokes that resolver, and passes the result to the controller method.
Custom resolvers aren't commonly used, but can be very handy in the right situation.
The example in your question uses CustomArgumentResolver to resolve the example's custom RequestAttribute class. The resolver pulls out request attributes and binds them to RequestAttribute objects, so that they can be declared as controller method parameters.
WebArgumentResolvers are a way for you to specify how the parameters of MVC-mapped methods should be resolved. If you'd like to use a custom object as a parameter for an MVC-mapped method, Spring tries to figure out how make sense of it in it's own way. Typically this would happen through binding, where some http parameters you submit match up with the fields of the object and Spring matches them up and creates a new object for you.
If you ever have a situation where the submitted parameters don't match up quite so neatly with your method parameters, WebArgumentResolvers are there to fill in the gap: you provide custom logic so Spring doesn't have to figure it out.
In your example, param is one such parameter to be matched up. This piece of custom code first checks if the parameter has an #RequestAttribute annotation. If it does, then the custom code pulls the value from that object and looks it up as an attribute on the http request, returning it. It it does not have that annotation, then the method returns the UNRESOLVED value, which simply indicates that this WebArgumentResolver doesn't know anything about this particular parameter and Spring should try a different method (such as binding).
I'd like to use memcached to cache the response produced by my controllers. The controllers themselves are Grails controllers, but there's nothing really Grails-specific about the problem. If I could figure out how to solve this problem in a Spring MVC, Struts (or similar) application, I should easily be able to migrate the solution to Grails.
Ideally, I'd like to identify the controller methods that are eligible for caching using Java annotations. Is anyone aware of an existing solution for this problem? I should emphasise that I'm not interested in using any caching technology other than memcached.
Thanks,
Don
The Simple Spring Memcached library the previous poster linked to would actually accomplish what you need to do. It doesn't limit itself to just DAO methods. You could annotate a controller method to cache it's response just as easily as annotating a DAO method.
So, if you have a Controller named SimpleController and you wanted to cache the response of that controller, you could do the following
public class SimpleController implements Controller {
#ReadThroughSingleCache(namespace = "SimpleController", keyIndex = 0, expiration = 3600)
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
return new ModelAndView("index")
}
This will cache the response of the controller in Memcached for an hour and any request that comes in that matches the same request will return the cached response.
Aaron, braveterry,
Thanks for suggesting my project: http://code.google.com/p/simple-spring-memcached/
Don, Aaron is correct that SSM is not limited to DAO methods, however there are a few caveats for his example:
I don't think HttpServletRequest's toString() method would produce a good key
You would need to make sure that ModelAndView is Serializable
That being said, there's no reason you can't delegate to another bean that has an appropriate signature
Here's some code as an example:
public class SimpleController implements Controller {
private BeanWithAnnotatedMethod bean; // Injected resource
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
Object keyObject = Helper.generateAppropriateKey(request);
String result = bean.annotatedMethod(keyObject);
return new ModelAndView(result)
}
Would something like this do the trick? http://code.google.com/p/simple-spring-memcached/