Вопрос

I'm worked with Spring in the past on a big project, but I've never started a Spring MVC web app from scratch.

Well that's what I'm currently doing now for practice as I have a project coming up that will require it.

I was successfully able to make a simple Spring MVC Web App that used .JSP pages (using annotations, no XML). I wanted to use Thymeleaf though and started my conversion process to that.

Well now I'm getting a 404 Error and my HomeController class isn't even being hit it seems like.

I get no errors in the Console output.

I've Google search, read through tutorials, and code samples. Second pair of eyes would be nice. Thanks! :)

Note: going from .JSP to Thymeleaf the only changes made was the addition of the ThymeleafConfig class. I don't see how it went from working to not working.

Here's my code:

WebInit.java

public class WebInit implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // Creates the root application context
        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(ServletConfig.class);
        appContext.setDisplayName("REPLACE ME");
        appContext.setConfigLocation("com.demo.config");

        // Creates the Spring Container shared by all Servlets and Filters
        servletContext.addListener(new ContextLoaderListener(appContext));

        // Further configures the servlet context
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
                "dispatcher", new DispatcherServlet(appContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.setAsyncSupported(true);
        dispatcher.addMapping("/");
    }
}

ServetConfig.java

@Configuration
@Import(WebConfig.class)
@ImportResource({/*"classpath:META-INF/spring/persistence-context.xml"*/})
public class ServletConfig {

}

WebConfig.java

@Configuration
@ComponentScan("com.illinois.dnr")
@EnableAspectJAutoProxy
@EnableWebMvc
@Import({ThymeleafConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter {
    // Maps resources path to webapp/resources
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations(
                "/resources/");
    }

    // Provides internationalization of messages
    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasename("messages");
        return source;
    }
}

Thymeleaf.java

@Configuration
public class ThymeleafConfig {
    @Bean 
    public ServletContextTemplateResolver templateResolver() {
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setOrder(1);
        return resolver;
    }

    @Bean 
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        return engine;
    }

    @Bean 
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        return resolver;
    }
}

HomeController.java

@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    /**
     * Simply selects the home view to render by returning its name.
     */
    @RequestMapping(value = "/dnr", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);
        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
                DateFormat.LONG, locale);
        String formattedDate = dateFormat.format(date);
        model.addAttribute("serverTime", formattedDate);
        return "home";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String loginPage(Locale locale, Model model) {
        logger.info("Login");
        return "login";
    }

    @RequestMapping(value = "/home", method = RequestMethod.POST)
    public String login(@Validated User user, Model model) {
        model.addAttribute("userName", user.getUserName());
        logger.info("User");
        return "user";
    }

    @RequestMapping(value = { "/", "/welcome**" }, method = RequestMethod.GET)
    public ModelAndView welcomePage() {

        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Hello World");
        model.addObject("message", "This is welcome page!");
        model.setViewName("hello");
        logger.info("Hello");
        return model;
    }

    @RequestMapping(value = "/admin**", method = RequestMethod.GET)
    public ModelAndView adminPage() {

        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Hello World");
        model.addObject("message", "This is protected page - Admin Page!");
        model.setViewName("admin");
        logger.info("admin");
        return model;
    }

    @RequestMapping(value = "/dba**", method = RequestMethod.GET)
    public ModelAndView dbaPage() {

        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Hello World");
        model.addObject("message", "This is protected page - Database Page!");
        model.setViewName("admin");
        logger.info("dba");
        return model;
    }
}
Это было полезно?

Решение

I'm not sure if it helps but you can simplified WebInit by extendsAbstractAnnotationConfigDispatcherServletInitializer instead of implements WebApplicationInitializer. Otherwise Thymeleaf configuration seems ok to me (suppose it's called). I'm giving here working skeleton of config so you can experiment with it.

WebApplicationInitializer

public class WebInitializer extends
    AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

WebConfig

@Configuration
@EnableWebMvc
@ComponentScan("com.kreuzman")
public class WebConfig {

    @Bean
    public ITemplateResolver templateResolver() {
        TemplateResolver resolver = new ServletContextTemplateResolver();
        resolver.setPrefix("/templates/");
    resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setCacheTTLMs(0l);

        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());

        return engine;
    }

    @Bean
    public ViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        viewResolver.setCharacterEncoding("UTF-8");

        return viewResolver;
    }

}

Другие советы

What page are you trying to hit? Does that html page exist in /WEB-INF/views/?

Is it a JSP page? you need to exclude all pages that are not going to be resolved by ThymeleafViewResolver. I also noticed you didn't set the ThymeleafViewResolver to first in the order.

Like so

@Bean 
public ThymeleafViewResolver thymeleafViewResolver() 
{
    String[] excludedViews = new String[]{
        "login", "logout"};

    ThymeleafViewResolver resolver = new ThymeleafViewResolver();
    resolver.setTemplateEngine(templateEngine());
    /*
     * This is how we get around Thymeleaf view resolvers throwing an error instead of returning
     * of null and allowing the next view resolver in the {@see
     * DispatcherServlet#resolveViewName(String, Map<String, Object>, Locale,
     * HttpServletRequest)} to resolve the view.
     */
    resolver.setExcludedViewNames(excludedViews);
    resolver.setOrder(1);
    return resolver;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top