AndreasGloeckner
AndreasGloeckner

Reputation: 302

cannot find html pages with spring boot

I am using spring boot and angular in front end. Sometimes I want to redirect directly from a service. We are using only .html, .css and script files (no .jsp).

This is return "static/pages/savepassword.html"; how I tried to redirect to savepassword

So 1st question how do I redirect properly in this case?

Next, Spring doesn't find my html files. I have this structure

enter image description here

I read that spring should find all static files in "static" automatically, but it doesn't. So I tried to configure my ViewControllerRegistry and RessourceControllerRegistry

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
//      registry.addResourceHandler("/stylesheets/**").addResourceLocations("/stylesheets/");
//      registry.addResourceHandler("/scripts/**").addResourceLocations("/scripts/");
//      registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        super.addViewControllers(registry);
        registry.addViewController("/savePassword").setViewName("static/pages/savePassword.html");
        registry.addViewController("/login").setViewName("static/pages/login.html");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }

When I call "static/pages/login.html" directly, it works. When i want to call "/login" it says 404. 2nd question: What do I need to configure and what not?

UPDATE-1: Spring Security

http
            .authorizeRequests().antMatchers("/login", "/logout", "/user/**").permitAll()
            .anyRequest().authenticated()
        .and()
            .exceptionHandling()
            .accessDeniedHandler(new CustomAccessDeniedHandler())
            .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
        .and()
            .requiresChannel()
            .anyRequest().requiresSecure()
        .and()
            .csrf()
            .disable()
            //.csrfTokenRepository(csrfTokenRepository())
            .addFilterBefore(new MDCFilter(), ExceptionTranslationFilter.class)
            // .addFilterBefore(new CorsFilter(),// ChannelProcessingFilter.class)
            //.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
            .formLogin()
            .successHandler(new CustomSuccessAuthenticationHandler())
            .failureHandler(new CustomFailureHandler())
            .loginPage("/login").permitAll();

UPDATE-2 I updated my structure, but still have problems with redirecting from /login to the custom login. enter image description here

UPDATE-3 I had a look at the 2nd comment by Bob Shields: https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot I did the following:

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        super.addViewControllers(registry);
        registry.addViewController("/savepassword").setViewName("savepassword.html");
        registry.addViewController("/login").setViewName("login.html");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }

I set .html because otherwise /login would cause recursive problems. With this config, I can enter /login and /templates/login.html will be called (same for savepassword.html).

Now, I would like to know how to include my css/js in html:

<link rel="stylesheet" type="text/css" href="..resources/stylesheets/bootstrap.min.css" />
    <script src="..resources/scripts/angular.min.js"></script>
    <script src="..resources/scripts/login.js"></script>

This doesn't work...but I cann call my scripts directly with /scripts/login.js.

Upvotes: 5

Views: 18598

Answers (2)

AndreasGloeckner
AndreasGloeckner

Reputation: 302

Ok, now I totally got it. I try to be specific as possible.

My final structure

structure

1. Static Ressources

