From 01d234f9f30fea0b9dee559606bd7a73e3e09b05 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 7 Jun 2016 18:06:14 +0200 Subject: [PATCH] Use composed @RequestMapping variants in the reference manual Issue: SPR-13995 --- src/asciidoc/web-mvc.adoc | 135 +++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 9ab799bb38..a08426d3a7 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -785,7 +785,7 @@ to the value of a URI template variable: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET) + @GetMapping("/owners/{ownerId}") public String findOwner(**@PathVariable** String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); @@ -807,20 +807,21 @@ template variable by name. You can specify it in the annotation: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET) + @GetMapping("/owners/{ownerId}") public String findOwner(**@PathVariable("ownerId")** String theOwner, Model model) { // implementation omitted } ---- Or if the URI template variable name matches the method argument name you can omit that -detail. As long as your code is not compiled without debugging information, Spring MVC -will match the method argument name to the URI template variable name: +detail. As long as your code is compiled with debugging information or the `-parameters` +compiler flag on Java 8, Spring MVC will match the method argument name to the URI +template variable name: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path="/owners/{ownerId}", method=RequestMethod.GET) + @GetMapping("/owners/{ownerId}") public String findOwner(**@PathVariable** String ownerId, Model model) { // implementation omitted } @@ -832,7 +833,7 @@ A method can have any number of `@PathVariable` annotations: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) + @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); @@ -844,7 +845,7 @@ A method can have any number of `@PathVariable` annotations: When a `@PathVariable` annotation is used on a `Map` argument, the map is populated with all URI template variables. -A URI template can be assembled from type and path level __@RequestMapping__ +A URI template can be assembled from type and method level __@RequestMapping__ annotations. As a result the `findPet()` method can be invoked with a URL such as `/owners/42/pets/21`. @@ -863,7 +864,7 @@ annotations. As a result the `findPet()` method can be invoked with a URL such a } ---- -A `@PathVariable` argument can be of __any simple type__ such as int, long, Date, etc. +A `@PathVariable` argument can be of __any simple type__ such as `int`, `long`, `Date`, etc. Spring automatically converts to the appropriate type or throws a `TypeMismatchException` if it fails to do so. You can also register support for parsing additional data types. See <> and <>. @@ -876,24 +877,24 @@ Sometimes you need more precision in defining URI template variables. Consider t 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.For example: +name and the second - the regular expression. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") - public void handle(@PathVariable String version, @PathVariable String extension) { - // ... - } + public void handle(@PathVariable String version, @PathVariable String extension) { + // ... } ---- [[mvc-ann-requestmapping-patterns]] ==== Path Patterns -In addition to URI templates, the `@RequestMapping` annotation also supports Ant-style -path patterns (for example, `/myPath/{asterisk}.do`). A combination of URI template variables and -Ant-style globs is also supported (e.g. `/owners/{asterisk}/pets/{petId}`). +In addition to URI templates, the `@RequestMapping` annotation and all _composed_ +`@RequestMapping` variants also support Ant-style path patterns (for example, +`/myPath/{asterisk}.do`). A combination of URI template variables and Ant-style globs is +also supported (e.g. `/owners/{asterisk}/pets/{petId}`). [[mvc-ann-requestmapping-pattern-comparison]] @@ -923,7 +924,7 @@ can be customized (see <> in the section on configurin [[mvc-ann-requestmapping-placeholders]] ==== Path Patterns with Placeholders -Patterns in `@RequestMapping` annotations support ${...} placeholders against local +Patterns in `@RequestMapping` annotations support `${...}` placeholders against local properties and/or system properties and environment variables. This may be useful in cases where the path a controller is mapped to may need to be customized through configuration. For more information on placeholders, see the javadocs of the @@ -1030,7 +1031,7 @@ Below is an example of extracting the matrix variable "q": ---- // GET /pets/42;q=11;r=22 - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) + @GetMapping("/pets/{petId}") public void findPet(@PathVariable String petId, @MatrixVariable int q) { // petId == 42 @@ -1047,7 +1048,7 @@ specific to identify where the variable is expected to be: ---- // GET /owners/42;q=11/pets/21;q=22 - @RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) + @GetMapping("/owners/{ownerId}/pets/{petId}") public void findPet( @MatrixVariable(name="q", pathVar="ownerId") int q1, @MatrixVariable(name="q", pathVar="petId") int q2) { @@ -1065,7 +1066,7 @@ A matrix variable may be defined as optional and a default value specified: ---- // GET /pets/42 - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET) + @GetMapping("/pets/{petId}") public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { // q == 1 @@ -1080,7 +1081,7 @@ All matrix variables may be obtained in a Map: ---- // GET /owners/42;q=11;r=12/pets/21;q=22;s=23 - @RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET) + @GetMapping("/owners/{ownerId}/pets/{petId}") public void findPet( @MatrixVariable MultiValueMap matrixVars, @MatrixVariable(pathVar="petId"") MultiValueMap petMatrixVars) { @@ -1131,21 +1132,20 @@ to `false`. [[mvc-ann-requestmapping-consumes]] ==== Consumable Media Types You can narrow the primary mapping by specifying a list of consumable media types. The -request will be matched only if the __Content-Type__ request header matches the specified +request will be matched only if the `Content-Type` request header matches the specified media type. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Controller - @RequestMapping(path = "/pets", method = RequestMethod.POST, **consumes="application/json"**) + @PostMapping(path = "/pets", **consumes = "application/json"**) public void addPet(@RequestBody Pet pet, Model model) { // implementation omitted } ---- -Consumable media type expressions can also be negated as in __!text/plain__ to match to -all requests other than those with __Content-Type__ of __text/plain__. Also consider +Consumable media type expressions can also be negated as in `!text/plain` to match to +all requests other than those with `Content-Type` of `text/plain`. Also consider using constants provided in `MediaType` such as `APPLICATION_JSON_VALUE` and `APPLICATION_JSON_UTF8_VALUE`. @@ -1161,7 +1161,7 @@ rather than extend type-level consumable types. [[mvc-ann-requestmapping-produces]] ==== Producible Media Types You can narrow the primary mapping by specifying a list of producible media types. The -request will be matched only if the __Accept__ request header matches one of these +request will be matched only if the `Accept` request header matches one of these values. Furthermore, use of the __produces__ condition ensures the actual content type used to generate the response respects the media types specified in the __produces__ condition. For example: @@ -1169,8 +1169,7 @@ condition. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Controller - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, **produces = MediaType.APPLICATION_JSON_UTF8_VALUE**) + @GetMapping(path = "/pets/{petId}", **produces = MediaType.APPLICATION_JSON_UTF8_VALUE**) @ResponseBody public Pet getPet(@PathVariable String petId, Model model) { // implementation omitted @@ -1186,8 +1185,8 @@ the `UTF-8` charset. ==== Just like with __consumes__, producible media type expressions can be negated as in -__!text/plain__ to match to all requests other than those with an __Accept__ header -value of __text/plain__. Also consider using constants provided in `MediaType` such +`!text/plain` to match to all requests other than those with an `Accept` header +value of `text/plain`. Also consider using constants provided in `MediaType` such as `APPLICATION_JSON_VALUE` and `APPLICATION_JSON_UTF8_VALUE`. [TIP] @@ -1212,7 +1211,7 @@ example with a request parameter value condition: @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { - @RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, **params="myParam=myValue"**) + @GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**) public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } @@ -1230,7 +1229,7 @@ specific request header value: @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { - @RequestMapping(path = "/pets", method = RequestMethod.GET, **headers="myHeader=myValue"**) + @GetMapping(path = "/pets", **headers = "myHeader=myValue"**) public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } @@ -1253,15 +1252,17 @@ respectively instead. They are intended specifically for that purpose. `@RequestMapping` methods mapped to "GET" are also implicitly mapped to "HEAD", i.e. there is no need to have "HEAD" explicitly declared. An HTTP HEAD request -is processed as if it was an HTTP GET except instead of writing the body only +is processed as if it were an HTTP GET except instead of writing the body only the number of bytes are counted and the "Content-Length" header set. -`@RequestMapping` method have built-in support for HTTP OPTIONS. By default an +`@RequestMapping` methods have built-in support for HTTP OPTIONS. By default an HTTP OPTIONS request is handled by setting the "Allow" response header to the HTTP methods explicitly declared on all `@RequestMapping` methods with matching URL patterns. When no HTTP methods are explicitly declared the "Allow" header is set to "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS". Ideally always declare the -HTTP method(s) an `@RequestMapping` method is intended to handle. +HTTP method(s) that an `@RequestMapping` method is intended to handle, or alternatively +use one of the dedicated _composed_ `@RequestMapping` variants (see +<>). Although not necessary an `@RequestMapping` method can be mapped to and handle either HTTP HEAD or HTTP OPTIONS, or both. @@ -1272,9 +1273,9 @@ either HTTP HEAD or HTTP OPTIONS, or both. [[mvc-ann-methods]] === Defining @RequestMapping handler methods -An `@RequestMapping` handler method can have a very flexible signatures. The supported +`@RequestMapping` handler methods can have very flexible signatures. The supported method arguments and return values are described in the following section. Most -arguments can be used in arbitrary order with the only exception of `BindingResult` +arguments can be used in arbitrary order with the only exception being `BindingResult` arguments. This is described in the next section. [NOTE] @@ -1378,7 +1379,7 @@ sample won't work: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(method = RequestMethod.POST) + @PostMapping public String processSubmit(**@ModelAttribute("pet") Pet pet**, Model model, **BindingResult result**) { ... } ---- @@ -1388,7 +1389,7 @@ this working you have to reorder the parameters as follows: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(method = RequestMethod.POST) + @PostMapping public String processSubmit(**@ModelAttribute("pet") Pet pet**, **BindingResult result**, Model model) { ... } ---- @@ -1468,7 +1469,7 @@ The following code snippet shows the usage: // ... - @RequestMapping(method = RequestMethod.GET) + @GetMapping public String setupForm(**@RequestParam("petId") int petId**, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); @@ -1500,7 +1501,7 @@ be bound to the value of the HTTP request body. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/something", method = RequestMethod.PUT) + @PutMapping("/something") public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); } @@ -1572,14 +1573,14 @@ or the MVC Java config. [[mvc-ann-responsebody]] ==== Mapping the response body with the @ResponseBody annotation -The `@ResponseBody` annotation is similar to `@RequestBody`. This annotation can be put +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). For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/something", method = RequestMethod.PUT) + @GetMapping("/something") @ResponseBody public String helloWorld() { return "Hello World"; @@ -1598,7 +1599,7 @@ section and <>. 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 +`@RequestMapping` methods with `@ResponseBody`, you can annotate your controller Class with `@RestController`. {api-spring-framework}/web/bind/annotation/RestController.html[`@RestController`] @@ -1707,8 +1708,8 @@ i.e., with or without an attribute name. The `@ModelAttribute` annotation can be used on `@RequestMapping` methods as well. In that case the return value of the `@RequestMapping` method is interpreted as a model -attribute rather than as a view name. The view name is derived from view name -conventions instead much like for methods returning void -- see <>. +attribute rather than as a view name. The view name is then derived based on view name +conventions instead, much like for methods returning `void` -- see <>. [[mvc-ann-modelattrib-method-args]] @@ -1727,7 +1728,7 @@ form field individually. [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) + @PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(**@ModelAttribute Pet pet**) { } ---- @@ -1749,9 +1750,9 @@ using an URI template variable and a type converter. Here is an example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/accounts/{account}", method = RequestMethod.PUT) + @PutMapping("/accounts/{account}") public String save(@ModelAttribute("account") Account account) { - + // ... } ---- @@ -1774,7 +1775,7 @@ following the `@ModelAttribute` argument: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) + @PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { if (result.hasErrors()) { @@ -1807,7 +1808,7 @@ public Account findAccount(@PathVariable String accountId) { return accountRepository.findOne(accountId); } -@RequestMapping(path="update", method=POST) +@PostMapping("update") public String update(@Valid AccountUpdateForm form, BindingResult result, **@ModelAttribute(binding=false)** Account account) { @@ -1823,7 +1824,7 @@ subsequently reported back to the user: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) + @PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { new PetValidator().validate(pet, result); @@ -1842,7 +1843,7 @@ annotation: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) + @PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) { if (result.hasErrors()) { @@ -2079,8 +2080,8 @@ binding directly within your controller class. `@InitBinder` identifies methods initialize the `WebDataBinder` that will be used to populate command and form object arguments of annotated handler methods. -Such init-binder methods support all arguments that `@RequestMapping` supports, except -for command/form objects and corresponding validation result objects. Init-binder +Such init-binder methods support all arguments that `@RequestMapping` methods support, +except for command/form objects and corresponding validation result objects. Init-binder methods must not have a return value. Thus, they are usually declared as `void`. Typical arguments include `WebDataBinder` in combination with `WebRequest` or `java.util.Locale`, allowing code to register context-specific editors. @@ -2209,7 +2210,7 @@ the view class or interface to be used: @RestController public class UserController { - @RequestMapping(path = "/user", method = RequestMethod.GET) + @GetMapping("/user") @JsonView(User.WithoutPasswordView.class) public User getUser() { return new User("eric", "7!jd#h23"); @@ -2261,7 +2262,7 @@ to the model: @Controller public class UserController extends AbstractController { - @RequestMapping(path = "/user", method = RequestMethod.GET) + @GetMapping("/user") public String getUser(Model model) { model.addAttribute("user", new User("eric", "7!jd#h23")); model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class); @@ -2309,7 +2310,7 @@ is an example of such a controller method: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(method=RequestMethod.POST) + @PostMapping public Callable processUpload(final MultipartFile file) { return new Callable() { @@ -3006,7 +3007,7 @@ through `Model` nor `RedirectAttributes`. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/files/{path}", method = RequestMethod.POST) + @PostMapping("/files/{path}") public String upload(...) { // ... return "redirect:files/{path}"; @@ -3178,7 +3179,7 @@ application/atom+xml is shown below. private List contentList = new ArrayList(); - @RequestMapping(path="/content", method=RequestMethod.GET) + @GetMapping("/content") public ModelAndView getContent() { ModelAndView mav = new ModelAndView(); mav.setViewName("content"); @@ -3326,7 +3327,7 @@ Spring MVC also provides a mechanism for building links to controller methods. F @RequestMapping("/hotels/{hotel}") public class BookingController { - @RequestMapping("/bookings/{booking}") + @GetMapping("/bookings/{booking}") public String getBooking(@PathVariable Long booking) { // ... @@ -3791,7 +3792,7 @@ use `MultipartHttpServletRequest` or `MultipartFile` in the method parameters: @Controller public class FileUploadController { - @RequestMapping(path = "/form", method = RequestMethod.POST) + @PostMapping("/form") public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { @@ -3820,7 +3821,7 @@ the method parameter: @Controller public class FileUploadController { - @RequestMapping(path = "/form", method = RequestMethod.POST) + @PostMapping("/form") public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") Part file) { @@ -3878,7 +3879,7 @@ multipart: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping(path = "/someUrl", method = RequestMethod.POST) + @PostMapping("/someUrl") public String onSubmit(**@RequestPart("meta-data") MetaData metadata, @RequestPart("file-data") MultipartFile file**) { @@ -3937,7 +3938,7 @@ may be more convenient to directly set the status of the response and optionally error content to the body of the response. You can do that with `@ExceptionHandler` methods. When declared within a controller such -methods apply to exceptions raised by `@RequestMapping` methods of that contoroller (or +methods apply to exceptions raised by `@RequestMapping` methods of that controller (or any of its sub-classes). You can also declare an `@ExceptionHandler` method within an `@ControllerAdvice` class in which case it handles exceptions from `@RequestMapping` methods from many controllers. Below is an example of a controller-local @@ -4487,14 +4488,14 @@ This involves calculating a lastModified `long` and/or an Etag value for a given comparing it against the `'If-Modified-Since'` request header value, and potentially returning a response with status code 304 (Not Modified). -As described in <>, Controllers can interact with the request/response using +As described in <>, controllers can interact with the request/response using `HttpEntity` types. Controllers returning `ResponseEntity` can include HTTP caching information in responses like this: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping("/book/{id}") + @GetMapping("/book/{id}") public ResponseEntity showBook(@PathVariable Long id) { Book book = findBook(id);