Thymeleaf + Spring MVC + Rest - spring-boot

I don't understand, how to change #Controller to #RestController for RESTFull serivce, if html template linked with attributes that I get in ModelAndView
#Controller
public class MyController{
#GetMapping("/index")
public ModelAndView index(){
return new ModelAndView("/index", name, userService.getUser().getName());
}
}
and in thymeleaf template it's look like
<p th:text="'Hello, ' + ${name} + '!'" />
But I wanna go to index page, and in background get user name
#RestController
public class MyController{
#GetMapping("/api/user")
public String index(){
return userService.getUser().getName();
}
}
I can use ajax for update tag "p", but in this way it's nothing benefit of using thymeleaf, I can use jsp. So what the best way use thymeleaf with rest and is it rational?

I think the purpose of Thymeleafis for server-side rendering.
Thymeleaf is a Java template engine for processing and creating HTML, XML, JavaScript, CSS, and text.
When you are using JSON API and parse the JSON and use angular or any other client-side rendering framework for that. Thymeleaf with REST is not the approach.
But if you want to use both ways like provide data to Thymeleaf and also provide REST services to other application follow below approach.
#RequestMapping('/foobars')
abstract class FoobarBaseController {
#RequestMapping
abstract listAll()
}
#Controller
class FoobarHtmlController extends FoobarBaseController {
#Override ModelAndView listAll() {
new ModelAndView('foobars/foobarThymeleafTemplate', [foobars: foobarsList])
}
}
#RestController
#RequestMapping('/foobars', produces = MediaType.APPLICATION_JSON_VALUE)
class FoobarJsonController extends FoobarBaseController {
#Override Collection<Foobar> listAll() {
foobarsList
}
}
I hope this address your question properly.

Related

Spring Boot redirect to another url

In my spring boot project i wanted to do a redirection from http://localhost:8080 to http://localhost:8080/birdspotting. This is the code of the Home controller:
#RestController
public class HomeController {
#GetMapping("/")
public String showHomePage() {
return "redirect:/birdspotting";
}
}
The result of going to http://localhost:8080 is a print of redirect:/birdspotting
Basically RestController = Controller + RequestBody
Which will send json response but we are expecting view resolver to return the page or redirect url.
So use #Controller instead of #RestController to fix the issue.
Update:
If you want to use both in Same controller then use #Controller on class level and then wherever you want to return API call response put #ResponseBody on method and wherever you want to return web browser page don't put #ResponseBody.
You have to create the other endpoint like:
#GetMapping("/")
public String showHomePage() {
return "redirect:/birdspotting";
}
#GetMapping("/birdspotting")
public String birdspottingPage() {
return "birdspotting";
}
It's expect you have birdspotting.html in your templates.

Spring Thymeleaf - Call Service Method Boolean for Display of HTML Item

In my Header HTML I display a UL/LI Menu where the visiblity of one of the LI items depends on a Service method call.
I tried this:
HomeController
#Controller
public class HomeController {
private static final Logger log = LogManager.getLogger(HomeController.class);
#Autowired
private EtdService etdService;
#GetMapping("/home")
public String home(Model model) throws EtdException {
model.addAttribute("tierTemplate", etdService.getTierTemplate());
// Also tried this explicitly
model.addAttribute("etdService", etdService);
return "home";
}
}
Service Interface (EtdService)
public interface EtdService {
boolean isChangeUserAllowed();
}
Service Implementation (EtdServiceImpl)
#Component
public class EtdServiceImpl implements EtdService {
#Override
public boolean isChangeUserAllowed() {
System.out.println("Got here");
return false;
}
}
HTML:
<li th:if="${#etdService.isChangeUserAllowed()}" class="nav-item dropdown" id="changeUserPanel" role="presentation">
<!-- ... Definition of this LI -- note can't put a new DIV in a UL list ... -->
</li>
Error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'etdService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:772) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
In addition to the answer by bphilipnyc (set the direct value into the model),
model.addAttribute("isChangeUserAllowed", etdService.isChangeUserAllowed());
If you need to globalize common Model Attributes without re-adding every time, a solution is a #ControllerAdvice class with a #ModelAttribute, e.g.
/**
* This class is used to globalize common Model Attributes without re-adding every time
* The approach is to mark it as #ControllerAdvice to make it apply to every Controller method,
* and implement a #ModelAttribute Model-Adder to append to the model on every Controller method.
*/
// Makes the methods of this class apply to all Controller Request methods
#ControllerAdvice
public class GlobalController {
#Autowired
MyService myService;
#ModelAttribute // A Model Attribute Adder method
public void setGlobalModelAttributes(HttpServletRequest request, Model model) {
model.addAttribute("isChangeUserAllowed", myService.isChangeUserAllowed());
model.addAttribute("currentUserFullName", myService.getCurrentUserFullName());
}
}
Some more examples
https://stackoverflow.com/a/33879102/1005607
https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation
You are referencing an instance method in Thymeleaf. Here are two options:
1) Reference it by adding the value of the boolean to the model:
#GetMapping("/home")
public String home(Model model) throws EtdException {
//...
model.addAttribute("isChangeUserAllowed", etdService.isChangeUserAllowed());
return "home";
}
And in your HTML: th:if="${isChangeUserAllowed}"
To avoid NPEs, you can alternatively use #bools.isTrue(isChangeUserAllowed) or the appropriate method in the bools utility.
This is the preferred way and the path that the Thymeleaf documentation takes. A clear benefit is that the front-end is now not tied to the service.
2) Reference it statically instead (not recommended):
Error trying call method from view Thymeleaf Spring
Aside: the recommended way is to use constructor injection instead of autowiring.

