Search code examples
spring-boottwitter-bootstrap-3thymeleaf

Why is Thymeleaf/bootstrap rendering not consistent?


I have a Spring Boot / Thymeleaf application. I've noticed that in some situations, the style rendering doesn't happen properly. I finally have one case that is totally unexplainable. I have 2 pieces of code:

@GetMapping(value = "/server-admin"+"/"+"cardtype")
public MyModelAndView adminEditEntityList2(Model model, Locale locale) {return super.adminEditEntityList(model, locale);}

    @GetMapping(value = "/server-admin", params = { "cardtype" })
@Override
public MyModelAndView adminEditEntityList(Model model, Locale locale) {return super.adminEditEntityList(model, locale);}

The resulting html is identical, I'm including only one copy here:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Home</title>
        <meta charset="UTF-8">
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" />
        <link rel="stylesheet" href="css/navbar.css" />
        <link rel="stylesheet" href="css/body.css" />
        <link rel="stylesheet" href="css/footer.css" />
        <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <script src="/resources/pwstrength.js"></script>
        <script src='https://www.google.com/recaptcha/api.js'></script>
    <style>
            .row-even {background-color:white;}
            .row-odd {background-color:white-smoke;}
        </style>
    </head>
    <body>
        <div class="navigation">

            <!-- 
            <div><span th:text="@{__${#httpServletRequest.requestURI}__}"></span></div>
            <div th:with="value=${T(ws.daley.hollow.Params).params(param)}"><span th:text="${value}"></span></div>
            <div><span th:text="${param}"></span></div>
            <div th:each="var:${#vars.getVariableNames()}"><span th:text="'--'+${var}+'->'+${#vars.getVariable(var)}"></span></div>
             -->
            <div id="navbar-examle" class="navbar navbar-static">
                <div class="navbar-inner">
                    <div id="a" class="container" style="width: auto;">
                        <a class="brand" href="#" style="float:left;"><img
                            src="/images/Dadavatar.png"><span>My Project</span></a>
                        <ul id="b" class="nav" role="navigation">
                            <li id="c" class="dropdown" style="float:left;">
                                <a id="drop1" href="#" role="button" class="dropdown-toggle" data-toggle="dropdown"><span>Login</span><b class="caret"></b></a>
                                <ul id="d" class="dropdown-menu" role="menu" aria-labelledby="drop1">
                                    <li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="/login?lang=en_US">Login</a></li>
                                    <li role="presentation" class="active"><a role="menuitem" tabindex="-1" href="/login?logout&amp;lang=en_US">Logout</a></li>
                                    <li role="presentation" class="divider"></li>
                                    <li role="presentation" class="disabled"><a role="menuitem" tabindex="-1" href="/forgetPassword.html?lang=en_US">Reset Password</a></li>
                                    <li role="presentation" class="divider"></li>
                                    <li role="presentation"><a role="menuitem" tabindex="-1" href="/registration.html?lang=en_US">Sign up</a></li>
                                    <li role="presentation"><a role="menuitem" tabindex="-1" href="/registration.html?captcha&amp;lang=en_US">Sign up with Captcha</a></li>
                                    <li role="presentation" class="divider"></li>
                                    <li role="presentation" class="active"><a role="menuitem" tabindex="-1" href="(lang=${lang})" onclick="enable2FA()">Enable 2FA</a></li>
                                    <li role="presentation" class="active"><a role="menuitem" tabindex="-1" href="(lang=${lang})" onclick="disable2FA()">Disable 2FA</a></li>
                                </ul>
                            </li>
                            <li class="dropdown" style="float:left;">
                                <a id="drop2" href="(lang=${lang})" role="button" class="dropdown-toggle" data-toggle="dropdown">

                                    <span>English</span>

                                    <b class="caret"></b></a>
                                <ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
                                    <li role="presentation"><a role="menuitem" tabindex="-1" href="?lang=en_US">English</a></li>
                                    <li role="presentation"><a role="menuitem" tabindex="-1" href="?lang=en_US">Spanish</a></li>
                                </ul>
                            </li>

                                <li class="dropdown" style="float:left;">
                                    <a id="drop3" href="#+&#39;?lang=&#39;+${lang}" role="button" class="dropdown-toggle" data-toggle="dropdown">
                                        <span>Administration</span>
                                        <b class="caret"></b></a>
                                    <ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?loggedlist&amp;lang=en_US">Logged On Users</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?userSession&amp;lang=en_US">User List</a></li>
                                    <li role="presentation" class="divider"></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?user&amp;lang=en_US">User Accounts</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?role&amp;lang=en_US">Roles</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?privilege&amp;lang=en_US">Privileges</a></li>
                                    <li role="presentation" class="divider"></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?card&amp;lang=en_US">Cards</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?cardtype&amp;lang=en_US">Card Types</a></li>
                                    <li role="presentation" class="divider"></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?piprocessor&amp;lang=en_US">PiProcessors</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?node&amp;lang=en_US">Nodes</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-admin?nodefunction&amp;lang=en_US">Node Functions</a></li>
                                    </ul>
                                </li>


                                <li class="dropdown" style="float:left;">
                                    <a id="drop4" href="#+&#39;?lang=&#39;+${lang}" role="button" class="dropdown-toggle" data-toggle="dropdown">
                                        <span>Create Room</span>
                                        <b class="caret"></b></a>
                                    <ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?room&amp;lang=en_US">Rooms</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?item&amp;lang=en_US">Items</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?itemstate&amp;lang=en_US">Item States</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?objective&amp;lang=en_US">Objectives</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?person&amp;lang=en_US">Persons</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?picture&amp;lang=en_US">Pictures</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?pictureimage&amp;lang=en_US">Picture Images</a></li>
                                        <li role="presentation"><a role="menuitem" tabindex="-1" href="/server-room?view&amp;lang=en_US">Views</a></li>
                                    </ul>
                                </li>


                        </ul>
                    </div>
                </div>
            </div>

    </div>










        <div class="content">
            <div>

                <div>
                    <h1>Card Types (Click to Edit)</h1>
                    <div>
                        <div>



                                <div>
                                    <div class="col-md-9">
                                        <a href="/server-admin?lang=en_US&amp;cardtype&amp;edit&amp;name=adhesive"><span>adhesive</span></a>
                                    </div>

                                </div>
                                <div>
                                    <div class="col-md-9">
                                        <a href="/server-admin?lang=en_US&amp;cardtype&amp;edit&amp;name=card"><span>card</span></a>
                                    </div>

                                </div>
                                <div>
                                    <div class="col-md-9">
                                        <a href="/server-admin?lang=en_US&amp;cardtype&amp;edit&amp;name=fob"><span>fob</span></a>
                                    </div>

                                </div>
                                <div>
                                    <div class="col-md-9">
                                        <a href="/server-admin?lang=en_US&amp;cardtype&amp;edit&amp;name=test"><span>test</span></a>
                                    </div>

                                </div>

                        </div>
                        <br/>
                        <div class="rowW col-md-9">
                            <div class="col-md-3 btn btn-primary rowW"><a href="/server-admin?lang=en_US&amp;cardtype&amp;new" class="btn-primary">New Card Type</a></div>
                        </div>
                    </div>
                </div>

            </div>
        </div>
        <br>
        <br />
        <div id="qr">
            <p>
                <span>Scan this Barcode using Google Authenticator app on your phone</span>
                <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Android</a>
                <span>and</span>
                <a href="https://itunes.apple.com/us/app/google-authenticator/id388497605">iPhone</a>
            </p>
        </div>
        <div>
            <footer class="footer">
                <div class="container">
                    <span class="text-muted">&copy; 2017 - Field Server</span>
                </div>
            </footer>
         </div>
    </body>
