parent
7cfae94d70
commit
0d3fb0ee0f
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
|
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
UriComponents uriComponents = UriComponentsBuilder
|
UriComponents uriComponents = UriComponentsBuilder
|
||||||
.fromUriString("https://example.com/hotels/{hotel}") // <1>
|
.fromUriString("https://example.com/hotels/{hotel}") // <1>
|
||||||
|
|
@ -21,12 +21,28 @@
|
||||||
<4> Build a `UriComponents`.
|
<4> Build a `UriComponents`.
|
||||||
<5> Expand variables and obtain the `URI`.
|
<5> Expand variables and obtain the `URI`.
|
||||||
|
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uriComponents = UriComponentsBuilder
|
||||||
|
.fromUriString("https://example.com/hotels/{hotel}") // <1>
|
||||||
|
.queryParam("q", "{q}") // <2>
|
||||||
|
.encode() // <3>
|
||||||
|
.build() // <4>
|
||||||
|
|
||||||
|
val uri = uriComponents.expand("Westin", "123").toUri() // <5>
|
||||||
|
----
|
||||||
|
<1> Static factory method with a URI template.
|
||||||
|
<2> Add or replace URI components.
|
||||||
|
<3> Request to have the URI template and URI variables encoded.
|
||||||
|
<4> Build a `UriComponents`.
|
||||||
|
<5> Expand variables and obtain the `URI`.
|
||||||
|
|
||||||
The preceding example can be consolidated into one chain and shortened with `buildAndExpand`,
|
The preceding example can be consolidated into one chain and shortened with `buildAndExpand`,
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
URI uri = UriComponentsBuilder
|
URI uri = UriComponentsBuilder
|
||||||
.fromUriString("https://example.com/hotels/{hotel}")
|
.fromUriString("https://example.com/hotels/{hotel}")
|
||||||
|
|
@ -35,28 +51,54 @@ as the following example shows:
|
||||||
.buildAndExpand("Westin", "123")
|
.buildAndExpand("Westin", "123")
|
||||||
.toUri();
|
.toUri();
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uri = UriComponentsBuilder
|
||||||
|
.fromUriString("https://example.com/hotels/{hotel}")
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.encode()
|
||||||
|
.buildAndExpand("Westin", "123")
|
||||||
|
.toUri()
|
||||||
|
----
|
||||||
|
|
||||||
You can shorten it further by going directly to a URI (which implies encoding),
|
You can shorten it further by going directly to a URI (which implies encoding),
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
URI uri = UriComponentsBuilder
|
URI uri = UriComponentsBuilder
|
||||||
.fromUriString("https://example.com/hotels/{hotel}")
|
.fromUriString("https://example.com/hotels/{hotel}")
|
||||||
.queryParam("q", "{q}")
|
.queryParam("q", "{q}")
|
||||||
.build("Westin", "123");
|
.build("Westin", "123");
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uri = UriComponentsBuilder
|
||||||
|
.fromUriString("https://example.com/hotels/{hotel}")
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.build("Westin", "123")
|
||||||
|
----
|
||||||
|
|
||||||
You shorter it further still with a full URI template, as the following example shows:
|
You shorter it further still with a full URI template, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
URI uri = UriComponentsBuilder
|
URI uri = UriComponentsBuilder
|
||||||
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
||||||
.build("Westin", "123");
|
.build("Westin", "123");
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uri = UriComponentsBuilder
|
||||||
|
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
|
||||||
|
.build("Westin", "123")
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,39 +118,62 @@ exposes shared configuration options.
|
||||||
|
|
||||||
The following example shows how to configure a `RestTemplate`:
|
The following example shows how to configure a `RestTemplate`:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
||||||
|
|
||||||
String baseUrl = "https://example.org";
|
String baseUrl = "https://example.org";
|
||||||
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
||||||
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VARIABLES);
|
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
|
||||||
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
restTemplate.setUriTemplateHandler(factory);
|
restTemplate.setUriTemplateHandler(factory);
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
|
||||||
|
|
||||||
|
val baseUrl = "https://example.org"
|
||||||
|
val factory = DefaultUriBuilderFactory(baseUrl)
|
||||||
|
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
|
||||||
|
|
||||||
|
val restTemplate = RestTemplate()
|
||||||
|
restTemplate.uriTemplateHandler = factory
|
||||||
|
----
|
||||||
|
|
||||||
The following example configures a `WebClient`:
|
The following example configures a `WebClient`:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
||||||
|
|
||||||
String baseUrl = "https://example.org";
|
String baseUrl = "https://example.org";
|
||||||
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
|
||||||
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VARIABLES);
|
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
|
||||||
|
|
||||||
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
|
||||||
|
|
||||||
|
val baseUrl = "https://example.org"
|
||||||
|
val factory = DefaultUriBuilderFactory(baseUrl)
|
||||||
|
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
|
||||||
|
|
||||||
|
val client = WebClient.builder().uriBuilderFactory(factory).build()
|
||||||
|
----
|
||||||
|
|
||||||
In addition, you can also use `DefaultUriBuilderFactory` directly. It is similar to using
|
In addition, you can also use `DefaultUriBuilderFactory` directly. It is similar to using
|
||||||
`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
|
`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
|
||||||
that holds configuration and preferences, as the following example shows:
|
that holds configuration and preferences, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
String baseUrl = "https://example.com";
|
String baseUrl = "https://example.com";
|
||||||
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
|
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
|
||||||
|
|
@ -117,6 +182,16 @@ that holds configuration and preferences, as the following example shows:
|
||||||
.queryParam("q", "{q}")
|
.queryParam("q", "{q}")
|
||||||
.build("Westin", "123");
|
.build("Westin", "123");
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val baseUrl = "https://example.com"
|
||||||
|
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
|
||||||
|
|
||||||
|
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.build("Westin", "123")
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -144,10 +219,10 @@ URI variables intentionally contain reserved characters.
|
||||||
|
|
||||||
The following example uses the first option:
|
The following example uses the first option:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||||
.queryParam("q", "{q}")
|
.queryParam("q", "{q}")
|
||||||
.encode()
|
.encode()
|
||||||
.buildAndExpand("New York", "foo+bar")
|
.buildAndExpand("New York", "foo+bar")
|
||||||
|
|
@ -155,24 +230,48 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||||
|
|
||||||
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
|
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.encode()
|
||||||
|
.buildAndExpand("New York", "foo+bar")
|
||||||
|
.toUri()
|
||||||
|
|
||||||
|
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
|
||||||
|
----
|
||||||
|
|
||||||
You can shorten the preceding example by going directly to the URI (which implies encoding),
|
You can shorten the preceding example by going directly to the URI (which implies encoding),
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||||
|
.queryParam("q", "{q}")
|
||||||
|
.build("New York", "foo+bar")
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
||||||
.queryParam("q", "{q}")
|
.queryParam("q", "{q}")
|
||||||
.build("New York", "foo+bar")
|
.build("New York", "foo+bar")
|
||||||
----
|
----
|
||||||
|
|
||||||
You can shorten it further still with a full URI template, as the following example shows:
|
You can shorten it further still with a full URI template, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}")
|
RI uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}")
|
||||||
|
.build("New York", "foo+bar")
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}")
|
||||||
.build("New York", "foo+bar")
|
.build("New York", "foo+bar")
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
@ -180,8 +279,8 @@ The `WebClient` and the `RestTemplate` expand and encode URI templates internall
|
||||||
the `UriBuilderFactory` strategy. Both can be configured with a custom strategy.
|
the `UriBuilderFactory` strategy. Both can be configured with a custom strategy.
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
String baseUrl = "https://example.com";
|
String baseUrl = "https://example.com";
|
||||||
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
|
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
|
||||||
|
|
@ -194,6 +293,22 @@ as the following example shows:
|
||||||
// Customize the WebClient..
|
// Customize the WebClient..
|
||||||
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val baseUrl = "https://example.com"
|
||||||
|
val factory = DefaultUriBuilderFactory(baseUrl).apply {
|
||||||
|
encodingMode = EncodingMode.TEMPLATE_AND_VALUES
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customize the RestTemplate..
|
||||||
|
val restTemplate = RestTemplate().apply {
|
||||||
|
uriTemplateHandler = factory
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customize the WebClient..
|
||||||
|
val client = WebClient.builder().uriBuilderFactory(factory).build()
|
||||||
|
----
|
||||||
|
|
||||||
The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to
|
The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to
|
||||||
expand and encode URI templates. As a factory, it provides a single place to configure
|
expand and encode URI templates. As a factory, it provides a single place to configure
|
||||||
|
|
|
||||||
|
|
@ -83,24 +83,43 @@ The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
|
||||||
annotation enables cross-origin requests on annotated controller methods,
|
annotation enables cross-origin requests on annotated controller methods,
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/account")
|
@RequestMapping("/account")
|
||||||
public class AccountController {
|
public class AccountController {
|
||||||
|
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Account retrieve(@PathVariable Long id) {
|
public Account retrieve(@PathVariable Long id) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void remove(@PathVariable Long id) {
|
public void remove(@PathVariable Long id) {
|
||||||
// ...
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/account")
|
||||||
|
class AccountController {
|
||||||
|
|
||||||
|
@CrossOrigin
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
fun retrieve(@PathVariable id: Long): Account {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
fun remove(@PathVariable id: Long) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
----
|
----
|
||||||
|
|
||||||
By default, `@CrossOrigin` allows:
|
By default, `@CrossOrigin` allows:
|
||||||
|
|
@ -118,8 +137,8 @@ should only be used where appropriate.
|
||||||
`@CrossOrigin` is supported at the class level, too, and is inherited by all methods,
|
`@CrossOrigin` is supported at the class level, too, and is inherited by all methods,
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
|
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
|
||||||
@RestController
|
@RestController
|
||||||
|
|
@ -137,29 +156,67 @@ public class AccountController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@CrossOrigin(origins = ["https://domain2.com"], maxAge = 3600)
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/account")
|
||||||
|
class AccountController {
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
fun retrieve(@PathVariable id: Long): Account {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
fun remove(@PathVariable id: Long) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
You can use `@CrossOrigin` at both the class level and the method level,
|
You can use `@CrossOrigin` at both the class level and the method level,
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@CrossOrigin(maxAge = 3600)
|
@CrossOrigin(maxAge = 3600)
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/account")
|
@RequestMapping("/account")
|
||||||
public class AccountController {
|
public class AccountController {
|
||||||
|
|
||||||
@CrossOrigin("https://domain2.com")
|
@CrossOrigin("https://domain2.com")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Account retrieve(@PathVariable Long id) {
|
public Account retrieve(@PathVariable Long id) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void remove(@PathVariable Long id) {
|
public void remove(@PathVariable Long id) {
|
||||||
// ...
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@CrossOrigin(maxAge = 3600)
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/account")
|
||||||
|
class AccountController {
|
||||||
|
|
||||||
|
@CrossOrigin("https://domain2.com")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
fun retrieve(@PathVariable id: Long): Account {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
fun remove(@PathVariable id: Long) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -196,26 +253,46 @@ should only be used where appropriate.
|
||||||
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
|
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
public class WebConfig implements WebMvcConfigurer {
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
|
||||||
registry.addMapping("/api/**")
|
registry.addMapping("/api/**")
|
||||||
.allowedOrigins("https://domain2.com")
|
.allowedOrigins("https://domain2.com")
|
||||||
.allowedMethods("PUT", "DELETE")
|
.allowedMethods("PUT", "DELETE")
|
||||||
.allowedHeaders("header1", "header2", "header3")
|
.allowedHeaders("header1", "header2", "header3")
|
||||||
.exposedHeaders("header1", "header2")
|
.exposedHeaders("header1", "header2")
|
||||||
.allowCredentials(true).maxAge(3600);
|
.allowCredentials(true).maxAge(3600);
|
||||||
|
|
||||||
// Add more mappings...
|
// Add more mappings...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
class WebConfig : WebMvcConfigurer {
|
||||||
|
|
||||||
|
override fun addCorsMappings(registry: CorsRegistry) {
|
||||||
|
|
||||||
|
registry.addMapping("/api/**")
|
||||||
|
.allowedOrigins("https://domain2.com")
|
||||||
|
.allowedMethods("PUT", "DELETE")
|
||||||
|
.allowedHeaders("header1", "header2", "header3")
|
||||||
|
.exposedHeaders("header1", "header2")
|
||||||
|
.allowCredentials(true).maxAge(3600)
|
||||||
|
|
||||||
|
// Add more mappings...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -226,8 +303,7 @@ public class WebConfig implements WebMvcConfigurer {
|
||||||
To enable CORS in the XML namespace, you can use the `<mvc:cors>` element,
|
To enable CORS in the XML namespace, you can use the `<mvc:cors>` element,
|
||||||
as the following example shows:
|
as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim"]
|
||||||
[subs="verbatim"]
|
|
||||||
----
|
----
|
||||||
<mvc:cors>
|
<mvc:cors>
|
||||||
|
|
||||||
|
|
@ -262,21 +338,39 @@ for CORS.
|
||||||
To configure the filter, pass a
|
To configure the filter, pass a
|
||||||
`CorsConfigurationSource` to its constructor, as the following example shows:
|
`CorsConfigurationSource` to its constructor, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||||
[subs="verbatim"]
|
.Java
|
||||||
----
|
----
|
||||||
CorsConfiguration config = new CorsConfiguration();
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
|
||||||
// Possibly...
|
// Possibly...
|
||||||
// config.applyPermitDefaultValues()
|
// config.applyPermitDefaultValues()
|
||||||
|
|
||||||
config.setAllowCredentials(true);
|
config.setAllowCredentials(true);
|
||||||
config.addAllowedOrigin("https://domain1.com");
|
config.addAllowedOrigin("https://domain1.com");
|
||||||
config.addAllowedHeader("*");
|
config.addAllowedHeader("*");
|
||||||
config.addAllowedMethod("*");
|
config.addAllowedMethod("*");
|
||||||
|
|
||||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
source.registerCorsConfiguration("/**", config);
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
|
||||||
CorsFilter filter = new CorsFilter(source);
|
CorsFilter filter = new CorsFilter(source);
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val config = CorsConfiguration()
|
||||||
|
|
||||||
|
// Possibly...
|
||||||
|
// config.applyPermitDefaultValues()
|
||||||
|
|
||||||
|
config.allowCredentials = true
|
||||||
|
config.addAllowedOrigin("https://domain1.com")
|
||||||
|
config.addAllowedHeader("*")
|
||||||
|
config.addAllowedMethod("*")
|
||||||
|
|
||||||
|
val source = UrlBasedCorsConfigurationSource()
|
||||||
|
source.registerCorsConfiguration("/**", config)
|
||||||
|
|
||||||
|
val filter = CorsFilter(source)
|
||||||
----
|
----
|
||||||
|
|
|
||||||
|
|
@ -46,33 +46,51 @@ integration for using Spring MVC with FreeMarker templates.
|
||||||
|
|
||||||
The following example shows how to configure FreeMarker as a view technology:
|
The following example shows how to configure FreeMarker as a view technology:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureViewResolvers(ViewResolverRegistry registry) {
|
||||||
|
registry.freeMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure FreeMarker...
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FreeMarkerConfigurer freeMarkerConfigurer() {
|
||||||
|
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
||||||
|
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
|
||||||
|
return configurer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
----
|
----
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
public class WebConfig implements WebMvcConfigurer {
|
class WebConfig : WebMvcConfigurer {
|
||||||
|
|
||||||
@Override
|
override fun configureViewResolvers(registry: ViewResolverRegistry) {
|
||||||
public void configureViewResolvers(ViewResolverRegistry registry) {
|
registry.freeMarker()
|
||||||
registry.freemarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure FreeMarker...
|
// Configure FreeMarker...
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public FreeMarkerConfigurer freeMarkerConfigurer() {
|
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
|
||||||
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
setTemplateLoaderPath("/WEB-INF/freemarker")
|
||||||
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
|
|
||||||
return configurer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The following example shows how to configure the same in XML:
|
The following example shows how to configure the same in XML:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<mvc:annotation-driven/>
|
<mvc:annotation-driven/>
|
||||||
|
|
||||||
|
|
@ -89,8 +107,7 @@ The following example shows how to configure the same in XML:
|
||||||
Alternatively, you can also declare the `FreeMarkerConfigurer` bean for full control over all
|
Alternatively, you can also declare the `FreeMarkerConfigurer` bean for full control over all
|
||||||
properties, as the following example shows:
|
properties, as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
|
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
|
||||||
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
||||||
|
|
@ -114,8 +131,7 @@ properties on the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property
|
||||||
a `java.util.Properties` object, and the `freemarkerVariables` property requires a
|
a `java.util.Properties` object, and the `freemarkerVariables` property requires a
|
||||||
`java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`:
|
`java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
|
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
|
||||||
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
||||||
|
|
@ -167,8 +183,7 @@ controller, you can use code similar to the next example to bind to field values
|
||||||
display error messages for each input field in similar fashion to the JSP equivalent. The
|
display error messages for each input field in similar fashion to the JSP equivalent. The
|
||||||
following example shows a `personForm` view:
|
following example shows a `personForm` view:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<!-- FreeMarker macros have to be imported into a namespace.
|
<!-- FreeMarker macros have to be imported into a namespace.
|
||||||
We strongly recommend sticking to 'spring'. -->
|
We strongly recommend sticking to 'spring'. -->
|
||||||
|
|
@ -309,8 +324,7 @@ time, a class name or style attribute. Note that FreeMarker can specify default
|
||||||
values for the attributes parameter. The following example shows how to use the `formInput`
|
values for the attributes parameter. The following example shows how to use the `formInput`
|
||||||
and `showWErrors` macros:
|
and `showWErrors` macros:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<@spring.formInput "command.name"/>
|
<@spring.formInput "command.name"/>
|
||||||
<@spring.showErrors "<br>"/>
|
<@spring.showErrors "<br>"/>
|
||||||
|
|
@ -322,8 +336,7 @@ occurs through Spring's Validation framework.
|
||||||
|
|
||||||
The generated HTML resembles the following example:
|
The generated HTML resembles the following example:
|
||||||
|
|
||||||
[source,jsp,indent=0]
|
[source,jsp,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
Name:
|
Name:
|
||||||
<input type="text" name="name" value="">
|
<input type="text" name="name" value="">
|
||||||
|
|
@ -357,8 +370,7 @@ value of 'London' for this field, so no validation is necessary. When the form i
|
||||||
rendered, the entire list of cities to choose from is supplied as reference data in the
|
rendered, the entire list of cities to choose from is supplied as reference data in the
|
||||||
model under the name 'cityMap'. The following listing shows the example:
|
model under the name 'cityMap'. The following listing shows the example:
|
||||||
|
|
||||||
[source,jsp,indent=0]
|
[source,jsp,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
...
|
...
|
||||||
Town:
|
Town:
|
||||||
|
|
@ -372,8 +384,7 @@ keys are what the form actually submits as `POST` request parameters. The map va
|
||||||
labels that the user sees. In the preceding example, given a list of three well known cities
|
labels that the user sees. In the preceding example, given a list of three well known cities
|
||||||
and a default value in the form backing object, the HTML resembles the following:
|
and a default value in the form backing object, the HTML resembles the following:
|
||||||
|
|
||||||
[source,jsp,indent=0]
|
[source,jsp,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
Town:
|
Town:
|
||||||
<input type="radio" name="address.town" value="London">London</input>
|
<input type="radio" name="address.town" value="London">London</input>
|
||||||
|
|
@ -384,26 +395,37 @@ and a default value in the form backing object, the HTML resembles the following
|
||||||
If your application expects to handle cities by internal codes (for example), you can create the map of
|
If your application expects to handle cities by internal codes (for example), you can create the map of
|
||||||
codes with suitable keys, as the following example shows:
|
codes with suitable keys, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
protected Map<String, String> referenceData(HttpServletRequest request) throws Exception {
|
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
|
||||||
Map<String, String> cityMap = new LinkedHashMap<>();
|
Map<String, String> cityMap = new LinkedHashMap<>();
|
||||||
cityMap.put("LDN", "London");
|
cityMap.put("LDN", "London");
|
||||||
cityMap.put("PRS", "Paris");
|
cityMap.put("PRS", "Paris");
|
||||||
cityMap.put("NYC", "New York");
|
cityMap.put("NYC", "New York");
|
||||||
|
|
||||||
Map<String, String> model = new HashMap<>();
|
Map<String, Object> model = new HashMap<>();
|
||||||
model.put("cityMap", cityMap);
|
model.put("cityMap", cityMap);
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
protected fun referenceData(request: HttpServletRequest): Map<String, *> {
|
||||||
|
val cityMap = linkedMapOf(
|
||||||
|
"LDN" to "London",
|
||||||
|
"PRS" to "Paris",
|
||||||
|
"NYC" to "New York"
|
||||||
|
)
|
||||||
|
return hashMapOf("cityMap" to cityMap)
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
The code now produces output where the radio values are the relevant codes, but the
|
The code now produces output where the radio values are the relevant codes, but the
|
||||||
user still sees the more user-friendly city names, as follows:
|
user still sees the more user-friendly city names, as follows:
|
||||||
|
|
||||||
[source,jsp,indent=0]
|
[source,jsp,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
Town:
|
Town:
|
||||||
<input type="radio" name="address.town" value="LDN">London</input>
|
<input type="radio" name="address.town" value="LDN">London</input>
|
||||||
|
|
@ -426,8 +448,7 @@ template processing to provide different behavior for different fields in your f
|
||||||
To switch to XHTML compliance for your tags, specify a value of `true` for a
|
To switch to XHTML compliance for your tags, specify a value of `true` for a
|
||||||
model or context variable named `xhtmlCompliant`, as the following example shows:
|
model or context variable named `xhtmlCompliant`, as the following example shows:
|
||||||
|
|
||||||
[source,jsp,indent=0]
|
[source,jsp,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<#-- for FreeMarker -->
|
<#-- for FreeMarker -->
|
||||||
<#assign xhtmlCompliant = true>
|
<#assign xhtmlCompliant = true>
|
||||||
|
|
@ -438,8 +459,7 @@ compliant.
|
||||||
|
|
||||||
In similar fashion, you can specify HTML escaping per field, as the following example shows:
|
In similar fashion, you can specify HTML escaping per field, as the following example shows:
|
||||||
|
|
||||||
[source,jsp,indent=0]
|
[source,jsp,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<#-- until this point, default HTML escaping is used -->
|
<#-- until this point, default HTML escaping is used -->
|
||||||
|
|
||||||
|
|
@ -471,8 +491,8 @@ NOTE: The Groovy Markup Template engine requires Groovy 2.3.1+.
|
||||||
|
|
||||||
The following example shows how to configure the Groovy Markup Template Engine:
|
The following example shows how to configure the Groovy Markup Template Engine:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
|
@ -493,11 +513,29 @@ The following example shows how to configure the Groovy Markup Template Engine:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
class WebConfig : WebMvcConfigurer {
|
||||||
|
|
||||||
|
override fun configureViewResolvers(registry: ViewResolverRegistry) {
|
||||||
|
registry.groovy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the Groovy Markup Template Engine...
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun groovyMarkupConfigurer() = GroovyMarkupConfigurer().apply {
|
||||||
|
resourceLoaderPath = "/WEB-INF/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
The following example shows how to configure the same in XML:
|
The following example shows how to configure the same in XML:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<mvc:annotation-driven/>
|
<mvc:annotation-driven/>
|
||||||
|
|
||||||
|
|
@ -517,8 +555,7 @@ The following example shows how to configure the same in XML:
|
||||||
Unlike traditional template engines, Groovy Markup relies on a DSL that uses a builder
|
Unlike traditional template engines, Groovy Markup relies on a DSL that uses a builder
|
||||||
syntax. The following example shows a sample template for an HTML page:
|
syntax. The following example shows a sample template for an HTML page:
|
||||||
|
|
||||||
[source,groovy,indent=0]
|
[source,groovy,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
yieldUnescaped '<!DOCTYPE html>'
|
yieldUnescaped '<!DOCTYPE html>'
|
||||||
html(lang:'en') {
|
html(lang:'en') {
|
||||||
|
|
@ -589,8 +626,8 @@ You can declare a `ScriptTemplateConfigurer` bean to specify the script engine t
|
||||||
the script files to load, what function to call to render templates, and so on.
|
the script files to load, what function to call to render templates, and so on.
|
||||||
The following example uses Mustache templates and the Nashorn JavaScript engine:
|
The following example uses Mustache templates and the Nashorn JavaScript engine:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
|
@ -612,11 +649,30 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
class WebConfig : WebMvcConfigurer {
|
||||||
|
|
||||||
|
override fun configureViewResolvers(registry: ViewResolverRegistry) {
|
||||||
|
registry.scriptTemplate()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun configurer() = ScriptTemplateConfigurer().apply {
|
||||||
|
engineName = "nashorn"
|
||||||
|
setScripts("mustache.js")
|
||||||
|
renderObject = "Mustache"
|
||||||
|
renderFunction = "render"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
The following example shows the same arrangement in XML:
|
The following example shows the same arrangement in XML:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<mvc:annotation-driven/>
|
<mvc:annotation-driven/>
|
||||||
|
|
||||||
|
|
@ -631,25 +687,38 @@ The following example shows the same arrangement in XML:
|
||||||
|
|
||||||
The controller would look no different for the Java and XML configurations, as the following example shows:
|
The controller would look no different for the Java and XML configurations, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@Controller
|
@Controller
|
||||||
public class SampleController {
|
public class SampleController {
|
||||||
|
|
||||||
@GetMapping("/sample")
|
@GetMapping("/sample")
|
||||||
public String test(Model model) {
|
public String test(Model model) {
|
||||||
model.addObject("title", "Sample title");
|
model.addAttribute("title", "Sample title");
|
||||||
model.addObject("body", "Sample body");
|
model.addAttribute("body", "Sample body");
|
||||||
return "template";
|
return "template";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@Controller
|
||||||
|
class SampleController {
|
||||||
|
|
||||||
|
@GetMapping("/sample")
|
||||||
|
fun test(model: Model): String {
|
||||||
|
model["title"] = "Sample title"
|
||||||
|
model["body"] = "Sample body"
|
||||||
|
return "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
The following example shows the Mustache template:
|
The following example shows the Mustache template:
|
||||||
|
|
||||||
[source,html,indent=0]
|
[source,html,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -680,8 +749,8 @@ browser facilities that are not available in the server-side script engine.
|
||||||
|
|
||||||
The following example shows how to do so:
|
The following example shows how to do so:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
|
@ -703,6 +772,26 @@ The following example shows how to do so:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
class WebConfig : WebMvcConfigurer {
|
||||||
|
|
||||||
|
override fun configureViewResolvers(registry: ViewResolverRegistry) {
|
||||||
|
registry.scriptTemplate()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun configurer() = ScriptTemplateConfigurer().apply {
|
||||||
|
engineName = "nashorn"
|
||||||
|
setScripts("polyfill.js", "handlebars.js", "render.js")
|
||||||
|
renderFunction = "render"
|
||||||
|
isSharedEngine = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
NOTE: Setting the `sharedEngine` property to `false` is required when you use non-thread-safe
|
NOTE: Setting the `sharedEngine` property to `false` is required when you use non-thread-safe
|
||||||
script engines with templating libraries not designed for concurrency, such as Handlebars or
|
script engines with templating libraries not designed for concurrency, such as Handlebars or
|
||||||
|
|
@ -711,8 +800,7 @@ to https://bugs.openjdk.java.net/browse/JDK-8076099[this bug].
|
||||||
|
|
||||||
`polyfill.js` defines only the `window` object needed by Handlebars to run properly, as follows:
|
`polyfill.js` defines only the `window` object needed by Handlebars to run properly, as follows:
|
||||||
|
|
||||||
[source,javascript,indent=0]
|
[source,javascript,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
var window = {};
|
var window = {};
|
||||||
----
|
----
|
||||||
|
|
@ -722,8 +810,7 @@ implementation should also store any reused cached templates or pre-compiled tem
|
||||||
You can do so on the script side (and handle any customization you need -- managing
|
You can do so on the script side (and handle any customization you need -- managing
|
||||||
template engine configuration, for example). The following example shows how to do so:
|
template engine configuration, for example). The following example shows how to do so:
|
||||||
|
|
||||||
[source,javascript,indent=0]
|
[source,javascript,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
function render(template, model) {
|
function render(template, model) {
|
||||||
var compiledTemplate = Handlebars.compile(template);
|
var compiledTemplate = Handlebars.compile(template);
|
||||||
|
|
@ -756,8 +843,7 @@ When developing with JSPs, you can declare a `InternalResourceViewResolver` or a
|
||||||
mapped to a class and a URL. With a `ResourceBundleViewResolver`, you can mix
|
mapped to a class and a URL. With a `ResourceBundleViewResolver`, you can mix
|
||||||
different types of views by using only one resolver, as the following example shows:
|
different types of views by using only one resolver, as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<!-- the ResourceBundleViewResolver -->
|
<!-- the ResourceBundleViewResolver -->
|
||||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
|
||||||
|
|
@ -776,8 +862,7 @@ different types of views by using only one resolver, as the following example sh
|
||||||
encourage placing your JSP files in a directory under the `'WEB-INF'` directory so there
|
encourage placing your JSP files in a directory under the `'WEB-INF'` directory so there
|
||||||
can be no direct access by clients.
|
can be no direct access by clients.
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
||||||
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
|
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
|
||||||
|
|
@ -858,8 +943,7 @@ such as `firstName` and `lastName`. We can use it as the form-backing object of
|
||||||
form controller, which returns `form.jsp`. The following example shows what `form.jsp` could
|
form controller, which returns `form.jsp`. The following example shows what `form.jsp` could
|
||||||
look like:
|
look like:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form>
|
<form:form>
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -886,8 +970,7 @@ how inner tags are used with the `form` tag.
|
||||||
|
|
||||||
The following listing shows the generated HTML, which looks like a standard form:
|
The following listing shows the generated HTML, which looks like a standard form:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -913,8 +996,7 @@ The preceding JSP assumes that the variable name of the form-backing object is
|
||||||
(definitely a best practice), you can bind the form to the named variable, as the
|
(definitely a best practice), you can bind the form to the named variable, as the
|
||||||
following example shows:
|
following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form modelAttribute="user">
|
<form:form modelAttribute="user">
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -952,8 +1034,8 @@ This tag renders an HTML `input` tag with the `type` set to `checkbox`.
|
||||||
Assume that our `User` has preferences such as newsletter subscription and a list of
|
Assume that our `User` has preferences such as newsletter subscription and a list of
|
||||||
hobbies. The following example shows the `Preferences` class:
|
hobbies. The following example shows the `Preferences` class:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
public class Preferences {
|
public class Preferences {
|
||||||
|
|
||||||
|
|
@ -986,11 +1068,19 @@ hobbies. The following example shows the `Preferences` class:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
class Preferences(
|
||||||
|
var receiveNewsletter: Boolean,
|
||||||
|
var interests: StringArray,
|
||||||
|
var favouriteWord: String
|
||||||
|
)
|
||||||
|
----
|
||||||
|
|
||||||
The corresponding `form.jsp` could then resemble the following:
|
The corresponding `form.jsp` could then resemble the following:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form>
|
<form:form>
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -1035,8 +1125,7 @@ There are three approaches to the `checkbox` tag, which should meet all your che
|
||||||
Note that, regardless of the approach, the same HTML structure is generated. The following
|
Note that, regardless of the approach, the same HTML structure is generated. The following
|
||||||
HTML snippet defines some checkboxes:
|
HTML snippet defines some checkboxes:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Interests:</td>
|
<td>Interests:</td>
|
||||||
|
|
@ -1074,8 +1163,7 @@ the available options in the `items` property. Typically, the bound property is
|
||||||
collection so that it can hold multiple values selected by the user. The following example
|
collection so that it can hold multiple values selected by the user. The following example
|
||||||
shows a JSP that uses this tag:
|
shows a JSP that uses this tag:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form>
|
<form:form>
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -1106,8 +1194,7 @@ This tag renders an HTML `input` element with the `type` set to `radio`.
|
||||||
A typical usage pattern involves multiple tag instances bound to the same property
|
A typical usage pattern involves multiple tag instances bound to the same property
|
||||||
but with different values, as the following example shows:
|
but with different values, as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Sex:</td>
|
<td>Sex:</td>
|
||||||
|
|
@ -1132,8 +1219,7 @@ used as the value and the map entry's value are used as the label to be displaye
|
||||||
You can also use a custom object where you can provide the property names for the value
|
You can also use a custom object where you can provide the property names for the value
|
||||||
by using `itemValue` and the label by using `itemLabel`, as the following example shows:
|
by using `itemValue` and the label by using `itemLabel`, as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Sex:</td>
|
<td>Sex:</td>
|
||||||
|
|
@ -1147,8 +1233,7 @@ by using `itemValue` and the label by using `itemLabel`, as the following exampl
|
||||||
|
|
||||||
This tag renders an HTML `input` tag with the type set to `password` with the bound value.
|
This tag renders an HTML `input` tag with the type set to `password` with the bound value.
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Password:</td>
|
<td>Password:</td>
|
||||||
|
|
@ -1162,8 +1247,7 @@ Note that, by default, the password value is not shown. If you do want the
|
||||||
password value to be shown, you can set the value of the `showPassword` attribute to
|
password value to be shown, you can set the value of the `showPassword` attribute to
|
||||||
`true`, as the following example shows:
|
`true`, as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Password:</td>
|
<td>Password:</td>
|
||||||
|
|
@ -1182,8 +1266,7 @@ option as well as the use of nested `option` and `options` tags.
|
||||||
|
|
||||||
Assume that a `User` has a list of skills. The corresponding HTML could be as follows:
|
Assume that a `User` has a list of skills. The corresponding HTML could be as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Skills:</td>
|
<td>Skills:</td>
|
||||||
|
|
@ -1194,8 +1277,7 @@ Assume that a `User` has a list of skills. The corresponding HTML could be as fo
|
||||||
If the `User's` skill are in Herbology, the HTML source of the 'Skills' row could be
|
If the `User's` skill are in Herbology, the HTML source of the 'Skills' row could be
|
||||||
as follows:
|
as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Skills:</td>
|
<td>Skills:</td>
|
||||||
|
|
@ -1216,8 +1298,7 @@ as follows:
|
||||||
This tag renders an HTML `option` element. It sets `selected`, based on the bound
|
This tag renders an HTML `option` element. It sets `selected`, based on the bound
|
||||||
value. The following HTML shows typical output for it:
|
value. The following HTML shows typical output for it:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>House:</td>
|
<td>House:</td>
|
||||||
|
|
@ -1235,8 +1316,7 @@ value. The following HTML shows typical output for it:
|
||||||
If the `User's` house was in Gryffindor, the HTML source of the 'House' row would be
|
If the `User's` house was in Gryffindor, the HTML source of the 'House' row would be
|
||||||
as follows:
|
as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>House:</td>
|
<td>House:</td>
|
||||||
|
|
@ -1259,8 +1339,7 @@ as follows:
|
||||||
This tag renders a list of HTML `option` elements. It sets the `selected` attribute,
|
This tag renders a list of HTML `option` elements. It sets the `selected` attribute,
|
||||||
based on the bound value. The following HTML shows typical output for it:
|
based on the bound value. The following HTML shows typical output for it:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Country:</td>
|
<td>Country:</td>
|
||||||
|
|
@ -1275,8 +1354,7 @@ based on the bound value. The following HTML shows typical output for it:
|
||||||
|
|
||||||
If the `User` lived in the UK, the HTML source of the 'Country' row would be as follows:
|
If the `User` lived in the UK, the HTML source of the 'Country' row would be as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Country:</td>
|
<td>Country:</td>
|
||||||
|
|
@ -1311,8 +1389,7 @@ the item label property applies to the map value.
|
||||||
|
|
||||||
This tag renders an HTML `textarea` element. The following HTML shows typical output for it:
|
This tag renders an HTML `textarea` element. The following HTML shows typical output for it:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<tr>
|
<tr>
|
||||||
<td>Notes:</td>
|
<td>Notes:</td>
|
||||||
|
|
@ -1329,16 +1406,14 @@ This tag renders an HTML `input` tag with the `type` set to `hidden` with the bo
|
||||||
an unbound hidden value, use the HTML `input` tag with the `type` set to `hidden`.
|
an unbound hidden value, use the HTML `input` tag with the `type` set to `hidden`.
|
||||||
The following HTML shows typical output for it:
|
The following HTML shows typical output for it:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:hidden path="house"/>
|
<form:hidden path="house"/>
|
||||||
----
|
----
|
||||||
|
|
||||||
If we choose to submit the `house` value as a hidden one, the HTML would be as follows:
|
If we choose to submit the `house` value as a hidden one, the HTML would be as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<input name="house" type="hidden" value="Gryffindor"/>
|
<input name="house" type="hidden" value="Gryffindor"/>
|
||||||
|
|
||||||
|
|
@ -1356,8 +1431,8 @@ Assume that we want to display all error messages for the `firstName` and `lastN
|
||||||
fields once we submit the form. We have a validator for instances of the `User` class
|
fields once we submit the form. We have a validator for instances of the `User` class
|
||||||
called `UserValidator`, as the following example shows:
|
called `UserValidator`, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
public class UserValidator implements Validator {
|
public class UserValidator implements Validator {
|
||||||
|
|
||||||
|
|
@ -1371,11 +1446,25 @@ called `UserValidator`, as the following example shows:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
class UserValidator : Validator {
|
||||||
|
|
||||||
|
override fun supports(candidate: Class<*>): Boolean {
|
||||||
|
return User::class.java.isAssignableFrom(candidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validate(obj: Any, errors: Errors) {
|
||||||
|
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.")
|
||||||
|
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
The `form.jsp` could be as follows:
|
The `form.jsp` could be as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form>
|
<form:form>
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -1404,8 +1493,7 @@ The `form.jsp` could be as follows:
|
||||||
If we submit a form with empty values in the `firstName` and `lastName` fields,
|
If we submit a form with empty values in the `firstName` and `lastName` fields,
|
||||||
the HTML would be as follows:
|
the HTML would be as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -1441,8 +1529,7 @@ shows that the `errors` tag also supports some basic wildcarding functionality.
|
||||||
The following example displays a list of errors at the top of the page, followed by
|
The following example displays a list of errors at the top of the page, followed by
|
||||||
field-specific errors next to the fields:
|
field-specific errors next to the fields:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form>
|
<form:form>
|
||||||
<form:errors path="*" cssClass="errorBox"/>
|
<form:errors path="*" cssClass="errorBox"/>
|
||||||
|
|
@ -1468,8 +1555,7 @@ field-specific errors next to the fields:
|
||||||
|
|
||||||
The HTML would be as follows:
|
The HTML would be as follows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
|
<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
|
||||||
|
|
@ -1522,8 +1608,7 @@ request.
|
||||||
To support HTTP method conversion, the Spring MVC form tag was updated to support setting
|
To support HTTP method conversion, the Spring MVC form tag was updated to support setting
|
||||||
the HTTP method. For example, the following snippet comes from the Pet Clinic sample:
|
the HTTP method. For example, the following snippet comes from the Pet Clinic sample:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<form:form method="delete">
|
<form:form method="delete">
|
||||||
<p class="submit"><input type="submit" value="Delete Pet"/></p>
|
<p class="submit"><input type="submit" value="Delete Pet"/></p>
|
||||||
|
|
@ -1534,8 +1619,7 @@ The preceding example performs an HTTP POST, with the "`real`" DELETE method hid
|
||||||
a request parameter. It is picked up by the `HiddenHttpMethodFilter`, which is defined in
|
a request parameter. It is picked up by the `HiddenHttpMethodFilter`, which is defined in
|
||||||
web.xml, as the following example shows:
|
web.xml, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>httpMethodFilter</filter-name>
|
<filter-name>httpMethodFilter</filter-name>
|
||||||
|
|
@ -1550,8 +1634,8 @@ web.xml, as the following example shows:
|
||||||
|
|
||||||
The following example shows the corresponding `@Controller` method:
|
The following example shows the corresponding `@Controller` method:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@RequestMapping(method = RequestMethod.DELETE)
|
@RequestMapping(method = RequestMethod.DELETE)
|
||||||
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
|
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
|
||||||
|
|
@ -1559,7 +1643,15 @@ The following example shows the corresponding `@Controller` method:
|
||||||
return "redirect:/owners/" + ownerId;
|
return "redirect:/owners/" + ownerId;
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@RequestMapping(method = [RequestMethod.DELETE])
|
||||||
|
fun deletePet(@PathVariable ownerId: Int, @PathVariable petId: Int): String {
|
||||||
|
clinic.deletePet(petId)
|
||||||
|
return "redirect:/owners/$ownerId"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
[[mvc-view-jsp-formtaglib-html5]]
|
[[mvc-view-jsp-formtaglib-html5]]
|
||||||
==== HTML5 Tags
|
==== HTML5 Tags
|
||||||
|
|
@ -1603,8 +1695,7 @@ To be able to use Tiles, you have to configure it by using files that contain de
|
||||||
https://tiles.apache.org[]). In Spring, this is done by using the `TilesConfigurer`.
|
https://tiles.apache.org[]). In Spring, this is done by using the `TilesConfigurer`.
|
||||||
The following example `ApplicationContext` configuration shows how to do so:
|
The following example `ApplicationContext` configuration shows how to do so:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
||||||
<property name="definitions">
|
<property name="definitions">
|
||||||
|
|
@ -1630,8 +1721,7 @@ implementations, the `UrlBasedViewResolver` and the `ResourceBundleViewResolver`
|
||||||
You can specify locale-specific Tiles definitions by adding an underscore and then
|
You can specify locale-specific Tiles definitions by adding an underscore and then
|
||||||
the locale, as the following example shows:
|
the locale, as the following example shows:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
||||||
<property name="definitions">
|
<property name="definitions">
|
||||||
|
|
@ -1657,8 +1747,7 @@ them otherwise in the file names for Tiles definitions.
|
||||||
The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to
|
The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to
|
||||||
resolve. The following bean defines a `UrlBasedViewResolver`:
|
resolve. The following bean defines a `UrlBasedViewResolver`:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
|
||||||
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
|
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
|
||||||
|
|
@ -1674,16 +1763,14 @@ view names and view classes that the resolver can use. The following example sho
|
||||||
definition for a `ResourceBundleViewResolver` and the corresponding view names and view
|
definition for a `ResourceBundleViewResolver` and the corresponding view names and view
|
||||||
classes (taken from the Pet Clinic sample):
|
classes (taken from the Pet Clinic sample):
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
|
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
|
||||||
<property name="basename" value="views"/>
|
<property name="basename" value="views"/>
|
||||||
</bean>
|
</bean>
|
||||||
----
|
----
|
||||||
|
|
||||||
[source,java,indent=0]
|
[literal,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
...
|
...
|
||||||
welcomeView.(class)=org.springframework.web.servlet.view.tiles3.TilesView
|
welcomeView.(class)=org.springframework.web.servlet.view.tiles3.TilesView
|
||||||
|
|
@ -1725,8 +1812,7 @@ configuration, scoped beans, and so on. Note that you need to define one Spring
|
||||||
for each preparer name (as used in your Tiles definitions). The following example shows
|
for each preparer name (as used in your Tiles definitions). The following example shows
|
||||||
how to define a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean:
|
how to define a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
||||||
<property name="definitions">
|
<property name="definitions">
|
||||||
|
|
@ -1761,8 +1847,8 @@ package `org.springframework.web.servlet.view.feed`.
|
||||||
optionally override the `buildFeedMetadata()` method (the default implementation is
|
optionally override the `buildFeedMetadata()` method (the default implementation is
|
||||||
empty). The following example shows how to do so:
|
empty). The following example shows how to do so:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
public class SampleContentAtomView extends AbstractAtomFeedView {
|
public class SampleContentAtomView extends AbstractAtomFeedView {
|
||||||
|
|
||||||
|
|
@ -1777,14 +1863,29 @@ empty). The following example shows how to do so:
|
||||||
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
// implementation omitted
|
// implementation omitted
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
class SampleContentAtomView : AbstractAtomFeedView() {
|
||||||
|
|
||||||
|
override fun buildFeedMetadata(model: Map<String, Any>,
|
||||||
|
feed: Feed, request: HttpServletRequest) {
|
||||||
|
// implementation omitted
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildFeedEntries(model: Map<String, Any>,
|
||||||
|
request: HttpServletRequest, response: HttpServletResponse): List<Entry> {
|
||||||
|
// implementation omitted
|
||||||
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Similar requirements apply for implementing `AbstractRssFeedView`, as the following example shows:
|
Similar requirements apply for implementing `AbstractRssFeedView`, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
public class SampleContentRssView extends AbstractRssFeedView {
|
public class SampleContentRssView extends AbstractRssFeedView {
|
||||||
|
|
||||||
|
|
@ -1801,6 +1902,24 @@ Similar requirements apply for implementing `AbstractRssFeedView`, as the follow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
class SampleContentRssView : AbstractRssFeedView() {
|
||||||
|
|
||||||
|
override fun buildFeedMetadata(model: Map<String, Any>,
|
||||||
|
feed: Channel, request: HttpServletRequest) {
|
||||||
|
// implementation omitted
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildFeedItems(model: Map<String, Any>,
|
||||||
|
request: HttpServletRequest, response: HttpServletResponse): List<Item> {
|
||||||
|
// implementation omitted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The `buildFeedItems()` and `buildFeedEntries()` methods pass in the HTTP request, in case
|
The `buildFeedItems()` and `buildFeedEntries()` methods pass in the HTTP request, in case
|
||||||
you need to access the Locale. The HTTP response is passed in only for the setting of
|
you need to access the Locale. The HTTP response is passed in only for the setting of
|
||||||
|
|
@ -1847,8 +1966,8 @@ A simple PDF view for a word list could extend
|
||||||
`org.springframework.web.servlet.view.document.AbstractPdfView` and implement the
|
`org.springframework.web.servlet.view.document.AbstractPdfView` and implement the
|
||||||
`buildPdfDocument()` method, as the following example shows:
|
`buildPdfDocument()` method, as the following example shows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
public class PdfWordList extends AbstractPdfView {
|
public class PdfWordList extends AbstractPdfView {
|
||||||
|
|
||||||
|
|
@ -1862,6 +1981,21 @@ A simple PDF view for a word list could extend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
class PdfWordList : AbstractPdfView() {
|
||||||
|
|
||||||
|
override fun buildPdfDocument(model: Map<String, Any>, doc: Document, writer: PdfWriter,
|
||||||
|
request: HttpServletRequest, response: HttpServletResponse) {
|
||||||
|
|
||||||
|
val words = model["wordList"] as List<String>
|
||||||
|
for (word in words) {
|
||||||
|
doc.add(Paragraph(word))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
A controller can return such a view either from an external view definition
|
A controller can return such a view either from an external view definition
|
||||||
(referencing it by name) or as a `View` instance from the handler method.
|
(referencing it by name) or as a `View` instance from the handler method.
|
||||||
|
|
@ -1965,24 +2099,38 @@ Configuration is standard for a simple Spring web application: The MVC configura
|
||||||
has to define an `XsltViewResolver` bean and regular MVC annotation configuration.
|
has to define an `XsltViewResolver` bean and regular MVC annotation configuration.
|
||||||
The following example shows how to do so:
|
The following example shows how to do so:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
@ComponentScan
|
@ComponentScan
|
||||||
@Configuration
|
@Configuration
|
||||||
public class WebConfig implements WebMvcConfigurer {
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public XsltViewResolver xsltViewResolver() {
|
public XsltViewResolver xsltViewResolver() {
|
||||||
XsltViewResolver viewResolver = new XsltViewResolver();
|
XsltViewResolver viewResolver = new XsltViewResolver();
|
||||||
viewResolver.setPrefix("/WEB-INF/xsl/");
|
viewResolver.setPrefix("/WEB-INF/xsl/");
|
||||||
viewResolver.setSuffix(".xslt");
|
viewResolver.setSuffix(".xslt");
|
||||||
return viewResolver;
|
return viewResolver;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@EnableWebMvc
|
||||||
|
@ComponentScan
|
||||||
|
@Configuration
|
||||||
|
class WebConfig : WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun xsltViewResolver() = XsltViewResolver().apply {
|
||||||
|
setPrefix("/WEB-INF/xsl/")
|
||||||
|
setSuffix(".xslt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
[[mvc-view-xslt-controllercode]]
|
[[mvc-view-xslt-controllercode]]
|
||||||
|
|
@ -1993,8 +2141,8 @@ We also need a Controller that encapsulates our word-generation logic.
|
||||||
The controller logic is encapsulated in a `@Controller` class, with the
|
The controller logic is encapsulated in a `@Controller` class, with the
|
||||||
handler method being defined as follows:
|
handler method being defined as follows:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
[subs="verbatim,quotes"]
|
.Java
|
||||||
----
|
----
|
||||||
@Controller
|
@Controller
|
||||||
public class XsltController {
|
public class XsltController {
|
||||||
|
|
@ -2017,6 +2165,32 @@ handler method being defined as follows:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
import org.springframework.ui.set
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class XsltController {
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
fun home(model: Model): String {
|
||||||
|
val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
|
||||||
|
val root = document.createElement("wordList")
|
||||||
|
|
||||||
|
val words = listOf("Hello", "Spring", "Framework")
|
||||||
|
for (word in words) {
|
||||||
|
val wordNode = document.createElement("word")
|
||||||
|
val textNode = document.createTextNode(word)
|
||||||
|
wordNode.appendChild(textNode)
|
||||||
|
root.appendChild(wordNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
model["wordList"] = root
|
||||||
|
return "home"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
So far, we have only created a DOM document and added it to the Model map. Note that you
|
So far, we have only created a DOM document and added it to the Model map. Note that you
|
||||||
can also load an XML file as a `Resource` and use it instead of a custom DOM document.
|
can also load an XML file as a `Resource` and use it instead of a custom DOM document.
|
||||||
|
|
@ -2039,8 +2213,7 @@ and end with an `xslt` file extension.
|
||||||
|
|
||||||
The following example shows an XSLT transform:
|
The following example shows an XSLT transform:
|
||||||
|
|
||||||
[source,xml,indent=0]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||||
|
|
@ -2068,8 +2241,7 @@ The following example shows an XSLT transform:
|
||||||
|
|
||||||
The preceding transform is rendered as the following HTML:
|
The preceding transform is rendered as the following HTML:
|
||||||
|
|
||||||
[source,html,indent=0]
|
[source,html,indent=0,subs="verbatim,quotes"]
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
----
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue