throwing NoHandlerFoundException and configuring custom 404 page in SpringBoot - spring

I have been learning Spring for just 7 months. While I used spring MVC only, i want to configure custom 404 page by throwing NoHandlerFoundException or enabling it in the dispatcher servlet. Now, i am learning spring boot, can anyone explain to me?

I had the same issue, got it resolved. Below given steps to solve the same.
Create a class GlobalExceptionHandler annotated with #ControllerAdvice
#ControllerAdvice
public class GlobalExceptionHandler
{
#ExceptionHandler(NoHandlerFoundException.class)
public String handleNotFoundError(Exception ex)
{
return "redirect:/yourCustom404page";
}
}
By default, when a page/resource does not exist the servlet container will render a default 404 page. If you want a custom 404 response then you need to tell DispatcherServlet to throw the exception if no handler is found. We can do this by setting the throwExceptionIfNoHandlerFound servlet initialization parameter to true
to achieve this
a. In spring-boot
spring.resources.add-mappings=false in your application.properties or yaml file.
b. If spring-mvc java based configuration is
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
...
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext)
{
final DispatcherServlet servlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
servlet.setThrowExceptionIfNoHandlerFound(true);
return servlet;
}
}
c. if spring-mvc xml based configuration, initialize your dispatcher servlet like this
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

Related

Spring unit test 404\unknown url in mock mvc

I would like to test that when an unknown url is requested and a 404 error is generated that my web app actually redirects to the right place.
I havent been able to get this working, I think because tomcat is handling the 404 errors so the forwardedUrl is always null for the tests. I know this works in reality because if I enter some rubbish into the url my app does redirect to my custom page.
My unit test looks like:
#Test
public void testUnknownUrl() throws Exception {
mockMvc.perform(get("/url_doesnt_exist"))
.andExpect(status().isNotFound())
.andExpect(forwardedUrl("/static/error/Sorry.html"));
}
My web.xml configuration is:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<error-page>
<error-code>404</error-code>
<location>/static/error/Sorry.html</location>
</error-page>
The mapping for /static is defined in my spring config like:
<resources mapping="/static/**" location="/resources/" />
Ultimately I would like to mock a request to an unknown url and then check that the page being returned is /static/error/Sorry.html.
Am I doing something wrong or is this not the way to handle 404 etc in spring? The check of the forwarded url in the unit test is always null.
A slightly different question but related all the same is, at what point does the tomcat error handling get invoked over and above the spring controller advice handling?
I'm not sure about the configuration with the /static path in your web.xml, it shouldn't be like that depending on your dispatcher-servlet (default name) configuration; but as far as I can tell you are in the right path.
This is what I have for mine:
#WebAppConfiguration
#ActiveProfiles("development")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
public class ErrorControllerTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)/*.alwaysExpect(status().isOk())*/.build();
}
#Test
public void testError() throws Exception {
mockMvc.perform(get("/error").contentType(MediaType.TEXT_HTML))
.andExpect(status().isOk()) // See "alwaysExpect" above
.andExpect(view().name("error"))
.andExpect(forwardedUrl("/WEB-INF/views/error.jsp"));
}
#Test
public void testResourceNotFound() throws Exception {
mockMvc.perform(get("/resource-not-found").contentType(MediaType.TEXT_HTML))
.andExpect(status().isNotFound())
.andExpect(view().name("resource-not-found"))
.andExpect(forwardedUrl("/WEB-INF/views/resource-not-found.jsp"));
}
}
Sample project is here. I'm using JSPs but you can switch to .html just by changing the InternalResourceViewResolver configuration.
I am using spring 3 and after some reading around I have found out the dispatcher servlet just returns a response code without throwing an exception which I guess is why tomcat always handles this.
Removing the error page tags from web.xml results in a tomcat generic 404 page, so I think the answer to this is to upgrade to spring 4 where I can then pass an init param to the dispatcher servlet requesting it throw an error for a page not found.
I am happy to be corrected on this though as it may help my understanding.

Spring+Jersey transactional annotation

