I was doing some practice programming with spring mvc and I decided to do an example regarding content negotiation.
I started with a uri "/products":
When I ask for /products.json it returns me json, which I am happy about
When I ask for /products.xml it returns the proper xml, again I am happy about that
When I ask for the html view (/products), at the moment I only display a simple html table for the products, but what if I want to include extra dynamic content for the html page like a tag cloud or similar products (things unrelated to products)?
Below is my code for the controller method.
#RequestMapping(method = RequestMethod.GET)
public ModelAndView getAllProducts(){
ModelAndView result = new ModelAndView("index");
GenericListElementWrapper<Product> products = new GenericListElementWrapper<Product>();
products.setList(productDao.getAll());
ModelMap modelMap = new ModelMap();
modelMap.addAttribute("products",products);
result.addAllObjects(modelMap);
return result;
}
What I would like to achieve is the following:
A way to have keep my single controller method but the html view would have extra content
The ideas that I had was:
Perhaps use servlet filters to enrich the ModelAndView just for the text/html mimetype only? But then you are doing this for all html requests which may be undesirable?
Currently the way I am explaining myself feels like I want a completely rendered html view to be sent to the client. Perhaps I am looking at this problem incorrectly and I should be thinking along the lines of retrieving the extra content after the page has been loaded via javascript?
So is it possible to achieve my intended solution? The other part is whether my intended solution is actually desirable in practice :P
A possibility is to add an interceptor and map it to your the path of your choice. The interceptor.postHandle provides access the ModelAndView after the handler on your controller is executed. Making it possible to add some extra's.
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/my/path"/>
<ref bean="enhancedContentInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
#Component
public class EnhancedContentInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
return true;
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
if (request.getContentType().equals("text/html")) {
modelAndView.addObject("tags", tagProvider.getTags());
}
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {}
}
}
Related
Thought this would be a common use case but I'm coming up short. Also, I'm using Scala but a Java answer would be fine, too.
In Jersey I'd like to set a simple cookie on all responses coming out of my app.
A quick google shows that I can set cookies on a single response by performing the following:
return Response.ok(new Viewable("/index", model))
.cookie(new NewCookie("name", "Hello, world!"))
.build();
That's great if I just want to set a cookie on a per-response basis, but I want it on every response. This seems like a job for middleware. The Jersey Docs recommend this for setting up middleware:
class MyResponseMiddleware ContainerResponseFilter {
override def filter(req: ContainerRequestContext, res: ContainerResponseContext) = {
// do stuff here
}
}
The problem is there's no way to set a cookie on the ContainerResponseContext as .getCookies returns a read-only map, unlike .getHeaders() which is mutable.
I also tried to create a cookie by setting the headers:
containerResponseContext.getHeaders().add(HttpHeaders.SET_COOKIE, new NewCookie(...)) but this did not make it to the browser.
It seems like if I could get a reference to ServletResponse or HttpServletResponse this would be simple but that doesn't appear to be possible in Jersey's middleware (filters).
This seems like a pretty straightforward use-case so I feel like I'm missing something obvious.
You can simply Inject HttpServletResponse into the filter.
Use #Context annotation as follows.
#Provider
public class ResponseHTTPStatusFilter implements ContainerResponseFilter{
#Context HttpServletResponse resp;
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
resp.addCookie(cookie);
}
}
So I'd like to return an HTML page without sending a redirect. The reason being is using a redirect changes the URL in the browser, and I can't redirect someone to the login if they aren't logged in. What's the most straight forward way to do this? Seems like it should be simple without using jsp or other server side view technologies.
You can use forward.
Example:
say /static/myWebpage.html is your static html page
This code will return the content of myWebpage.html without changing the url
#Controller
#RequestMapping("/path")
public class TestingController {
#RequestMapping("/page")
public String someOtherPage(HttpServletRequest request, HttpServletResponse response) {
return "forward:/static/myWebpage.html";
}
}
Again your url would be "localhost/path/page" but you will be viewing "localhost/static/myWebPage.html"
The answer given above is correct for most Spring configurations. Other times you'll just get a Content-Type: text/plain webpage with the forwarding information as the text. You must explicitly pass the forwarding information to a ModelAndView.
#Controller
#RequestMapping("/path")
public class TestingController {
#RequestMapping("/page")
public ModelAndView someOtherPage(HttpServletRequest request, HttpServletResponse response) {
return new ModelAndView("forward:/static/myWebpage.html");
}
}
#Controller
public class CentralizedExceptionController extends DefaultHandlerExceptionResolver {
#Override
protected ModelAndView handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("working?!");
return new ModelAndView();
}
I have this in my code, but in case of a 404 its never called.
(I dont have an error-page defined in my web.xml, and i dont want to)
Take a look at this jira issue: https://jira.springsource.org/browse/SPR-8837?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=72648#comment-72648
If your Spring dispatcher servlet is configured to process all/most URLs, then you are probably getting the 404 error along with this DispatcherServlet log message from console:
No mapping found for HTTP request with URI [xxx]
This indicates that Spring's DispatcherServlet is processing the request but do not have an appropriate #RequestMapping to dispatch to.
A simple solution would be to limit requests processed by dispatcher servlet by reconfiguring web.xml's servlet-mapping > url-pattern to only URLs specified by your application's #RequestMappings. However, this is NOT very practical (so don't do this).
One way to overcome this would be to create a #RequestMapping that handles all "unhandled" request mappings - some kind of fallback request mapping.
#RequestMapping("**")
#ResponseBody
public String fallbackRequestMapping() {
return "do something useful...";
}
Note that this answer is similar in approach to Dani's answer but written with annotation based development in mind. Therefore, it is useful to understand the associated Spring issue.
plz check. Your controller class name should not be Controller.java.
You need to use #ExceptionHandler annotation to your method:
#ExceptionHandler(NoSuchRequestHandlingMethodException.class)
public ModelAndView handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...
}
I have a Spring HandlerInterceptor intercepting the frontend URL's in my application (/app/*). I want to determine which action method in the Handler is about to be invoked from within the HandlerInterceptor. Is there a way to look that up, do I need to inject something into the interceptor that can look that up based on the requested path?
The Interceptor is like this:
public class PageCacheInterceptor implements HandlerInterceptor {...}
It is mapped like this:
<mvc:interceptors>
<bean class="com.example.web.interceptors.PageCacheInterceptor" />
</mvc:interceptors>
Background (because I know you'll ask!). I am adding simple page caching to my app and want to use an annotation like #Cacheable on each suitable method in the controller. The interceptor can then determine whether to cache a response based on the action that created it.
For example:
#RequestMapping(value = "", method = RequestMethod.GET)
#Cacheable(events={Events.NEW_ORDER,Events.NEW_STAT})
public String home(Model model) {...}
The events are the ones that cause the cache to be invalidated. For example /widget/list action would have it's cached response invalidated by a new widget being saved.
Edit: I've upgraded to the latest Spring 3.1 M2, as this blog post hinted at features I need, but it's not clear whether injecting these new classes or sub-classing them will be required. Has any one used them to retrieve the HandlerMethod in an interceptor?
Ok so the solution was actually really easy:
1) Upgrade to Spring 3.1
2) RTFM (properly)
For example a HandlerInterceptor can cast the handler from Object to HandlerMethod and get access to the target controller method, its annotations, etc
3) Cast the handler object to HandlerMethod in the Interceptor.
Then you can do this sort of thing:
HandlerMethod method = (HandlerMethod) handler;
Cacheable methodAnnotation = method.getMethodAnnotation(Cacheable.class);
if (methodAnnotation != null) {
System.out.println("cacheable request");
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre-handle");
HandlerMethod hm=(HandlerMethod)handler;
Method method=hm.getMethod(); if(method.getDeclaringClass().isAnnotationPresent(Controller.class)){
if(method.isAnnotationPresent(ApplicationAudit.class))
{
System.out.println(method.getAnnotation(ApplicationAudit.class).value());
request.setAttribute("STARTTIME",System.currentTimemillis());
}
}
return true;
}
This post has more details,hope this helps http://www.myjavarecipes.com/spring-profilingaudit-using-mvc-filters/
Here is my problem, I need to map a AJAX request using spring. Now, I know that I need these two guys:
HttpServletRequest, to get the message the client sent to me and parse it from JSON(most likely) to a Map and HttpServletResponse to put my message to the client.
What I do not know is the right(simple, concise) way to do that...
Here is a code sample from the springframework site:
/**
* Normal comments here
*
* ##org.springframework.web.servlet.handler.metadata.PathMap("/foo.cgi")
* ##org.springframework.web.servlet.handler.metadata.PathMap("/baz.cgi")
*/
public class FooController extends AbstractController {
private Cruncher cruncher;
public FooController(Cruncher cruncher) {
this.cruncher = cruncher;
}
protected ModelAndView handleRequestInternal (
HttpServletRequest request, HttpServletResponse response) throws Exception {
return new ModelAndView("test");
}
}
Which is nice. Except that, as far as I can see, I cannot map a URL for each method in that class as I would do with this kind of synchronous request:
#Controller
#RequestMapping("/test")
public class ControllerTest {
#RequestMapping(value = "/test.htm", method = RequestMethod.GET)
public void showSearchView(Model model) {...}
...
}
Can I do something that simple for AJAX requests?
Not sure where you found that first example on SpringSource! That is the old-bad(tm) way of doing it. I'm pretty sure AbstractController is even marked deprecated in Spring 3.
The second way works fine for mapping AJAX requests. If you really want to parse it all yourself, HttpServletRequest and HttpServletResponse are legal parameters for that handler method. However, Spring will happily do it for you: http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
(If you're stuck on an older version of Spring there are also 3rd party libraries for adding JSON mapping to handlers.)
This is the answer I found. I modified the method shown in my post and added a HttpServletRequest to the method arguments.
public void showSearchView(Model model, HttpServletRequest req, HttpServletRequest resp) {
if(req==null||resp==null)throw new RuntimeException("OLOLOLOLOL xD");
}
That's it. If anyone have a better answer or comments, I'd be glad to hear.