spring-boot application displaying html code for view when executed in the browser

I am recently start to work with spring-boot in my spring projects, and right now I am facing this problem:
I have one spring-boot application with this main class:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and this controller:
#Controller
public class AcessoController {
#RequestMapping(value = "/signin")
public String signin(Model model) {
return "acesso/signin";
}
#RequestMapping(value = "/admin")
public String admin(Model model) {
return "private/admin";
}
#RequestMapping(value = "/index")
public String index(Model model) {
return "public/index";
}
}
when I run the application and try access the url mapping /signin, for example, the browser display the html code for this view, instead of the actual content.
What I am doing wrong here?
Are you trying to render a view using a template engine, or just return a static HTML file?
If you are trying to render a template, then you most likely do not have the right dependency in place to pull in a template engine. (Per your code, I believe this is what you are trying to do.) Even if you don't intend to use the template engine for templates, you will want one to render the HTML for you. Depending on your spring-boot setup, try starting with spring-boot-starter-web, or pull in Thymeleaf (spring-boot-starter-thymeleaf) or Freemarker (spring-boot-starter-freemarker) specifically.
If you want to simply return static content and do not want do custom configuration, you'll need to place the files in a certain location and do not need specific controller request mappings.
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content

How to map Multiple controllers in Spring MVC

I have two controllers in my Application; one is userController, where I have add, delete and update methods; the other one is studentController, where I also have add, delete and update methods.
All the mappings are same in my methods using #RequestMapping annotation in both controllers. I have one confusion: if we are passing the same action from the JSP, then how will the Dispatcher find the corresponding controller? If anybody could describe this using example will be appreciated.
You have to set a #RequestMapping annotation at the class level the value of that annotation will be the prefix of all requests coming to that controller,
for example:
you can have a user controller
#Controller
#RequestMapping("user")
public class UserController {
#RequestMapping("edit")
public ModelAndView edit(#RequestParam(value = "id", required = false) Long id, Map<String, Object> model) {
...
}
}
and a student controller
#Controller
#RequestMapping("student")
public class StudentController {
#RequestMapping("edit")
public ModelAndView edit(#RequestParam(value = "id", required = false) Long id, Map<String, Object> model) {
...
}
}
Both controller have the same method, with same request mapping but you can access them via following uris:
yourserver/user/edit
yourserver/student/edit
hth
We can have any number of controllers, the URL mapping will decide which controller to call..
Please refer here for detailed Spring MVC multiple Controller example

Using SimpleFormController

I have a controller which accepts a form post -
#Controller
public class RegistrationFormController extends SimpleFormController {
.....
.....
#RequestMapping(value="index", method=RequestMethod.POST)
protected ModelAndView onSubmit(#ModelAttribute Registration registration) throws Exception {
String uname=registration.getUsername();
.....
.......
ModelAndView mv = new ModelAndView("success");
.....
......
mv.addObject("addr",addr);
return mv;
}
Thsi would work just as well even if i do not extend SimpleFormController.
What then can I gain by extending?
Well, the setup seems not good. The #Controller annotation is a nice feature which is used for declaring stereotypes. It just says that it is another spring Component or Spring managed bean and can be detected in component scan.
Where as when you extend SimpleFormController you explicitly say that it is a Controller and it has to be used as a controller, it will be used to accept submitted form data and return a response in form of a view.
The two notations in the same class makes no sense at all, I feel that making a class SimpleFormController restricts it from using any method name and you are forced to use a onSubmit method. Whereas, if you use #Controller you leverage all the flexibility in Spring 3 and above.

Resources