</html>

Good page: Good page Bad page: Bad page: I've noticed this kind of problem earlier off and on. I never found a case where it is so obvious. I copied and pasted the html from "View Source" in Chrome with vim editor and then use diff to compare the two pages. They are identical. The only change seems to be the URL used for invocation.


Solution

  • Notice the two urls in the browser:

    • localhost:8081/server-admin/cardtype?lang=en_US
    • localhost:8081/server-admin?cardtype&lang=en_US

    Now, your css links:

    <link rel="stylesheet" href="css/navbar.css" />
    <link rel="stylesheet" href="css/body.css" />
    <link rel="stylesheet" href="css/footer.css" />
    

    These are all relative links. Because of that, when you load the page localhost:8081/server-admin/cardtype?lang=en_US it's trying to resolve your css to.

    • localhost:8081/server-admin/css/navbar.css
    • localhost:8081/server-admin/css/body.css
    • localhost:8081/server-admin/css/footer.css

    However, when you are on the page localhost:8081/server-admin?cardtype&lang=en_US, there is no second /. Therefore, the relative links (incorrectly) resolve to:

    • localhost:8081/css/navbar.css
    • localhost:8081/css/body.css
    • localhost:8081/css/footer.css

    This is easily diagnosed by watching the network tab of your browser (there will show a bunch of 404s for your css files). I think the easiest way to fix this would just be to use thymeleafs urls, like this -- which should work regardless of the urls you use.

    <link rel="stylesheet" th:href="@{/css/navbar.css}" />
    <link rel="stylesheet" th:href="@{/css/body.css}" />
    <link rel="stylesheet" th:href="@{/css/footer.css}" />