Spring has some default configuration where you can put your static ressources without configure it. You need to create those folders in src/main/resources

  • /META-INF/resources/
  • resources (yes, an additional resources folder in src/main/resources
  • static
  • public

Have a look here in Spring Doku. For an example, check the 2nd comment by Bob Shields:

After creating a bunch of "findme.txt" files, each of which had different "source-relative" path string inside, I found that the following locations can be fetched with "http://host/findme.txt" are:

src/main/resources/public/findme.txt

src/main/resources/static/findme.txt

src/main/resources/resources/findme.txt - (yes, resources^2!)

src/main/resources/findme.txt - not found!!

I placed my folder "scripts" and "stylesheets" in the static folder. When I want to include files from these folders in html, the source looks like src="scripts/file.js"because the files and folders in staticare found directly rooted to host/scripts/file.js

No ResourceHandlerRegistry was necessary for me!

2. View Controller

I wanted to root my login.html to /login, savepassword.html to /savepassword and my index.html to /. Here is what I did:

registry.addViewController("/savepassword").setViewName("savepassword.html");
registry.addViewController("/login").setViewName("login.html");
registry.addViewController("/").setViewName("loggedin/index.html");

I set *.html because otherwise I had a problem that spring didn't know to differ between /login and the login(.html). As you can see I placed the index.html in a different folder called "loggedin". This folder is right in my public-folder (found by Spring, see 1.). Why do I do this? Have a look at 3.

3. Spring Security

.authorizeRequests().antMatchers("/stylesheets/**", "/scripts/**","/login", "/user/**").permitAll()
...omitted further configuration...
.loginPage("/login").permitAll();

First, I want that a user always has to be logged in before he can visit any other sites. So all ressources for loginpage must be available.

This piece of code makes every file in stylesheets and scripts accessible. Remember stylesheets and scripts-folder are placed in static. I had to set "/login" because of the custom loginpage. Honestly I thought that the .loginPage("/login").permitAll();would grant access, but it doesn't. Without it Spring tries to access the "/login", but can't cause of no rights and then automatically redirects to login => ERR_TOO_MANDY_REDIRECTS. "/user/**" is just for some of my services that every user can use.

As you can see, I don't give access to "loggedin"-folder, so that every visitor can enter files in "loggedin" only if he is loggedin ;)

4. Redirecting

I just marked my whole controller with @RequestController, which implements @ResponseBody. When I return a String like return "redirect:/savepassword";, Spring will set this as ResponseBody and it will be interpreted as String/Json(?).

I had to mark the controller as @Controller and set @ResponseBody to every service that must return json or sth. My service that shall redirect was only marked with @RequestMapping and method=RequestMethod.GET. Finally, I return a String like return "redirect:/savepassword"; This will make Spring redirect to /savepassword, which actually is placed in src/main/resources/public/savepassword.html.

Finally, it works and it was hard enough to find how spring serves the files. I hope it helps someoneelse :)

Upvotes: 3

Lazar Lazarov
Lazar Lazarov

Reputation: 2532

In my opinion it is better to put your *.HTML files in your resources folder with all the static CSS FONTS and so on like in the picture below. In webapp you can have a WEB-INF folder only.

I am using this kind of structure and I have no problems finding any classes css or htmls and I do not use extensions at the end in java ("static/pages/login.html" like yours). If I want to access html I go like "user/someHtml" if it is in folder user for ex.

I know this answer is not precisely the one you are looking for but it may help you in someway.

enter image description here

EDIT: Now for the :

   @Override
   public void addViewControllers(ViewControllerRegistry registry) {
       super.addViewControllers(registry);
       registry.addViewController("/savePassword").setViewName("static/pages/savePassword.html");
       registry.addViewController("/login").setViewName("static/pages/login.html");
       registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}

Lets say you have a Controller:

@Controller
@RequestMapping("/myController")
public class MyController{
...


@RequestMapping(value = "/login")
public String login(Model model) {
     // Your code ....

    return "login"; //<- this is your login.html if it is directly in resource/templates folder
}
    @RequestMapping(value = "/savePassword")
public String savePassword(Model model) {
     // Your code ....

    return "savePassword"; //<- this is your savePassword.html if it is directly in resource/templates folder
} 
.....
}

Now your addViewController show look like this :

   @Override
      public void addViewControllers(ViewControllerRegistry registry) {
          super.addViewControllers(registry);
          registry.addViewController("/savePassword").setViewName("forward:/myController/savePassword");
       registry.addViewController("/login").setViewName("forward:/myController/login");
       registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}

You see in your addViewController you are trying to access a CONTROLLER not a HTML you will access your HTML through the CONTROLLER you access from the method addViewControllers.

UPDATE:

If you put your CSS into your static folder in resources like :

enter image description here

You can simply access them like this :

<link rel="stylesheet" th:href="@{/css/bootstrap-datetimepicker.min.css}" />

Hope this helps!

Upvotes: 3

Related Questions