Spring: How can I keep routes in sync with URLs on the page? - spring

How can I keep links in my UI templates (e.g. Thymeleaf templates) in sync with the corresponding request mappings in my Spring application?
I've seen that e.g. the Play framework uses the #router-Object within its templates. How is it solved by Spring?
One example:
Spring Controller - simple
#Controller
public class UserController {
#GetMapping("/users/{username}")
public String getUser(#PathParam String username) {
// do some stuff....
return "user";
}
}
HTML-Page
<body>
User details
</body>
Now I want to change "/users" to "/accounts". I'm pretty sure that I've got to update every html page by hand to update the link. Is there an easier solution for this?

As far as I know, there is no simple way to do this with built-in tools from Spring. However, I don't think that this would be too hard to build. You would need the following:
A YAML file with all of your URL templates defined
A Properties Spring bean that contains your URL mappings (read from the YAML file)
All of your #RequestMapping annotations would have to be prop values; i.e.
#GetMapping("${urls.users.byUsername}")
A custom tag that knows about the Properties bean and can create URLs from the templates that were defined in your YAML file.

Related

Are template engines (i.e. Thymeleaf) necessary for Spring Boot applications?

I am currently working on a Spring Boot project and I am fairly new to template engines. This will also be my first own private project with Spring Boot.
I would like to know whether it is necessary to include a template engine, such as Thymeleaf, while developing a web application with Spring Boot. I'm using PostegreSQL for the database.
I read under another post that a template engine is not needed, if the backend framework uses JSON for data exchange, because template engines are for rendering retrieved data to HTML for the client. I retrieve JSON objects from the database, can I leave template engines out of my project then?
If any more details are needed, leave a comment below.
No they aren't necessary, in fact most new projects that require web-pages are using single page applications now like Angular, React, Vue, ... over thymeleaf or jsp.
Aside from that a Spring project doesn't always need web pages, for instance when you are just creating a REST API for other applications to call on, or when you are automating things like: a mail service / printing / ... You name it.
However, when you DO want a simple solution with some pages that aren't all that dynamic or complex, pivotal / VMware does recommend to use thymeleaf (over jsp and other solutions) because it integrates easily.
I read under another post that a template engine is not needed, if the backend framework uses JSON for data exchange, because template engines are for rendering retrieved data to HTML for the client. I retrieve JSON objects from the database, can I leave template engines out of my project then?
This is partly true. Yes, Thymeleaf and alike are mostly intended to render data to HTML. They can render any text data, including JSON, but there are tools better suited for the job. On other hand it does not matter how you store the data in your database or what database you are using. You can't just skip rendering (serializing) the response so it does not matter how you store it. What matters is what you want to return as response. For HTML Thymeleaf or even JSP are suitable, but for JSON you may want to use Jackson or Gson instead.
You didn't mentioned the technology you are going to use, so for my examples I'll assume you intend to use Spring Web MVC. Lets take a look at "traditional" controller:
#Controller
public class GreetingController {
#GetMapping("/greeting")
public ModelAndView greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new ModelAndView("greeting", "greeting", new Greeting(name));
}
}
When you make GET request to "/greeting", Spring will call greeting and get the object it returns. In this case it contains the model (the data we want to render) and the view (the template file to use). Then it will try to find a view (something like greeting.html or greeting.jsp) and use template engine like Thymeleaf (or whatever else is configured) to render it (typically to HTML).
What if we want to return JSON instead? In this case we need to:
modify greeting to return Greeting instance instead of ModelAndView
Use RestController instead of Controller. This will tell Spring MVC that we want to directly serialize the object returned to JSON (or similar format) instead of using template to do that.
Here is the modified example:
#RestController
public class GreetingController {
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(name);
}
}
Spring MVC still needs some help to serialize the Greeting instance to JSON. But if you use Spring Boot and the web starter, you don't have to worry about it. It will include Jackson for you.
This is in no way exhaustive answer. As a matter of fact it skips a lot of details, but I hope nevertheless it is useful one. If you want to create REST API using JSON, this Building a RESTful Web Service guide is a good place to start. If you follow the Starting with Spring Initializr steps you'll get a project setup with only what is needed (well maybe with a bit extra but I would not worry too much about it).

Automatically finding Thymeleaf templates with Spring Boot

