[[mvc-ann-requestmapping]]
= Mapping Requests

[.small]#xref:web/webflux/controller/ann-requestmapping.adoc[See equivalent in the Reactive stack]#

This section discusses request mapping for annotated controllers.


[[mvc-ann-requestmapping-annotation]]
== `@RequestMapping`

[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-annotation[See equivalent in the Reactive stack]#

You can use the `@RequestMapping` annotation to map requests to controllers methods. It has
various attributes to match by URL, HTTP method, request parameters, headers, and media
types. You can use it at the class level to express shared mappings or at the method level
to narrow down to a specific endpoint mapping.

There are also HTTP method specific shortcut variants of `@RequestMapping`:

* `@GetMapping`
* `@PostMapping`
* `@PutMapping`
* `@DeleteMapping`
* `@PatchMapping`

The shortcuts are
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-composed[Custom Annotations]
that are provided because, arguably, most controller methods should be mapped to a specific
HTTP method versus using `@RequestMapping`, which, by default, matches to all HTTP methods.
A `@RequestMapping` is still needed at the class level to express shared mappings.

NOTE: `@RequestMapping` cannot be used in conjunction with other `@RequestMapping`
annotations that are declared on the same element (class, interface, or method). If
multiple `@RequestMapping` annotations are detected on the same element, a warning will
be logged, and only the first mapping will be used. This also applies to composed
`@RequestMapping` annotations such as `@GetMapping`, `@PostMapping`, etc.

The following example has type and method level mappings:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@RestController
	@RequestMapping("/persons")
	class PersonController {

		@GetMapping("/{id}")
		public Person getPerson(@PathVariable Long id) {
			// ...
		}

		@PostMapping
		@ResponseStatus(HttpStatus.CREATED)
		public void add(@RequestBody Person person) {
			// ...
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@RestController
	@RequestMapping("/persons")
	class PersonController {

		@GetMapping("/{id}")
		fun getPerson(@PathVariable id: Long): Person {
			// ...
		}

		@PostMapping
		@ResponseStatus(HttpStatus.CREATED)
		fun add(@RequestBody person: Person) {
			// ...
		}
	}
----
======


[[mvc-ann-requestmapping-uri-templates]]
== URI patterns
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-uri-templates[See equivalent in the Reactive stack]#

`@RequestMapping` methods can be mapped using URL patterns.
Spring MVC is using `PathPattern` -- a pre-parsed pattern matched against the URL path also pre-parsed as `PathContainer`.
Designed for web use, this solution deals effectively with encoding and path parameters, and matches efficiently.
See xref:web/webmvc/mvc-config/path-matching.adoc[MVC config] for customizations of path matching options.

NOTE: the `AntPathMatcher` variant is now deprecated because it is less efficient and the String path input is a
challenge for dealing effectively with encoding and other issues with URLs.

You can map requests by using glob patterns and wildcards:

[cols="2,3,5"]
|===
|Pattern |Description |Example

| `spring`
| Literal pattern
| `+"/spring"+` matches `+"/spring"+`

| `+?+`
| Matches one character
| `+"/pages/t?st.html"+` matches `+"/pages/test.html"+` and `+"/pages/t3st.html"+`

| `+*+`
| Matches zero or more characters within a path segment
| `+"/resources/*.png"+` matches `+"/resources/file.png"+`

`+"/projects/*/versions"+` matches `+"/projects/spring/versions"+` but does not match `+"/projects/spring/boot/versions"+`

| `+**+`
| Matches zero or more path segments
| `+"/resources/**"+` matches `+"/resources/file.png"+` and `+"/resources/images/file.png"+`

`+"/**/resources"+` matches `+"/spring/resources"+` and `+"/spring/framework/resources"+`

`+"/resources/**/file.png"+` is invalid as `+**+` is not allowed in the middle of the path.

`+"/**/{name}/resources"+` is invalid as only a literal pattern is allowed right after `+**+`.
`+"/**/project/{project}/resources"+` is allowed.

`+"/**/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.

| `+{name}+`
| Matches a path segment and captures it as a variable named "name"
| `+"/projects/{project}/versions"+` matches `+"/projects/spring/versions"+` and captures `+project=spring+`

`+"/projects/{project}/versions"+` does not match `+"/projects/spring/framework/versions"+` as it captures a single path segment.

| `+{name:[a-z]+}+`
| Matches the regexp `+"[a-z]+"+` as a path variable named "name"
| `+"/projects/{project:[a-z]+}/versions"+` matches `+"/projects/spring/versions"+` but not `+"/projects/spring1/versions"+`

| `+{*path}+`
| Matches zero or more path segments and captures it as a variable named "path"
| `+"/resources/{*file}"+` matches `+"/resources/images/file.png"+` and captures `+file=/images/file.png+`

`+"{*path}/resources"+` matches `+"/spring/framework/resources"+` and captures `+path=/spring/framework+`

`+"/resources/{*path}/file.png"+` is invalid as `{*path}` is not allowed in the middle of the path.

`+"/{*path}/{name}/resources"+` is invalid as only a literal pattern is allowed right after `{*path}`.
`+"/{*path}/project/{project}/resources"+` is allowed.

`+"/{*path}/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.

|===

Captured URI variables can be accessed with `@PathVariable`. For example:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/owners/{ownerId}/pets/{petId}")
	public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
		// ...
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/owners/{ownerId}/pets/{petId}")
	fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
		// ...
	}
----
======


You can declare URI variables at the class and method levels, as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@Controller
	@RequestMapping("/owners/{ownerId}")
	public class OwnerController {

		@GetMapping("/pets/{petId}")
		public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
			// ...
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@Controller
	@RequestMapping("/owners/{ownerId}")
	class OwnerController {

		@GetMapping("/pets/{petId}")
		fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
			// ...
		}
	}
----
======

URI variables are automatically converted to the appropriate type, or `TypeMismatchException`
is raised. Simple types (`int`, `long`, `Date`, and so on) are supported by default and you can
register support for any other data type.
See xref:web/webmvc/mvc-controller/ann-methods/typeconversion.adoc[Type Conversion] and xref:web/webmvc/mvc-controller/ann-initbinder.adoc[`DataBinder`].

You can explicitly name URI variables (for example, `@PathVariable("customId")`), but you can
leave that detail out if the names are the same and your code is compiled with the `-parameters`
compiler flag.

The syntax `{varName:regex}` declares a URI variable with a regular expression that has
syntax of `{varName:regex}`. For example, given URL `"/spring-web-3.0.5.jar"`, the following method
extracts the name, version, and file extension:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
	public void handle(@PathVariable String name, @PathVariable String version, @PathVariable String ext) {
		// ...
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
	fun handle(@PathVariable name: String, @PathVariable version: String, @PathVariable ext: String) {
		// ...
	}
----
======

URI path patterns can also have:

- Embedded `${...}` placeholders that are resolved on startup via
`PropertySourcesPlaceholderConfigurer` against local, system, environment, and
other property sources. This is useful, for example, to parameterize a base URL based on
external configuration.
- SpEL expression `#{...}`.


[[mvc-ann-requestmapping-pattern-comparison]]
== Pattern Comparison
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-pattern-comparison[See equivalent in the Reactive stack]#

When multiple patterns match a URL, the best match must be selected. This is done with
one of the following depending on whether use of parsed `PathPattern` is enabled for use or not:

* {spring-framework-api}/web/util/pattern/PathPattern.html#SPECIFICITY_COMPARATOR[`PathPattern.SPECIFICITY_COMPARATOR`]
* {spring-framework-api}/util/AntPathMatcher.html#getPatternComparator-java.lang.String-[`AntPathMatcher.getPatternComparator(String path)`]

Both help to sort patterns with more specific ones on top. A pattern is more specific if
it has a lower count of URI variables (counted as 1), single wildcards (counted as 1),
and double wildcards (counted as 2). Given an equal score, the longer pattern is chosen.
Given the same score and length, the pattern with more URI variables than wildcards is
chosen.

The default mapping pattern (`/{asterisk}{asterisk}`) is excluded from scoring and always
sorted last. Also, prefix patterns (such as `/public/{asterisk}{asterisk}`) are considered less
specific than other pattern that do not have double wildcards.

For the full details, follow the above links to the pattern Comparators.


[[mvc-ann-requestmapping-rfd]]
== Suffix Match and RFD

A reflected file download (RFD) attack is similar to XSS in that it relies on request input
(for example, a query parameter and a URI variable) being reflected in the response. However, instead of
inserting JavaScript into HTML, an RFD attack relies on the browser switching to perform a
download and treating the response as an executable script when double-clicked later.

In Spring MVC, `@ResponseBody` and `ResponseEntity` methods are at risk, because
they can render different content types, which clients can request through URL path extensions.
Disabling suffix pattern matching and using path extensions for content negotiation
lower the risk but are not sufficient to prevent RFD attacks.

To prevent RFD attacks, prior to rendering the response body, Spring MVC adds a
`Content-Disposition:inline;filename=f.txt` header to suggest a fixed and safe download
file. This is done only if the URL path contains a file extension that is neither
allowed as safe nor explicitly registered for content negotiation. However, it can
potentially have side effects when URLs are typed directly into a browser.

Many common path extensions are allowed as safe by default. Applications with custom
`HttpMessageConverter` implementations can explicitly register file extensions for content
negotiation to avoid having a `Content-Disposition` header added for those extensions.
See xref:web/webmvc/mvc-config/content-negotiation.adoc[Content Types].

See {spring-site-cve}/cve-2015-5211[CVE-2015-5211] for additional
recommendations related to RFD.


[[mvc-ann-requestmapping-consumes]]
== Consumable Media Types
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-consumes[See equivalent in the Reactive stack]#

You can narrow the request mapping based on the `Content-Type` of the request,
as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@PostMapping(path = "/pets", consumes = "application/json") // <1>
	public void addPet(@RequestBody Pet pet) {
		// ...
	}
----
<1> Using a `consumes` attribute to narrow the mapping by the content type.

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@PostMapping("/pets", consumes = ["application/json"]) // <1>
	fun addPet(@RequestBody pet: Pet) {
		// ...
	}
----
<1> Using a `consumes` attribute to narrow the mapping by the content type.
======

The `consumes` attribute also supports negation expressions -- for example, `!text/plain` means any
content type other than `text/plain`.

You can declare a shared `consumes` attribute at the class level. Unlike most other
request-mapping attributes, however, when used at the class level, a method-level `consumes` attribute
overrides rather than extends the class-level declaration.

TIP: `MediaType` provides constants for commonly used media types, such as
`APPLICATION_JSON_VALUE` and `APPLICATION_XML_VALUE`.


[[mvc-ann-requestmapping-produces]]
== Producible Media Types
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-produces[See equivalent in the Reactive stack]#

You can narrow the request mapping based on the `Accept` request header and the list of
content types that a controller method produces, as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@GetMapping(path = "/pets/{petId}", produces = "application/json") // <1>
	@ResponseBody
	public Pet getPet(@PathVariable String petId) {
		// ...
	}
----
<1> Using a `produces` attribute to narrow the mapping by the content type.

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/pets/{petId}", produces = ["application/json"]) // <1>
	@ResponseBody
	fun getPet(@PathVariable petId: String): Pet {
		// ...
	}
----
<1> Using a `produces` attribute to narrow the mapping by the content type.
======

The media type can specify a character set. Negated expressions are supported -- for example,
`!text/plain` means any content type other than "text/plain".

You can declare a shared `produces` attribute at the class level. Unlike most other
request-mapping attributes, however, when used at the class level, a method-level `produces` attribute
overrides rather than extends the class-level declaration.

TIP: `MediaType` provides constants for commonly used media types, such as
`APPLICATION_JSON_VALUE` and `APPLICATION_XML_VALUE`.


[[mvc-ann-requestmapping-params-and-headers]]
== Parameters, headers
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-params-and-headers[See equivalent in the Reactive stack]#

You can narrow request mappings based on request parameter conditions. You can test for the
presence of a request parameter (`myParam`), for the absence of one (`!myParam`), or for a
specific value (`myParam=myValue`). The following example shows how to test for a specific value:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") // <1>
	public void findPet(@PathVariable String petId) {
		// ...
	}
----
<1> Testing whether `myParam` equals `myValue`.

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) // <1>
	fun findPet(@PathVariable petId: String) {
		// ...
	}
----
<1> Testing whether `myParam` equals `myValue`.
======

You can also use the same with request header conditions, as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") // <1>
	public void findPet(@PathVariable String petId) {
		// ...
	}
----
<1> Testing whether `myHeader` equals `myValue`.

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
	fun findPet(@PathVariable petId: String) {
		// ...
	}
----
<1> Testing whether `myHeader` equals `myValue`.
======

TIP: You can match `Content-Type` and `Accept` with the headers condition, but it is better to use
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-consumes[consumes]
and xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-produces[produces]
instead.


[[mvc-ann-requestmapping-version]]
== API Version
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-version[See equivalent in the Reactive stack]#

There is no standard way to specify an API version, so when you enable API versioning
in the xref:web/webmvc/mvc-config/api-version.adoc[MVC Config] you need
to specify how to resolve the version. The MVC Config creates an
xref:web/webmvc-versioning.adoc#mvc-versioning-strategy[ApiVersionStrategy] that in turn
is used to map requests.

Once API versioning is enabled, you can begin to map requests with versions.
The `@RequestMapping` `version` attribute supports the following:

- No value -- matches any version
- Fixed version ("1.2") -- matches the given version only
- Baseline version ("1.2+") -- matches the given version and above

If multiple controller methods have a version less than or equal to the request version,
the highest of those, and closest to the request version, is the one considered,
in effect superseding the rest.

To illustrate this, consider the following mappings:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@RestController
	@RequestMapping("/account/{id}")
	public class AccountController {

		@GetMapping // <1>
		public Account getAccount() {
		}

		@GetMapping(version = "1.1") // <2>
		public Account getAccount1_1() {
		}

		@GetMapping(version = "1.2+") // <3>
		public Account getAccount1_2() {
		}

		@GetMapping(version = "1.5") // <4>
		public Account getAccount1_5() {
		}
	}
----
<1> match any version
<2> match version 1.1
<3> match version 1.2 and above
<4> match version 1.5
======

For request with version `"1.3"`:

- (1) matches as it matches any version
- (2) does not match
- (3) matches as it matches 1.2 and above, and is *chosen* as the highest match
- (4) is higher and does not match

For request with version `"1.5"`:

- (1) matches as it matches any version
- (2) does not match
- (3) matches as it matches 1.2 and above
- (4) matches and is *chosen* as the highest match

A request with version `"1.6"` does not have a match. (1) and (3) do match, but are
superseded by (4), which allows only a strict match, and therefore does not match.
In this scenario, a `NotAcceptableApiVersionException` results in a 400 response.

NOTE: The above assumes the request version is a
xref:web/webmvc/mvc-config/api-version.adoc["supported" version], or otherwise it
would fail.

See xref:web/webmvc-versioning.adoc[API Versioning] for more details on underlying
infrastructure and support for API Versioning.



[[mvc-ann-requestmapping-head-options]]
== HTTP HEAD, OPTIONS
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-head-options[See equivalent in the Reactive stack]#

`@GetMapping` (and `@RequestMapping(method=HttpMethod.GET)`) support HTTP HEAD
transparently for request mapping. Controller methods do not need to change.
A response wrapper, applied in `jakarta.servlet.http.HttpServlet`, ensures a `Content-Length`
header is set to the number of bytes written (without actually writing to the response).

By default, HTTP OPTIONS is handled by setting the `Allow` response header to the list of HTTP
methods listed in all `@RequestMapping` methods that have matching URL patterns.

For a `@RequestMapping` without HTTP method declarations, the `Allow` header is set to
`GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS`. Controller methods should always declare the
supported HTTP methods (for example, by using the HTTP method specific variants:
`@GetMapping`, `@PostMapping`, and others).

You can explicitly map the `@RequestMapping` method to HTTP HEAD and HTTP OPTIONS, but that
is not necessary in the common case.


[[mvc-ann-requestmapping-composed]]
== Custom Annotations
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-head-options[See equivalent in the Reactive stack]#

Spring MVC supports the use of xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[composed annotations]
for request mapping. Those are annotations that are themselves meta-annotated with
`@RequestMapping` and composed to redeclare a subset (or all) of the `@RequestMapping`
attributes with a narrower, more specific purpose.

`@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, and `@PatchMapping` are
examples of composed annotations. They are provided because, arguably, most
controller methods should be mapped to a specific HTTP method versus using `@RequestMapping`,
which, by default, matches to all HTTP methods. If you need an example of how to implement
a composed annotation, look at how those are declared.

NOTE: `@RequestMapping` cannot be used in conjunction with other `@RequestMapping`
annotations that are declared on the same element (class, interface, or method). If
multiple `@RequestMapping` annotations are detected on the same element, a warning will
be logged, and only the first mapping will be used. This also applies to composed
`@RequestMapping` annotations such as `@GetMapping`, `@PostMapping`, etc.

Spring MVC also supports custom request-mapping attributes with custom request-matching
logic. This is a more advanced option that requires subclassing
`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method, where
you can check the custom attribute and return your own `RequestCondition`.


[[mvc-ann-requestmapping-registration]]
== Explicit Registrations
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-requestmapping-registration[See equivalent in the Reactive stack]#

You can programmatically register handler methods, which you can use for dynamic
registrations or for advanced cases, such as different instances of the same handler
under different URLs. The following example registers a handler method:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@Configuration
	public class MyConfig {

		@Autowired
		public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) // <1>
				throws NoSuchMethodException {

			RequestMappingInfo info = RequestMappingInfo
					.paths("/user/{id}").methods(RequestMethod.GET).build(); // <2>

			Method method = UserHandler.class.getMethod("getUser", Long.class); // <3>

			mapping.registerMapping(info, handler, method); // <4>
		}
	}
----
<1> Inject the target handler and the handler mapping for controllers.
<2> Prepare the request mapping meta data.
<3> Get the handler method.
<4> Add the registration.

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@Configuration
	class MyConfig {

		@Autowired
		fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { // <1>
			val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() // <2>
			val method = UserHandler::class.java.getMethod("getUser", Long::class.java) // <3>
			mapping.registerMapping(info, handler, method) // <4>
		}
	}
----
<1> Inject the target handler and the handler mapping for controllers.
<2> Prepare the request mapping meta data.
<3> Get the handler method.
<4> Add the registration.
======



[[mvc-ann-httpexchange-annotation]]
== `@HttpExchange`
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-httpexchange-annotation[See equivalent in the Reactive stack]#

While the main purpose of `@HttpExchange` is to abstract HTTP client code with a
generated proxy, the interface on which such annotations are placed is a contract neutral
to client vs server use. In addition to simplifying client code, there are also cases
where an xref:integration/rest-clients.adoc#rest-http-service-client[HTTP Service Client]
may be a convenient way for servers to expose their API for client access. This leads
to increased coupling between client and server and is often not a good choice,
especially for public API's, but may be exactly the goal for an internal API.
It is an approach commonly used in Spring Cloud, and it is why `@HttpExchange` is
supported as an alternative to `@RequestMapping` for server side handling in
controller classes.

For example:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
	@HttpExchange("/persons")
	interface PersonService {

		@GetExchange("/{id}")
		Person getPerson(@PathVariable Long id);

		@PostExchange
		void add(@RequestBody Person person);
	}

	@RestController
	class PersonController implements PersonService {

		public Person getPerson(@PathVariable Long id) {
			// ...
		}

		@ResponseStatus(HttpStatus.CREATED)
		public void add(@RequestBody Person person) {
			// ...
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
	@HttpExchange("/persons")
	interface PersonService {

		@GetExchange("/{id}")
		fun getPerson(@PathVariable id: Long): Person

		@PostExchange
		fun add(@RequestBody person: Person)
	}

	@RestController
	class PersonController : PersonService {

		override fun getPerson(@PathVariable id: Long): Person {
			// ...
		}

		@ResponseStatus(HttpStatus.CREATED)
		override fun add(@RequestBody person: Person) {
			// ...
		}
	}
----
======

`@HttpExchange` and `@RequestMapping` have differences.
`@RequestMapping` can map to any number of requests by path patterns, HTTP methods,
and more, while `@HttpExchange` declares a single endpoint with a concrete HTTP method,
path, and content types.

For method parameters and returns values, generally, `@HttpExchange` supports a
subset of the method parameters that `@RequestMapping` does. Notably, it excludes any
server-side specific parameter types. For details, see the list for
xref:integration/rest-clients.adoc#rest-http-service-client-method-parameters[@HttpExchange] and
xref:web/webmvc/mvc-controller/ann-methods/arguments.adoc[@RequestMapping].

`@HttpExchange` also supports a `headers()` parameter which accepts `"name=value"`-like
pairs like in `@RequestMapping(headers={})` on the client side. On the server side,
this extends to the full syntax that
xref:#mvc-ann-requestmapping-params-and-headers[`@RequestMapping`] supports.
