Controller
Spring Boot follows to MVC pattern. So the controller controls the data flow into model object and updates the view whenever data changes. For example, a model object can be loaded from the db and view can be an html template.
// just for code demonstration
@Controller
@RequestMapping("/articles" )
class ContentController {
@Autowired
lateinit var srv: ContentsService
@GetMapping("/showLst/{idcateg}")
fun showLst(@PathVariable idCateg: Long, @RequestParam mode: Int): ModelAndView =
ModelAndView("/articlelist").apply {
addObject( "some_model_name", srv.loadData(idCateg, mode))
}
@PostMapping("/lists/add")
fun addTopArticleList(@ModelAttribute model: AddForm): String =
"redirect:/lists".apply {
srv.saveArticleList(model.form.toArticleList())
}
}
mapping
Although the @RequestMapping annotation allows define some parameters related to request, usually it is used only for define path of possible request. This annotation can be applied at the method or class level. In the last case, this defines the base URL of the mappings defined at the method level.
The @GET, @POST, @PUT, @DELETE are shorthand for the @RequestMapping with the corresponding http method (default GET). They can be applied only at the method level.
// multiple mapping
@GetMapping(
value = ["/articles/{lang}/{pathNavContents}/{pathSubNavContents}/{pathArticle}",
"/articles/{lang}/{pathNavContents}/{pathSubNavContents}",
"/articles/{lang}/{pathNavContents}"])
// mapping everything that stars with /articles
@GetMapping("/articles/**")
The path variables are defined in annotation in curly brackets and as parameter of method with the @PathVariable annotation. The path variables will be decoded for the controller. To get the encoded URL you can use request context. Spring Boot also contains the UriUtils class that allows you to decode/encode strings, parameters, and the url paths.
Sometimes it’s preferable to get a collection of path variables than enumerate them in a method.
@GetMapping( value =
["/articles/{lang}/{pathNavContents}/{pathSubNavContents}/{pathArticle}",
"/articles/{lang}/{pathNavContents}/{pathSubNavContents}",
"/articles/{lang}/{pathNavContents}"])
fun showArticle(@PathVariable paths: Map<String, String>,
request: HttpServletRequest) = ModelAndView("/article").apply {
srv.loadArticle(paths["lang"]!!,
paths["pathNavContents"]!!,
paths["pathSubNavContents"],
paths["pathArticle"] )
println("pathNavContents=${paths["pathNavContents"]}")
println("uri=${request.requestURI}")
}
/*for url
https://example.com/articles/en/%D0%BA%D0%B0%D0%BA%20%D0%BF%D0%B5%D1%82%D1%8C%20%D0%BF%D0%B5%D1%81%D0%BD%D0%B8
pathNavContents=как петь песни
uri=/articles/en/%D0%BA%D0%B0%D0%BA%20%D0%BF%D0%B5%D1%82%D1%8C%20%D0%BF%D0%B5%D1%81%D0%BD%D0%B8
*/
view
Spring Boot is trying to get a view from the return value of the handler methods. For example, a string value by default will be resolved as name of view. It can be static web page or html template. If necessary, name of view can be prefixed by "redirect:" and "forward:" for redirection.
@GetMapping(value= ["/","/index","/home"])
fun showHome(model: Model) = "/article".apply{
model.addAttribute("articleModel",
contentsSrv.loadArticleVM("","en","home"))
}
// Thymeleaf allows to use a html fragment as view.
// One file can contains multiple fragments.
// after :: follows name of fragment.
@GetMapping("/api/contents/{id}")
fun navContents(@PathVariable id: Long, model: Model)
= "/myfragments :: navContents".apply {
model.addAttribute("contents", contentsSrv.loadNavContents(id))
}
@PostMapping("/myform")
fun handlingForm(@ModelAttribute model: FormData) =
"redirect: myform".apply {...}
If you need return html content directly from the controller method, then use the @ResponseBody annotation. This allows the return value to be treated as a result rather than as a view name. In this case the http response mimetype is "text/plain". You can change it by writing other mimetype or select predefined type from org.springframework.http.MediaType class.
// @GetMapping("/rawHtml" , produces = [MediaType.TEXT_HTML_VALUE])
@GetMapping("/rawHtml" , produces = ["text/html"])
@ResponseBody
fun testString() = "<div>my html content</div>"
view model
There are different ways for passing data to page. One of them is using the ModelAndView class as return type of controller method. Thus, you can specify both the view and the data.
@GetMapping("/articles/{idArticle}")
fun showPage(@PathVariable idArticle: Long)
= ModelAndView("/article").apply {
addObject("articleModel", srv.loadData(idArticle))
}
// same result as above
@GetMapping("/articles/{idArticle}")
fun showPage(@PathVariable idArticle: Long, model: Model)
= "/article".apply {
model.addAttribute("articleModel", srv.loadData(idArticle))
}