Spring MVC Concepts: Difference between revisions
(→Model) |
|||
(41 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
=Internal= | =Internal= | ||
* [[ | * [[Spring_Framework#Spring_Framework_Core_Technologies_Concepts|Spring Framework]] | ||
* [[Spring REST Concepts]] | |||
=To Process= | =To Process= | ||
<font color=darkgray>TO PROCESS: | <font color=darkgray>TO PROCESS: | ||
Line 13: | Line 13: | ||
* https://docs.spring.io/spring-boot/docs/2.0.5.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications | * https://docs.spring.io/spring-boot/docs/2.0.5.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications | ||
</FONT> | </FONT> | ||
=Project Directory Layout= | |||
<font size=-1> | |||
src/main/resource/static | |||
src/main/resource/static/images | |||
src/main/resource/templates | |||
</font> | |||
=Model= | =Model= | ||
[https://docs.spring.io/spring-framework/docs/5.1.0.RELEASE/javadoc-api/ org.springframework.ui.Model] | The model is an object that ferries data between a [[#Controller|controller]] and whatever [[#View|view]] is charged with rendering that data. [https://docs.spring.io/spring-framework/docs/5.1.0.RELEASE/javadoc-api/ org.springframework.ui.Model] is a holder for model attributes, conceptually a map. Ultimately, data that is placed in the model's attributes will be copied into the servlet response attributes, where the [[#View|view]] can find them. [[@ModelAttribute]] annotations insure that the corresponding object instances are created in the model. | ||
=Controller= | =Controller= | ||
A controller is a class that handles requests, fetches and processes data, and then responds with information of some sort. The controller's method handing the requests are named '''handler methods'''. Controller classes must be annotated with [[@Controller]]. | A controller is a class that handles requests, fetches and processes data, and then responds with information of some sort. The controller's method handing the requests are named '''handler methods'''. Controller classes must be annotated with [[@Controller]] [[Spring_Dependency_Injection_and_Inversion_of_Control_Container_Concepts#Stereotypes|stereotype annotation]]. | ||
In case of a browser-facing application, a controller responds by optionally populating model data and passing the request to a view that produces HTML to be returned to the browser. The view that is supposed to render the response is indicated by its [[#View_Logical_Name|logical name]], which is returned by the handler method. | In case of a browser-facing application, a controller responds by optionally populating model data and passing the request to a [[#View|view]] that produces HTML to be returned to the browser. The view that is supposed to render the response is indicated by its [[#View_Logical_Name|logical name]], which is returned by the handler method. | ||
<syntaxhighlight lang='java'> | <syntaxhighlight lang='java'> | ||
Line 36: | Line 43: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
In case of a REST application, the controller writes data directly into the body of the response. | In case of a REST application, the controller writes data directly into the body of the response. | ||
A conventional pattern to designate the responsibilities of the handler methods and to associate them with paths and request types is to use [[@RequestMapping]] at class level to configure the '''base path''', and then use [[@GetMapping]], [[@PostMapping]], [[@PutMapping]], [[@DeleteMapping]] and [[@PatchMapping]] to designate HTTP method-specific handlers: | A conventional pattern to designate the responsibilities of the handler methods and to associate them with paths and request types is to use [[@RequestMapping]] at class level to configure the '''base path''', and then use <span id='@GetMapping'></span>[[@GetMapping]], <span id='@PostMapping'></span>[[@PostMapping]], <span id='@PutMapping'></span>[[@PutMapping]], <span id='@DeleteMapping'></span>[[@DeleteMapping]] and <span id='@PatchMapping'></span>[[@PatchMapping]] to designate HTTP method-specific handlers: | ||
<syntaxhighlight lang='java'> | <syntaxhighlight lang='java'> | ||
Line 62: | Line 68: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Controller Annotations== | |||
* [[@RequestMapping]] | |||
* [[@GetMapping]] | |||
* [[@PostMapping]] | |||
* [[@PutMapping]] | |||
* [[@DeleteMapping]] | |||
* [[@PatchMapping]] | |||
* [[@SessionAttributes]] | |||
* [[@ModelAttribute]] | |||
* [[@ResponseBody]] | |||
* [[@ResponseStatus]] | |||
* [[@PathVariable]] | |||
===Common Annotation Configuration Elements=== | |||
====path==== | |||
[[@RequestMapping]], [[@GetMapping]], etc. accept a "path" data member. The path accepts placeholders: | |||
"/{id}" | |||
Placeholders are captured with [[@PathVariable]] as follows: | |||
<syntaxhighlight lang='java'> | |||
@GetMapping("/{id}") | |||
public ... someMethod(@PathVariable("id") Long id) { | |||
... | |||
} | |||
</syntaxhighlight> | |||
==View Controller== | |||
A '''view controller''' is a controller that is simply enough that it does not populate a model or process input, but simply forward the request to a view. View controller do not require a class on their own, as there is no functionality to populate that class with. Instead, they can be defined by using a specific API in a [[Spring_Dependency_Injection_and_Inversion_of_Control_Container_Concepts#Configuration_Class|configuration class]]. | |||
<syntaxhighlight lang='java'> | |||
@Configuration | |||
public class MyWebConfiguration implements WebMvcConfigurer { | |||
@Override | |||
public void addViewControllers(ViewControllerRegistry registry) { | |||
registry.addViewController("/").setViewName("home"); | |||
} | |||
} | |||
</syntaxhighlight> | |||
The API allows to associate a GET request path with a [[#View_Logical_Name|view logical name]], without the need to write a controller class. ViewControllerRegistry.addViewController() returns a ViewControllerRegistration object on which setViewName() lets you to associate a view with the path. | |||
=View= | =View= | ||
The view renders data into HTML format | The view renders data into HTML format. | ||
The view is instantiated dynamically, and its implementation depends on the template engine that is available in the classpath. Template libraries are designed to be decoupled by any particular web framework, so they are unaware of Spring's model abstraction and are not able to work with the data the [[#Controller|controller]] places in the [[#Model|model]]. They can work with servlet request attributes, though, and Spring copies the model data into the request attributes before the request is handed over to the view. | |||
= | ==View Logical Name== | ||
The template name is derived from the [[#View_Logical_Name|logical name]] by prefixing it with /templates and postfixing it with .html. Simply placing a <''logical-view-name''>.html under src/resources/templates makes the template-based view available. | |||
==Redirect View== | |||
<syntaxhighlight lang='java'> | |||
"redirect:/home" | |||
</syntaxhighlight> | |||
==Template Libraries== | |||
<font color=darkgray>[[SIA]] page 52.</font> | |||
==Template Caching== | |||
By default, templates are only parsed once, when they’re first used, and the results of that parse are cached for subsequent use. This is desirable or production, as it prevents redundant template parsing on each request and thus improves performance. However, in development we want to be able to modify templates and test results so we need to disable template caching. | |||
spring.thymeleaf.cache=false | |||
spring.mustache.cache=false | |||
spring.groovy.template.cache=false | |||
spring.freemarker.cache=false | |||
[[Spring_Boot_Concepts#Developer_Tools_.28DevTools.29|DevTools]] disable template caching by default. | |||
=Configuring MVC Applications= | |||
Configuration in this context refers to [[Spring_Dependency_Injection_and_Inversion_of_Control_Container_Concepts#Configuration_Model|component configuration]]. | |||
<font color=darkgray>WebMvcConfigurer</font> | |||
=Property Configuration for MVC Applications= | |||
<font color=darkgray> | |||
TODO: | |||
export SERVER_PORT=9999 | |||
</font> | |||
Also see: {{Internal|Spring_Property_Injection_Concepts#The_PropertySource_Abstraction|Spring Property Injection Concepts - The PropertySource Abstraction}} | |||
=Testing MVC Applications= | =Testing MVC Applications= | ||
Line 105: | Line 193: | ||
Spring MVC testing implies using [[@WebMvcTest]]. MockMvc mocks the mechanics of Spring MVC, instead of starting a full blown web server. | Spring MVC testing implies using [[@WebMvcTest]]. MockMvc mocks the mechanics of Spring MVC, instead of starting a full blown web server. | ||
=REST | =<span id='REST_Clients'></span><span id='RestTemplate'></span>REST= | ||
{{Internal|Spring REST Concepts|Spring REST Concepts}} | |||
Latest revision as of 23:42, 6 February 2022
Internal
To Process
TO PROCESS:
- https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web.html#spring-web
- https://spring.io/guides/gs/rest-service/
- https://spring.io/guides/gs/serving-web-content/
- https://spring.io/guides/tutorials/bookmarks/
- https://docs.spring.io/spring-boot/docs/2.0.5.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications
Project Directory Layout
src/main/resource/static src/main/resource/static/images src/main/resource/templates
Model
The model is an object that ferries data between a controller and whatever view is charged with rendering that data. org.springframework.ui.Model is a holder for model attributes, conceptually a map. Ultimately, data that is placed in the model's attributes will be copied into the servlet response attributes, where the view can find them. @ModelAttribute annotations insure that the corresponding object instances are created in the model.
Controller
A controller is a class that handles requests, fetches and processes data, and then responds with information of some sort. The controller's method handing the requests are named handler methods. Controller classes must be annotated with @Controller stereotype annotation.
In case of a browser-facing application, a controller responds by optionally populating model data and passing the request to a view that produces HTML to be returned to the browser. The view that is supposed to render the response is indicated by its logical name, which is returned by the handler method.
@Controller
public class HomeController {
@GetMapping("/")
public String home() {
// returns the view name
return "home";
}
}
In case of a REST application, the controller writes data directly into the body of the response.
A conventional pattern to designate the responsibilities of the handler methods and to associate them with paths and request types is to use @RequestMapping at class level to configure the base path, and then use @GetMapping, @PostMapping, @PutMapping, @DeleteMapping and @PatchMapping to designate HTTP method-specific handlers:
@Controller
@RequestMapping("/something")
public class SomeController {
@GetMapping
public String read() {
...
}
@PostMapping
public String create() {
...
}
@PutMapping
public String update() {
...
}
}
Controller Annotations
- @RequestMapping
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
- @SessionAttributes
- @ModelAttribute
- @ResponseBody
- @ResponseStatus
- @PathVariable
Common Annotation Configuration Elements
path
@RequestMapping, @GetMapping, etc. accept a "path" data member. The path accepts placeholders:
"/{id}"
Placeholders are captured with @PathVariable as follows:
@GetMapping("/{id}")
public ... someMethod(@PathVariable("id") Long id) {
...
}
View Controller
A view controller is a controller that is simply enough that it does not populate a model or process input, but simply forward the request to a view. View controller do not require a class on their own, as there is no functionality to populate that class with. Instead, they can be defined by using a specific API in a configuration class.
@Configuration
public class MyWebConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
The API allows to associate a GET request path with a view logical name, without the need to write a controller class. ViewControllerRegistry.addViewController() returns a ViewControllerRegistration object on which setViewName() lets you to associate a view with the path.
View
The view renders data into HTML format.
The view is instantiated dynamically, and its implementation depends on the template engine that is available in the classpath. Template libraries are designed to be decoupled by any particular web framework, so they are unaware of Spring's model abstraction and are not able to work with the data the controller places in the model. They can work with servlet request attributes, though, and Spring copies the model data into the request attributes before the request is handed over to the view.
View Logical Name
The template name is derived from the logical name by prefixing it with /templates and postfixing it with .html. Simply placing a <logical-view-name>.html under src/resources/templates makes the template-based view available.
Redirect View
"redirect:/home"
Template Libraries
SIA page 52.
Template Caching
By default, templates are only parsed once, when they’re first used, and the results of that parse are cached for subsequent use. This is desirable or production, as it prevents redundant template parsing on each request and thus improves performance. However, in development we want to be able to modify templates and test results so we need to disable template caching.
spring.thymeleaf.cache=false spring.mustache.cache=false spring.groovy.template.cache=false spring.freemarker.cache=false
DevTools disable template caching by default.
Configuring MVC Applications
Configuration in this context refers to component configuration.
WebMvcConfigurer
Property Configuration for MVC Applications
TODO:
export SERVER_PORT=9999
Also see:
Testing MVC Applications
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.hamcrest.Matchers.containsString;
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")).
andExpect(status().isOk()).
andExpect(view().name("home")).
andExpect(content().string(containsString("Welcome to ...")));
}
}
Spring MVC testing implies using @WebMvcTest. MockMvc mocks the mechanics of Spring MVC, instead of starting a full blown web server.