I have a spring boot config file similar to following,
server:
port: 8002
servlet:
context-path: /api/
...
spring:
mvc:
static-path-pattern: "/resources/**"
...
I have copy pasted the build files from my angular project inside resources/resources/static.
Don't know if this is relevant.
Here goes my JwtFilter.java,
#Component
public class JwtFilter extends OncePerRequestFilter {
final private JwtUtil jwtUtil;
#Autowired
JwtFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
#Override
protected void doFilterInternal(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain
) throws ServletException, IOException {
Cookie[] cookies = httpServletRequest.getCookies();
if(cookies == null) {
cookies = new Cookie[]{};
}
Cookie jwtCookie = Arrays.stream(cookies)
.filter(cookie -> cookie.getName().equals(StringConstants.JWT_AT_COOKIE_NAME))
.findFirst()
.orElse(null);
Cookie rtCookie = Arrays.stream(cookies)
.filter(cookie -> cookie.getName().equals(StringConstants.RT_COOKIE_NAME))
.findFirst()
.orElse(null);
if (rtCookie == null) {
httpServletResponse.sendError(401, "REFRESH_TOKEN_NOT_FOUND");
return;
}
String jwt = null;
String uid = null;
try {
if (jwtCookie != null) {
jwt = jwtCookie.getValue();
uid = jwtUtil.extractSubject(jwt);
} else {
httpServletResponse.sendError(401, "User not authenticated!");
}
if (uid != null) {
if (!jwtUtil.validateToken(jwt)) {
httpServletResponse.sendError(401, "EXPIRED_JWT_TOKEN_EXCEPTION");
return;
}
}
} catch (SignatureException exception) {
httpServletResponse.sendError(403, exception.getMessage());
return;
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return path.equals("/api/auth") || path.equals("/api/auth/");
}
}
I have tried adding those js build files to META-INF/resources, public & resources.
No go there as well.
Dir structure is:
java/
...
resources/
resources/
META-INF/
resources/ [contains angular build files]
public/ [contains angular build files]
resources/ [contains angular build files]
static/ [contains angular build files]
Now if I go to http://localhost:8002/ then it just says
HTTP Status 404 – Not Found.
The project structure needs a bit more work on it.
Check out the question here.
Build angular 11 with Spring boot 2.x in one single jar
Delete this.
spring:
mvc:
static-path-pattern: "/resources/**"
You want to put npm build artifacts inside src/main/resources/static.
Firstly, npm run build and you will find the artifacts inside dist/.
Copy whatever from dist/ into src/main/resources/static/. For example, the dist/index.html will be copied and pasted into src/main/resources/static/index.html.
Force Springboot to Serve index.html
However, there is a catch here. If you do mind a hash fragment #/foo/bar in the url, you need to tell Springboot to serve index.html on 404.
#SpringBootApplication
public class BlogApiApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(BlogApiApplication.class);
app.run(args);
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
container.addErrorPages(error404Page);
}
};
}
}
In this way, you do not need hash mode. The same thing goes with React, Vue.
Reference
servlet not doing redirect, used Spring Boot 2.4.1. Code and comments below.
Class servlet
code
#WebServlet(name = "estore",
urlPatterns = {"/"},
loadOnStartup = 1)
public class EstoreServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
IOException{
// Here logic and after need redirect
response.sendRedirect("/index.html");
}
}
Information from request
code
Session - org.apache.catalina.session.StandardSessionFacade#2e555cdd
Servlet path - /
Server name - localhost
Local name - 0:0:0:0:0:0:0:1
Local addr - 0:0:0:0:0:0:0:1
Get Remote user - null
Get method - GET
Get protocol - HTTP/1.1
Get content type - null
Get server port - 8080
Get sheme - http
Get Request URI - /
Get Context Path -
Spring MVC configuration file
code
#Configuration
#ServletComponentScan(basePackages = "internet_store.web_ui.servlet")
#EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resourceViewResolver = new SpringResourceTemplateResolver();
resourceViewResolver.setPrefix("classpath:/templates/internet_store/");
resourceViewResolver.setSuffix(".html");
resourceViewResolver.setTemplateMode(TemplateMode.HTML);
resourceViewResolver.setCharacterEncoding("UTF-8");
resourceViewResolver.setCheckExistence(false);
return resourceViewResolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/", "classpath:/templates/internet_store/")
.setCachePeriod(320000)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable("estore");
}
}
Project structure
Resources
This code return error in browser ERR_TOO_MANY_REDIRECTS. If in controller set return "redirect:/foo" then redirect not doing too. Example below.
code
#GetMapping(value = "/back_client")
public String backButtonClientFormPressed() {
return "service/service";
}
index.html have controller with method
code
#GetMapping(value = "/index")
public String index(ModelMap modelMap) {
updatePage();
Path resourceDirectory = Paths.get("resources");
modelMap.addAttribute("error", "");
refreshData(modelMap);
return "index";
}
Change service/service to redirect:/index going to error ERR_TOO_MANY_REDIRECTS. Maybe problems with resources path? Thank You for You attention.
[SOLVED]
Need delete response.sendRedirect("/index.html"); from servlet class and add
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
I'm trying to develop a spring MVC project which has a index.jsp file as it's view. I searched and found I should do this by the help of two below configuration classes:
public class MainInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public static HashMap<String, String> response_code = new HashMap<String, String>();
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { MainConfiguration.class,
WebSocketConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
Security.addProvider(new BouncyCastleProvider());
servletContext.addListener(new MainContextListener());
}
}
and this is MainConfiguration class:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "-----")
public class MainConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/Content/**")
.addResourceLocations("/Content/");
registry.addResourceHandler("/Scripts/**")
.addResourceLocations("/Scripts/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver
= new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
I've configured my project to run on tomcat web-server inside intellij. but when index.jsp is directly in path : webapp/index.jsp it will be opened in browser after running the program but when I move it to a subfolder it doesn't.
Shouldn't this part of code give the address to spring so it find the url and don't give a 404 error?
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
You need to annotate your MainConfiguration.java with #Configuration. Also, you require to register an instance of the DispatcherServlet in the servlet context. Actually, you can refer - spring mvc with jsp example for detailed configurations.
As you know, in XML, the way to configure this is:
<error-page>
<error-code>404</error-code>
<location>/my-custom-page-not-found.html</location>
</error-page>
But I haven't found a way to do it in Java config. The first way I tried was:
#RequestMapping(value = "/**")
public String Error(){
return "error";
}
And it appeared to work, but it has conflicts retrieving the resources.
Is there a way to do it?
In Spring Framework, there are number of ways of handing exceptions (and particularly 404 error). Here is a documentation link.
First, you can still use error-page tag in web.xml, and customize error page. Here is an example.
Second, you can use one #ExceptionHandler for all controllers, like this:
#ControllerAdvice
public class ControllerAdvisor {
#ExceptionHandler(NoHandlerFoundException.class)
public String handle(Exception ex) {
return "404";//this is view name
}
}
For this to work, set throwExceptionIfNoHandlerFound property to true for DispatcherServlet in web.xml:
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
You can also pass some objects to error view, see javadoc for this.
The most clean solution since spring 4.2 RC3 is using the new createDispatcherServlet hook within the class extending AbstractDispatcherServletInitializer (or indirectly through extending AbstractAnnotationConfigDispatcherServletInitializer) like this:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
/* ... */
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
final DispatcherServlet dispatcherServlet = super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
}
Then you can use a global #ControllerAdvice (a class that is annotated with #ControllerAdvice) as described in the reference docs. Within the advice you can handle the NoHandlerFoundException with an #ExceptionHandler as described here.
This could look something like this:
#ControllerAdvice
public class NoHandlerFoundControllerAdvice {
#ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<String> handleNoHandlerFoundException(NoHandlerFoundException ex) {
// prepare responseEntity
return responseEntity;
}
}
Simple answer for 100% free xml:
Set properties for DispatcherServlet
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {AppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
boolean done = registration.setInitParameter("throwExceptionIfNoHandlerFound", "true"); // -> true
if(!done) throw new RuntimeException();
}
}
Create #ControllerAdvice:
#ControllerAdvice
public class AdviceController {
#ExceptionHandler(NoHandlerFoundException.class)
public String handle(Exception ex) {
return "redirect:/404";
}
#RequestMapping(value = {"/404"}, method = RequestMethod.GET)
public String NotFoudPage() {
return "404";
}
}
Use code-based Servlet container initialization as described in the doc and override registerDispatcherServlet method to set throwExceptionIfNoHandlerFound property to true:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() may not return empty or null");
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext);
// throw NoHandlerFoundException to Controller
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
}
Then create an exception handler:
#ControllerAdvice
public class ExceptionHandlerController {
#ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "404";// view name for 404 error
}
}
Don't forget about using #EnableWebMvc annotation on your Spring configuration file:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages= {"org.project.etc"})
public class WebConfig extends WebMvcConfigurerAdapter {
...
}
In your web configuration class,
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter
Declare a bean as follows,
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container)
{
ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/401.html");
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");
container.addErrorPages(error401Page,error404Page,error500Page);
}
};
}
Add the mentioned html files(401.html .etc) to /src/main/resources/static/ folder.
Hope this helps
For Java config there is a method setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) in DispatcherServlet. By settting it to true I guess you are doing same thing
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
then you can can this NoHandlerFoundException.class in controller advice as stated in above answer
it will be like something
public class WebXml implements WebApplicationInitializer{
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
DispatcherServlet dp = new DispatcherServlet(context);
dp.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", dp);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(MAPPING_URL);
}
}
The solution proposed in comments above really works:
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration)
{
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
A solution for Spring 5 and Thymeleaf 3.
In MyWebInitializer, enable exception throwing with setThrowExceptionIfNoHandlerFound(). We need to do casting to DispatcherServlet.
#Configuration
public class MyWebInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
...
#Override
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
var dispatcher = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcher.setThrowExceptionIfNoHandlerFound(true);
return dispatcher;
}
}
Create a controller advice with #ControllerAdvice and add error message to the ModealAndView.
#ControllerAdvice
public class ControllerAdvisor {
#ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handle(Exception ex) {
var mv = new ModelAndView();
mv.addObject("message", ex.getMessage());
mv.setViewName("error/404");
return mv;
}
}
Create 404 error template, which displays the error message. Based on my configuration, the file is src/main/resources/templates/error/404.html.
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Resource not found</title>
</head>
<body>
<h2>404 - resource not found</h2>
<p>
<span th:text="${message}" th:remove="tag"></span>
</p>
</body>
</html>
For completeness, I add the Thymeleaf resolver configuration. We configure the Thymeleaf templates to be in templates directory on the classpath.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.zetcode"})
public class WebConfig implements WebMvcConfigurer {
#Autowired
private ApplicationContext applicationContext;
...
#Bean
public SpringResourceTemplateResolver templateResolver() {
var templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
return templateResolver;
}
...
}
In springboot it is even simplier. Because of Spring autoconfiguration stuff, spring creates a bean org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties. This class is annotated with #ConfigurationProperties(prefix = "spring.mvc") and therefore it will seek for properties with spring.mvc prefix.
Part from javadoc:
Annotation for externalized configuration. Add this to a class definition or a
* #Bean method in a #Configuration class if you want to bind and validate
* some external Properties (e.g. from a .properties file).
You just have to add to your i.e. application.properties file following properties:
spring.mvc.throwExceptionIfNoHandlerFound=true
spring.resources.add-mappings=false //this is for spring so it won't return default handler for resources that not exist
and add exception resolver as follows:
#ControllerAdvice
public class ExceptionResponseStatusHandler {
#ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handle404() {
var out = new ModelAndView();
out.setViewName("404");//you must have view named i.e. 404.html
return out;
}
}
I am trying to build a single page HTML/Angular app backed by Spring MVC 4 without .jsp files.
When the user arrives to the root URL (http://myapp.com/ for example), I want to send the single page HTML document (index.html).
However, I am new to Spring MVC's Java config and cannot work out the proper combination of ServletMapping, ViewResolver, and ResourceHandler.
I know that there may be some additional configuration for the "default" or "index" page, but I may be mistaken.
So how do I make a Java configured Spring MVC 4 application send an html file?
File structure
/src
'-/main
|-/web-inf
'-/webapp
|-index.html (want to send this)
'-/app
|-/partials
'-app.js
WebAppConfig.java
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/").addResourceLocations("/index.html");
}
IndexController.java
#RequestMapping("/")
public void index() {
System.out.println("pls"); // executes
// Have tried returning "index" and "index.html"
}
WebInitializer.java
#Override
protected String[] getServletMappings() {
// have tried with "/" as well
return new String[] { "/*" };
}
I think you should add this:
<mvc:view-controller path="/" view-name="index"/>
in your dispathcer-servlet file.
Here are a few working solutions either way - in case anyone needs one for their specific use case.
If you DO NOT want to invoke a controller method and just serve the file.
WebInitializer.java:
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
Web.xml
<web-app>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
WebConfig.java
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("index.html").addResourceLocations("/index.html ");
}
If you DO want to invoke a controller method on landing.
No welcome file list is required in the web.xml
WebInitializer.java:
#Override
protected String[] getServletMappings() {
return new String[] { "/*" };
}
WebConfig.java
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("index.html").addResourceLocations("/index.html ");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setSuffix(".html");
return viewResolver;
}
Controller.java
#RequestMapping("/")
public String index() {
return "index";
}
Hope that helps somebody.