URI Template Patterns
URI templates can be used for convenient access to selected parts of a URL in a @RequestMapping
method.
A URI Template is a URI-like string, containing one or more variable names. When you substitute values for these variables, the template becomes a URI.
In Spring MVC you can use the @PathVariable
annotation on a method argument to bind it to the value of a URI template variable:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
//or
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// implementation omitted
}
//A method can have any number of @PathVariable annotations:
@GetMapping("/owners/{ownerId}/pets/{petId}")
public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
Pet pet = owner.getPet(petId);
model.addAttribute("pet", pet);
return "displayPet";
}
//or
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
URI Template Patterns with Regular Expressions
The @RequestMapping
annotation supports the use of regular expressions in URI template variables. The syntax is {varName:regex}
where the first part defines the variable name and the second - the regular expression.
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String extension) {
// ...
}
Path Pattern Comparison
When a URL matches multiple patterns, a sort is used to find the most specific match.
A pattern with a lower count of URI variables and wild cards is considered more specific. For example /hotels/{hotel}/*
has 1 URI variable and 1 wild card and is considered more specific than /hotels/{hotel}/**
which as 1 URI variable and 2 wild cards.
If two patterns have the same count, the one that is longer is considered more specific. For example /foo/bar*
is longer and considered more specific than/foo/*
.
When two patterns have the same count and length, the pattern with fewer wild cards is considered more specific. For example /hotels/{hotel}
is more specific than /hotels/*
.
There are also some additional special rules:
- The default mapping pattern
/**
is less specific than any other pattern. For example/api/{a}/{b}/{c}
is more specific. - A prefix pattern such as
/public/**
is less specific than any other pattern that doesn’t contain double wildcards. For example/public/path3/{a}/{b}/{c}
is more specific.
Matrix Variables
Matrix variables can appear in any path segment, each matrix variable separated with a ";" (semicolon). For example: "/cars;color=red;year=2012"
. Multiple values may be either "," (comma) separated "color=red,green,blue"
or the variable name may be repeated "color=red;color=green;color=blue"
.
If a URL is expected to contain matrix variables, the request mapping pattern must represent them with a URI template. This ensures the request can be matched correctly regardless of whether matrix variables are present or not and in what order they are provided.
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId"") MultiValueMap<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 11, "s" : 23]
}
Note that to enable the use of matrix variables, you must set the removeSemicolonContent
property of RequestMappingHandlerMapping
to false
. By default it is set to true
.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:annotation-driven enable-matrix-variables="true"/> </beans>
Supported method argument types
org.springframework.web.context.request.WebRequest request/session attribute access
java.util.Locale
for the current request locale
java.io.OutputStream
/ java.io.Writer
for generating the response’s content.
java.io.InputStream
/ java.io.Reader
for access to the request’s content.
java.util.Map
/ org.springframework.ui.Model
/ org.springframework.ui.ModelMap
@PathVariable access to URI template variables.
@MatrixVariable access to name-value pairs located in URI path segments.
@RequestParam access to specific Servlet request parameters.
@RequestPart access to the content of a "multipart/form-data" request part.
@SessionAttribute access to existing, permanent session attributes (e.g. user authentication object)
@RequestAttribute
access to request attributes.
java.util.Map
/ org.springframework.ui.Model
/ org.springframework.ui.ModelMap
The Errors
or BindingResult
parameters have to follow the model object that is being bound immediately as the method signature might have more than one model object and Spring will create a separate BindingResult
instance for each of them so the following sample won’t work:
Invalid ordering of BindingResult and @ModelAttribute.
@PostMapping public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { ... }
Note, that there is a Model
parameter in between Pet
and BindingResult
. To get this working you have to reorder the parameters as follows:
@PostMapping public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) { ... }
Supported method return types
A ModelAndView
object
A Model
object
A Map
object for exposing a model
A View
object
A String
value that is interpreted as the logical view name
void
if the method handles the response itself (by writing the response content directly, declaring an argument of type ServletResponse
/HttpServletResponse
for that purpose)
If the method is annotated with @ResponseBody
, the return type is written to the response HTTP body.
An HttpHeaders
object to return a response with no body.
Any other return type is considered to be a single model attribute to be exposed to the view, using the attribute name specified through @ModelAttribute
at the method level
Binding request parameters to method parameters with @RequestParam
Use the @RequestParam
annotation to bind request parameters to a method parameter in your controller.
@Controller @RequestMapping("/pets") @SessionAttributes("pet") public class EditPetForm { // ... @GetMapping public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ... }
Parameters using this annotation are required by default, but you can specify that a parameter is optional by setting @RequestParam
's required
attribute tofalse
(e.g., @RequestParam(name="id", required=false)
).
When an @RequestParam
annotation is used on a Map<String, String>
or MultiValueMap<String, String>
argument, the map is populated with all request parameters.
Mapping the request body with the @RequestBody annotation
The @RequestBody
method parameter annotation indicates that a method parameter should be bound to the value of the HTTP request body.
@PutMapping("/something") public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); }
Mapping the response body with the @ResponseBody annotation
The @ResponseBody
annotation is similar to @RequestBody
. This annotation can be placed on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).
@GetMapping("/something") @ResponseBody public String helloWorld() { return "Hello World"; }
Creating REST Controllers with the @RestController annotation
It’s a very common use case to have Controllers implement a REST API, thus serving only JSON, XML or custom MediaType content. For convenience, instead of annotating all your @RequestMapping
methods with @ResponseBody
, you can annotate your controller Class with @RestController
.
@RestController
is a stereotype annotation that combines @ResponseBody
and @Controller
. More than that, it gives more meaning to your Controller and also may carry additional semantics in future releases of the framework.
Using HttpEntity
The HttpEntity
is similar to @RequestBody
and @ResponseBody
. Besides getting access to the request and response body, HttpEntity
(and the response-specific subclass ResponseEntity
) also allows access to the request and response headers.
@RequestMapping("/something") public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException { String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader")); byte[] requestBody = requestEntity.getBody(); // do something with request header and body HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("MyResponseHeader", "MyValue"); return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED); }