Add Kotlin code snippets to WebMvc refdoc

See gh-21778
This commit is contained in:
Sebastien Deleuze 2019-08-22 16:49:12 +02:00
parent 7cfae94d70
commit 0d3fb0ee0f
4 changed files with 2098 additions and 558 deletions

View File

@ -4,8 +4,8 @@
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}") // <1>
@ -21,12 +21,28 @@
<4> Build a `UriComponents`.
<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`,
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
@ -35,28 +51,54 @@ as the following example shows:
.buildAndExpand("Westin", "123")
.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),
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VARIABLES);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
RestTemplate restTemplate = new RestTemplate();
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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VARIABLES);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
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
`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
that holds configuration and preferences, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
@ -117,6 +182,16 @@ that holds configuration and preferences, as the following example shows:
.queryParam("q", "{q}")
.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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.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"
----
[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),
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.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}")
.build("New York", "foo+bar")
----
You can shorten it further still with a full URI template, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.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")
----
@ -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.
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String baseUrl = "https://example.com";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
@ -194,6 +293,22 @@ as the following example shows:
// Customize the WebClient..
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
expand and encode URI templates. As a factory, it provides a single place to configure

View File

@ -83,12 +83,12 @@ The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation enables cross-origin requests on annotated controller methods,
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RestController
@RequestMapping("/account")
public class AccountController {
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
@ -100,7 +100,26 @@ public class AccountController {
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:
@ -118,8 +137,8 @@ should only be used where appropriate.
`@CrossOrigin` is supported at the class level, too, and is inherited by all methods,
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@ -137,17 +156,35 @@ 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,
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("https://domain2.com")
@GetMapping("/{id}")
@ -159,7 +196,27 @@ public class AccountController {
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,12 +253,12 @@ should only be used where appropriate.
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
@ -215,7 +272,27 @@ public class WebConfig implements WebMvcConfigurer {
// 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,
as the following example shows:
[source,xml,indent=0]
[subs="verbatim"]
[source,xml,indent=0,subs="verbatim"]
----
<mvc:cors>
@ -262,21 +338,39 @@ for CORS.
To configure the filter, pass a
`CorsConfigurationSource` to its constructor, as the following example shows:
[source,java,indent=0]
[subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
CorsConfiguration config = new CorsConfiguration();
CorsConfiguration config = new CorsConfiguration();
// Possibly...
// config.applyPermitDefaultValues()
// Possibly...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
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)
----

View File

@ -46,8 +46,8 @@ integration for using Spring MVC with FreeMarker templates.
The following example shows how to configure FreeMarker as a view technology:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@ -55,7 +55,7 @@ The following example shows how to configure FreeMarker as a view technology:
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freemarker();
registry.freeMarker();
}
// Configure FreeMarker...
@ -68,11 +68,29 @@ The following example shows how to configure FreeMarker as a view technology:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
}
// Configure FreeMarker...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("/WEB-INF/freemarker")
}
}
----
The following example shows how to configure the same in XML:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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
properties, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<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
`java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<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
following example shows a `personForm` view:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- FreeMarker macros have to be imported into a namespace.
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`
and `showWErrors` macros:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>
@ -322,8 +336,7 @@ occurs through Spring's Validation framework.
The generated HTML resembles the following example:
[source,jsp,indent=0]
[subs="verbatim,quotes"]
[source,jsp,indent=0,subs="verbatim,quotes"]
----
Name:
<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
model under the name 'cityMap'. The following listing shows the example:
[source,jsp,indent=0]
[subs="verbatim,quotes"]
[source,jsp,indent=0,subs="verbatim,quotes"]
----
...
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
and a default value in the form backing object, the HTML resembles the following:
[source,jsp,indent=0]
[subs="verbatim,quotes"]
[source,jsp,indent=0,subs="verbatim,quotes"]
----
Town:
<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
codes with suitable keys, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
protected Map<String, String> referenceData(HttpServletRequest request) throws Exception {
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
Map<String, String> cityMap = new LinkedHashMap<>();
cityMap.put("LDN", "London");
cityMap.put("PRS", "Paris");
cityMap.put("NYC", "New York");
Map<String, String> model = new HashMap<>();
Map<String, Object> model = new HashMap<>();
model.put("cityMap", cityMap);
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
user still sees the more user-friendly city names, as follows:
[source,jsp,indent=0]
[subs="verbatim,quotes"]
[source,jsp,indent=0,subs="verbatim,quotes"]
----
Town:
<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
model or context variable named `xhtmlCompliant`, as the following example shows:
[source,jsp,indent=0]
[subs="verbatim,quotes"]
[source,jsp,indent=0,subs="verbatim,quotes"]
----
<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>
@ -438,8 +459,7 @@ compliant.
In similar fashion, you can specify HTML escaping per field, as the following example shows:
[source,jsp,indent=0]
[subs="verbatim,quotes"]
[source,jsp,indent=0,subs="verbatim,quotes"]
----
<#-- 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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
syntax. The following example shows a sample template for an HTML page:
[source,groovy,indent=0]
[subs="verbatim,quotes"]
[source,groovy,indent=0,subs="verbatim,quotes"]
----
yieldUnescaped '<!DOCTYPE html>'
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 following example uses Mustache templates and the Nashorn JavaScript engine:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class SampleController {
@GetMapping("/sample")
public String test(Model model) {
model.addObject("title", "Sample title");
model.addObject("body", "Sample body");
model.addAttribute("title", "Sample title");
model.addAttribute("body", "Sample body");
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:
[source,html,indent=0]
[subs="verbatim,quotes"]
[source,html,indent=0,subs="verbatim,quotes"]
----
<html>
<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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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
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:
[source,javascript,indent=0]
[subs="verbatim,quotes"]
[source,javascript,indent=0,subs="verbatim,quotes"]
----
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
template engine configuration, for example). The following example shows how to do so:
[source,javascript,indent=0]
[subs="verbatim,quotes"]
[source,javascript,indent=0,subs="verbatim,quotes"]
----
function render(template, model) {
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
different types of views by using only one resolver, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- the 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
can be no direct access by clients.
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<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
look like:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form>
<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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form method="POST">
<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
following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form modelAttribute="user">
<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
hobbies. The following example shows the `Preferences` class:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form>
<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
HTML snippet defines some checkboxes:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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
shows a JSP that uses this tag:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form>
<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
but with different values, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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
by using `itemValue` and the label by using `itemLabel`, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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.
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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
`true`, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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
as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<td>Skills:</td>
@ -1216,8 +1298,7 @@ as follows:
This tag renders an HTML `option` element. It sets `selected`, based on the bound
value. The following HTML shows typical output for it:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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
as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<td>House:</td>
@ -1259,8 +1339,7 @@ as follows:
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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tr>
<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`.
The following HTML shows typical output for it:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:hidden path="house"/>
----
If we choose to submit the `house` value as a hidden one, the HTML would be as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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
called `UserValidator`, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form>
<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,
the HTML would be as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form method="POST">
<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
field-specific errors next to the fields:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form>
<form:errors path="*" cssClass="errorBox"/>
@ -1468,8 +1555,7 @@ field-specific errors next to the fields:
The HTML would be as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form method="POST">
<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
the HTTP method. For example, the following snippet comes from the Pet Clinic sample:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<form:form method="delete">
<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
web.xml, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<filter>
<filter-name>httpMethodFilter</filter-name>
@ -1550,8 +1634,8 @@ web.xml, as the following example shows:
The following example shows the corresponding `@Controller` method:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RequestMapping(method = RequestMethod.DELETE)
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;
}
----
[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]]
==== 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`.
The following example `ApplicationContext` configuration shows how to do so:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<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
the locale, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<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
resolve. The following bean defines a `UrlBasedViewResolver`:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<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
classes (taken from the Pet Clinic sample):
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
</bean>
----
[source,java,indent=0]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
...
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
how to define a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
@ -1761,8 +1847,8 @@ package `org.springframework.web.servlet.view.feed`.
optionally override the `buildFeedMetadata()` method (the default implementation is
empty). The following example shows how to do so:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SampleContentAtomView extends AbstractAtomFeedView {
@ -1777,14 +1863,29 @@ empty). The following example shows how to do so:
HttpServletRequest request, HttpServletResponse response) throws Exception {
// 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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
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
`buildPdfDocument()` method, as the following example shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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
(referencing it by name) or as a `View` instance from the handler method.
@ -1965,13 +2099,13 @@ Configuration is standard for a simple Spring web application: The MVC configura
has to define an `XsltViewResolver` bean and regular MVC annotation configuration.
The following example shows how to do so:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EnableWebMvc
@ComponentScan
@Configuration
public class WebConfig implements WebMvcConfigurer {
@EnableWebMvc
@ComponentScan
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public XsltViewResolver xsltViewResolver() {
@ -1980,9 +2114,23 @@ public class WebConfig implements WebMvcConfigurer {
viewResolver.setSuffix(".xslt");
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]]
@ -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
handler method being defined as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
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
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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="utf-8"?>
<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:
[source,html,indent=0]
[subs="verbatim,quotes"]
[source,html,indent=0,subs="verbatim,quotes"]
----
<html>
<head>

File diff suppressed because it is too large Load Diff