Created boilerplate project to expose RESTful API to JPA enabled database. It's using the following versions:
- Spring 3.2.6
- Hibernate 4.3.0
- Jersey 2.5.1
I finally was able to get them playing together, but still some question remains. Here's one of the most puzzling things (see excerpt from REST service class)
#Service
#Path("resources")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
public class ResourceServices extends AbstractServices<Resource> {
...
}
if class is annotated with #Service, #Transactional annotation is ignored and transaction for the methods is not started. However, when changed to #Component, everything works fine. Couldn't figure out, why.
The entire project can be seen here
I got puzzled by this as well, but finally figured this out.
The jersey-spring module will only import #Component beans from your context. There literally is a beanClass.isAnnotationPresent(Component.class) check in SpringComponentProvider.
Otherwise it appears to only create half-baked request-scoped instances of the bean (I traced this with Thread.dumpStack in service constructor). They seem to have dependency injection, but not AOP.
There's a number of JIRA items already in Jersey's issue tracker: JERSEY-2495, JERSEY-2059, JERSEY-2301
UPDATE: My pull request for these has been merged, this should be fixed in Jersey 2.11.
As mentioned in another answer SpringComponentProvider gets a bean created by Jersey and registers it in the Spring context, but in this case you don't get Spring AOP.
I managed to get it working with AOP the other way around: the bean is created by Spring (so in fact it is a proxy because of AOP) and then is registered in Jersey.
But I had to fix a bug in Jersey's ModelHelper class: https://github.com/jersey/jersey/pull/90
Without this fix Jersey was not able to find the #Path annotation in the Spring proxy.
This is the basic structure:
public class MyApplication extends ResourceConfig {
#Inject
public MyApplication(ServletContext servletContext) {
super(JSONController.class, XSSSecurityFilter.class, JacksonFeature.class);
WebApplicationContext springFactory = WebApplicationContextUtils.getWebApplicationContext(servletContext);
// TODO: scan entire Spring factory for beans annotated with #Path and register them, so we don't need to do this manually.
// Letting Jersey register the beans does not work because in this case Spring will not intercept the calls.
register(springFactory.getBean(UserServiceFacade.class));
}
}
The reason is your Spring has a different container for its annotations and jersey has a different container for its annotations , in order to access your beans in spring container you can refer to the below code ;
I beg to differ from the version your using, I haven't tried with the latest version of jersey:
load spring through web.xml like shown below as normal spring confifuration:
<servlet>
<servlet-name>project-spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:project-spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>project-spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Now load your jersey Resources through Application as shown below:
#ApplicationPath("/rest")
public class ResourceLoader extends Application
{
/* (non-Javadoc)
* #see javax.ws.rs.core.Application#getClasses()
*/
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
loadResourceClasses(classes);
return classes;
}
private void loadResourceClasses(Set<Class<?>> classes)
{
classes.add(StudentResource.class);
}
}
Then in your resource:
#Path("student")
class StudentResource
{
private StudentService studentService;
StudentResource(#Context ServletContext servletContext)
{
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.studentService= applicationContext.getBean(StudentService .class);
}
}
You can get your ApplicationContext where all the beans have been initailized using WebApplicationContextUtils of spring pass the servlet context, and get your bean

Jersey-Spring #Autowired not working

I'm using Jersey-Spring integration to expose business layer services.
In my web.xml I'm using the SpringServlet:
com.sun.jersey.spi.spring.container.servlet.SpringServlet
My business layer is #Component annotated, so I have #Service's using #Repository's provided via Spring's annotation config.
Repository's are provided to service's via #Autowired annotation.
If I use a repository through a service using my front end MVC classes everithig goes well, but if I use it through Jersey I get a NullPointerException on the repository object.
The version I'm using (through Maven) are:
Spring (and extensions): 3.1.3.RELEASE
Jersey (and extensions): 1.17
There is way to solve this problem using the same version mentioned in your question,
If needed ill mention the second way , the first way is to load sring through web.xml
like shown below as normal spring confifuration:
<servlet>
<servlet-name>project-spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:project-spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>project-spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Now load your jersey Resources through Application as shown below:
#ApplicationPath("/rest")
public class ResourceLoader extends Application
{
/* (non-Javadoc)
* #see javax.ws.rs.core.Application#getClasses()
*/
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
loadResourceClasses(classes);
return classes;
}
private void loadResourceClasses(Set<Class<?>> classes)
{
classes.add(StudentResource.class);
}
}
Then in your resource:
#Path("student")
class StudentResource
{
private StudentService studentService;
StudentResource(#Context ServletContext servletContext)
{
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.transactionService = applicationContext.getBean(StudentService .class);
}
}
There'r you go Spring has been configured with all dependency injections with Jersey!!!
You should try using #InjectParam

Spring Extension REST Resource #RequestParam annotation not detected

trying to use the spring extension of Restlet ,
have configured as per the example http://wiki.restlet.org/docs_2.1/13-restlet/28-restlet/70-restlet/196-restlet.html
In addition to that trying to capture the request parameters using the #RequestParam annotation but end up getting the parameter value as null.
Resource looks like,
class MyResource extends ServerResource implements IResource {
#Get
#RequestMapping(value="/id")
public void get(#RequestParam(value="name") String name) {
...
}
}
HTTP Request http://localhost:8080/messages/id?name=XXX
Web.xml looks like
<servlet>
<servlet-name>test-servlet</servlet-name>
<servlet-class>org.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
</servlet>
<!-- Catch all requests -->
<servlet-mapping>
<servlet-name>test-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
In the end the name value is 'null'. I think the spring based annotations are not detected. I have no clue why this is happening?

Servlet Mapping Help - Possible to Avoid Referencing Context Name?

I am working on a Spring application using Tomcat 6 and Spring 2.5. I'm trying to get my URL mapping correct. What I would like to have work is the following:
http://localhost:8080/idptest -> doesn't work
But instead, I have to reference the context name in my URL in order to resolve the mapping:
http://localhost:8080/<context_name>/idptest -> works
How can I avoid the requirement of referencing the context name in my URL without using a rewrite/proxy engine e.g. Apache?
Here is the servlet definition and mapping from my web.xml:
<servlet>
<servlet-name>idptest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/conf/idptest.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>idptest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Here's the outline of my controller (showing annotations for request mappings):
#Controller
#RequestMapping("/idptest")
public class MyController {
#RequestMapping(method=RequestMethod.GET)
public String setupForm(Model model){
MyObject someObject = new MyObject();
model.addAttribute("someObject", someObject);
return "myform";
}
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("someObject") MyObject someObject) throws Exception {
// POST logic...
}
}
Thanks!
That's going to depend on your servlet container, for Tomcat - you pretty much have to deploy your webapp as the ROOT webapp, that is, under $CATALINA_HOME/webapps/ROOT/
More info here
Just rename your war file to ROOT.war, then the application runs in root context (i.e. with empty context name)

Resources