How can I get Spring Boot and Thymeleaf to automatically find and map template files to be processed when accessed by the browser?
src/main/resources/templates/index.xhtml
src/main/resources/templates/bar.xhtml
src/main/resources/application.properties contains spring.thymeleaf.suffix=.xhtml
FooController.java contains #RequestMapping("/foo") and a #PostMapping method that returns bar
If I enter http://localhost:8080/ in the browser, Thymeleaf processes and displays the index.xhtml page with no extra configuration needed. But http://localhost:8080/index, http://localhost:8080/index.xhtml, and http://localhost:8080/index.html all result in 404 Not Found.
My index view does a POST to foo; FooController is activated and returns bar; and Thymeleaf processes and shows bar.xhtml, even though bar.xhtml isn't mapped anywhere in the configuration. Yet accessing http://localhost:8080/bar, http://localhost:8080/bar.xhtml, and http://localhost:8080/bar.html in a browser all result in 404 Not Found.
Why does GET http://localhost:8080/ process the index.xhtml template, but GET http://localhost:8080/index does not?
How can Thymleaf use bar as a view, but I cannot access http://localhost:8080/bar directly?
How can I configure Thymeleaf so that I can add src/main/resources/templates/example.xhtml and have it processed automatically as a template that I can access via http://localhost:8080/example in the browser, with no explicit configuration specifically for the example.xhtml file?
If I absolutely have to configure controllers (see my answer below), is there a way that I can at least do this in some declarative file, outside of my code?
As noted in Spring in Action, Fifth Edition, I can do something like this in a #Configuration class that implements WebMvcConfigurer
#Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/bar");
}
That will allow me to process bar.xhtml automatically. (I presume there is some default configuration registry.addViewController("/").setViewName("index"), which is why my index.xhtml file is getting processed by accessing the root path.
And I can even use the following to automatically pick up any template:
#Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/**");
}
Unfortunately this removes the mapping from / to /index, and also prevents accessing any static resources from src/main/resources. I'm not sure how to tell Thymeleaf to use a template if it can, and fall back to a static file if not.

Configuring Default Spring Boot app to render HTML pages and CSS JS content

I configured a Basic Spring Boot app containing Hibernate. However, no page is being rendered.
I have places my pages in src/main/resources/templates/ and content(CSS and JS) in src/main/resources/static/
I have added Thymeleaf through Maven and written a basic controller to render my page -
#RestController
#RequestMapping("/testui")
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
Instead of rendering a page with my name, it is rendering the string - "greeting"
I have placed the greeting.html in templates file. What are the changes in application.properties I need to make to render pages (what keys and values must I give?)
Update - I somehow do not have the webapp folder. Added that. Created a folder called jsp and added a simple page. Referenced it - didnt work
Why is it not working?
Because you are using #RestController. #RestController is used to create API controllers which serve JSON/XML, whereas #Controller is used to create controllers which serve web content such as HTML.
Proposed solution
Replace #RestController with #Controller. For further details, please see the official Getting Started guide of Spring.
You've added #RestController on the Controller class, which means that you want all values returned by the controller methods to be considered as response body (that's the equivalent of annotating all controller methods with #ResponseBody).
Remove that annotation from the class and replace it with #Controller and you should get the expected result.

Dynamic content in all page in Spring MVC

I am developing my site in Spring MVC with Sitemesh.
Dynamic content is changed oon every page, Menu and Footer I can include in template definition. But there comes a problem. On every site below the dynamic content there should be a news list with loaded some news from my DB. I created my #Controller and it loads 5 latest news, but how to add this on my template? What request mapping should implement my news controller?
I don't kwow how Sitemesh works, but I solved problems like that by using interceptor:
create a class that extends : HandlerInterceptorAdapter
Override the method postHandle and populate the modelAndView Object like this :
modelAndView.addObject("newslist",myNewsList);
So you will have a variable $newslist injected into all your views.
Don't forget to declare bean in your mvc-congig.xml :
<bean id="newsListInterceptor" class="mypackage.NewsListInterceptor"/>
As the interceptor is executed for each request I also use ehcache to store the result and avoid during a select in database for each call.

Spring MVC 3.0 - restrict what gets routed through the dispatcher servlet

I want to use Spring MVC 3.0 to build interfaces for AJAX transactions. I want the results to be returned as JSON, but I don't necessarily want the web pages to be built with JSP. I only want requests to the controllers to be intercepted/routed through the DispatcherServlet and the rest of the project to continue to function like a regular Java webapp without Spring integration.
My thought was to define the servlet-mapping url pattern in web.xml as being something like "/controller/*", then have the class level #RequestMapping in my controller to be something like #RequestMapping("/controller/colors"), and finally at the method level, have #RequestMapping(value = "/controller/colors/{name}", method = RequestMethod.GET).
Only problem is, I'm not sure if I need to keep adding "/controller" in all of the RequestMappings and no matter what combo I try, I keep getting 404 requested resource not available errors.
The ultimate goal here is for me to be able to type in a web browser "http://localhost:8080/myproject/controller/colors/red" and get back the RGB value as a JSON string.
You are not correct about needing to add the entire path everywhere, the paths are cumulative-
If you have a servlet mapping of /controller/* for the Spring's DispatcherServlet, then any call to /controller/* will be handled now by the DispatcherServlet, you just have to take care of rest of the path info in your #RequestMapping, so your controller can be
#Controller
#RequestMapping("/colors")
public class MyController{
#RequestMapping("/{name}
public String myMappedMethod(#PathVariable("name") String name, ..){
}
}
So now, this method will be handled by the call to /controller/colors/blue etc.
I don't necessarily want the web pages to be built with JSP
Spring MVC offers many view template integration options, from passthrough to raw html to rich templating engines like Velocity and Freemarker. Perhaps one of those options will fit what you're looking for.

Resources