I have issues configuring Spring boot 1.2.0.M1 to serve static content.
As soon as I add a #RestController component in my application, the static content is not displayed and I get the whitelabel error page instead. My resources are in the src/main/resources/static folder.
I followed the instructions at http://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot and managed to have them displayed by setting the ResourceHandlerRegistry priority to -1. But i guess it is not the standard and right way to do it. It seems that the handler of the REST resources takes priority over the resource handler registry. Is there a possibility to configure the handler for rest resource to be used for a sub-context only like /api ?
Update:
I have put the DispatcherServlet in debug and I have better understanding on why this is happening but still not sure what would be the best way to deal with it.
I have noticed that the following handlers are registered in the Dispatch servlet in the respective order by default :
SimpleUrlHandlerMapping -> favico
RequestMappingHandlerMapping -> annotated #RestController methods registered
SimpleUrlHanderMapping -> / (home page handling ?)
BeanNameUrlHandlerMapping
SimpleUrlHandlerMapping -> handles resources configured in the resources registry
WebMVCConfigurationSupport handler
When I perform a POST on the REST resource 2. handles it.
When I perform a GET on a static resource 2. throws a HttpRequestMethodNotSupportedException because it cannot find the resource (in its handleNoMatch method).
If i change the priorities using ResourceHandlerRegistry#setOrder(Ordered.HIGHEST_PRECEDENCE); 5. is placed before 2. but in this case i cannot perform a POST /user/api intended for my REST resource, it is not matched as a resource (i have configured a /** pattern for the resource handler).
If i compare to what node.js/express does for instance is that you configure routes for your controllers and if none matches the request, the request is handled by the handler for static resources or templates.
Do you know how if it is possible through annotation to have the 2 (i.e RequestMappingHandlerMapping ) not throw an exception but just pass the request to the next handler in the chain in case of no-match ?
I'd like avoiding having a specific context (/static for static resources).
Update 2:
Actually it was just a misconfiguration of my annotated REST controller #RestController
I configured the path in the value attribute of the annotation that is not meant for that but to store the controller's name
I forgot to add the #RequestMapping therefore the handler RequestMappingHandlerMapping was enabled for any url path and the get request did not match any annotated methods, therefore it returned an error.
Related
I'm hitting an API locally through a Postman.
Which one is executable in localhost - DispatcherServlet class or OncePerRequestFilter class ?
What is the use of DispatcherServlet in Spring MVC ?
The DispatcherServlet is a central servlet that helps in dispatching the request to the controllers. It is integrated with Spring IoC (Inversion of Control) Container (which is responsible for managing the life-cycle of the bean). This also provides various functionality for the development of the web applications. It acts as Front Controller for the web application.
Screenshot of what happens behind scenes in Spring MVC(From Spring Docs) :
What is the use of OncePerRequestFilter ?
It is a class that guarantees the execution of a method based on the request to be allowed, on any servlet container.
Q : Which one is executable in localhost - DispatcherServlet class or OncePerRequestFilter class ?
First the request will be dispatched by the DispatcherServlet. Then, filter will be called because filter acts as wall in between DispatcherServlet and Controllers. If the request meets criteria of the filter, then the request is allowed to hit the particular API. Otherwise, that request is filtered out.
According to the documentation-
org.springframework.web.filter.OncePerRequestFilter "guarantees to be just executed once per request". Under what circumstances a Filter may possibly be executed more than once per request?
And it will be executed for the localhost also. It must be executed whenever any request is generated.
How can I log all requests in Spring MVC, even the resource not founds ones?
I guess interceptors can not log resource not found requests. What do you suggest?
Have you tried using implementation of javax.servlet.Filter?
Filter in contrary to Spring's interceptor is part of a Java's standard and is executed by the servlet container for each incoming HTTP request..
Spring MVC use exactly one servlet and that is DispatcherServlet which, as the name suggest, disptach received request to correct controller which process the request futher.
Spring even provide few concrete implementations of Filter that can log incoming request for you such as: CommonsRequestLoggingFilter or ServletContextRequestLoggingFilter.
You can choose one of the above implementations or implement Filter interface by yourself - whatever you decide, by using Filter you should be able to log every single request received by your servlet container.
Is there a way to tell if Spring has loaded my #Controller?
I'm requesting a URL but I'm not hitting my controller and I can't figure out why
I'm loading controllers by doing a component scan
<context:component-scan base-package="com.example.app.web"/>
Other controllers in the same package as my failing controller are working fine.
My controller code is:
#Controller
#RequestMapping(value = "/app/administration/ecosystem")
public class AppEcosystemController {
#Autowired
EcosystemManagerService ecosystemManagerService;
#RequestMapping(value = "/Foo", method = RequestMethod.GET)
public String getEcosystem() {
/* Implementation */
}
The first thing I'd like to do is to be sure that this controller is getting picked up by the component scan.
Any suggestions?
Just enable logging for your application, you can find this information at INFO level
For example in my application I have a controller named UserController.
The following log4j.properties does the trick.
log4j.rootLogger=INFO, FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=../logs/rest-json.log
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
I can see in the log that RequestMappingHandlerMapping mapped my controller (scroll all the way to the right).
07:28:36,255 INFO RequestMappingHandlerMapping:182 - Mapped "{[/rest/**/users/{id}],methods=[GET],params=[],headers=[],consumes=[],produces=[text/xml || application/json],custom=[]}" onto public org..domain.User org.ramanh.controller.UserController.getUser(java.lang.String)
07:28:36,255 INFO RequestMappingHandlerMapping:182 - Mapped "{[/rest/**/users],methods=[POST],params=[],headers=[],consumes=[],produces=[text/xml || application/json],custom=[]}" onto public void org..controller.UserController.addUser(org...domain.User)
If you are still unsure I would suggest adding a method annotated with #PostConstruct.
You could easily look up the message in the log or place a break point in this method.
#PostConstruct
protected void iamAlive(){
log.info(“Hello AppEcosystemController”)
}
If you find that your controller is initialized correctly but still the url is not accessible.I would test the following
You are getting 404 error - maybe you are not pointing to the correct
url (do not forget to add the application as prefix to the url)
You are getting 404 error - Dispatcher servlet mapping in web.xml doesn't meet
the url above
You are getting 403/401 – maybe you are using
spring security and it’s blocking the url
You are getting 406 – your
content type definition is conflicting with your request
You are getting 50x – something is buggy in your code
I made an ApplicationContextDumper. Add it into application context, it will dump all beans and their dependencies in the current context and parent contexts (if any) into log file when the application context initialization finishes. It also lists the beans which aren’t referenced.
It was inspired by this answer.
You could start out with enabling debug logging for Spring as outlined here.
I'd also recommend leveraging the MVC testing support, which you'll find in the spring-test jar. Details on how to use it can be found here and here.
There is a web application that I am working on currently and it has to be extended to expose web services. In the current project - when the application context is loaded at startup - database queries are made and static data like role names is set as variables at the session level.
Like this:
private void loadRoles(ServletContext acontext) {
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(acontext);
IMyDataService myDataService = (IMyDataService ) appContext.getBean("myDataService");
List<Roles> rolesList = myDataService.listRoles();
acontext.setAttribute(MyAppConstants.ROLES, rolesList);
}
This value stored in the session attribute is used as follows in other places of the application:
public boolean checkAccess(HttpServletRequest arequest) {
HttpSession session = arequest.getSession();
List<Role> roles = (List<Roles>)session.getServletContext().getAttribute(MyAppConstants.ROLES);
.....
}
If I want to enhance the application to expose web services - my understanding is that I will no longer be having a ServletSession or HttpServletRequest available with me. So I want to move this static data from session variables to another place so that they are available in the context.
Is there a way in which I can achieve this?
I tried getting rid of storing data in session variables all together, but could not do that because there are just too many references.
Is there a better approach?
There is a difference between the session and the servlet context. I think that you are confuse because you are using the session object to get the servlet context. Your data is clearly set in the servlet context in this example. Even if you are using web services, you will have a servlet context provided by the servlet container.
Now I don't know which technologies you are using, but there's many other ways to make static data available to all your web services. For example, using a cache mechanism might be a better solution for data that is stored in the database...
More info :
A Servlet Session is a very different thing from a Servlet Context.
An HttpSession is a mechanism used to simulate and maintain a
continuous user Session between a Web Browser and Web Application,
largely managed by the Servlet Container. The HTTP protocol is
stateless, it is essentially a request-response scheme, so Servlet
Sessions are maintained by passing a unique HTTP cookie value for each
request, or by dynamically including an identifier in Servlet URLs,
known as URL-rewriting.
A ServletContext object represents the overall configuration of the
Servlet Container and has several methods to get configuration
parameters, exchange data amongst Servlets, forward requests and load
resources. The Servlet Context is usually obtained indirectly through
the ServletConfig object, passed to a Servlet's init(ServletConfig)
method, or from the Servlet getServletConfig() method.
Javadoc definition for ServletContext :
Defines a set of methods that a servlet uses to communicate with its
servlet container, for example, to get the MIME type of a file,
dispatch requests, or write to a log file.
There is one context per "web application" per Java Virtual Machine.
(A "web application" is a collection of servlets and content installed
under a specific subset of the server's URL namespace such as /catalog
and possibly installed via a .war file.)
EDIT
To get the ServletContext object in a Spring application, use the #Autowired annotation. Note that the object should be managed by the Spring container, it will be the case if you are using controllers for you REST services.
#Autowired
ServletContext servletContext;
Here an example :
#Controller
#RequestMapping("/foo")
public class RESTController {
#Autowired
ServletContext servletContext;
#ResponseBody
#RequestMapping(value="/bar", method = RequestMethod.GET)
public List<Roles> getRoles() {
return servletContext.getAttribute(MyAppConstants.ROLES);
}
}
Refactor your code by removing MyAppConstants.ROLES and by relying on Spring DI. There are much better ways to store static data than putting them in HTTP session or servlet context... (ex. create a list bean, use cache abstraction, use FactoryBean etc.).
I want to use Spring MVC 3.0 to build interfaces for AJAX transactions. I want the results to be returned as JSON, but I don't necessarily want the web pages to be built with JSP. I only want requests to the controllers to be intercepted/routed through the DispatcherServlet and the rest of the project to continue to function like a regular Java webapp without Spring integration.
My thought was to define the servlet-mapping url pattern in web.xml as being something like "/controller/*", then have the class level #RequestMapping in my controller to be something like #RequestMapping("/controller/colors"), and finally at the method level, have #RequestMapping(value = "/controller/colors/{name}", method = RequestMethod.GET).
Only problem is, I'm not sure if I need to keep adding "/controller" in all of the RequestMappings and no matter what combo I try, I keep getting 404 requested resource not available errors.
The ultimate goal here is for me to be able to type in a web browser "http://localhost:8080/myproject/controller/colors/red" and get back the RGB value as a JSON string.
You are not correct about needing to add the entire path everywhere, the paths are cumulative-
If you have a servlet mapping of /controller/* for the Spring's DispatcherServlet, then any call to /controller/* will be handled now by the DispatcherServlet, you just have to take care of rest of the path info in your #RequestMapping, so your controller can be
#Controller
#RequestMapping("/colors")
public class MyController{
#RequestMapping("/{name}
public String myMappedMethod(#PathVariable("name") String name, ..){
}
}
So now, this method will be handled by the call to /controller/colors/blue etc.
I don't necessarily want the web pages to be built with JSP
Spring MVC offers many view template integration options, from passthrough to raw html to rich templating engines like Velocity and Freemarker. Perhaps one of those options will fit what you're looking for.