@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
Controllers
Controllers provide access to the application behavior that you typically define through a service interface. Controllers interpret user input and transform it into a model that is represented to the user by the view. Spring implements a controller in a very abstract way, which enables you to create a wide variety of controllers.
As you can see, the @Controller
and @RequestMapping
annotations allow flexible
method names and signatures. In this particular example the method accepts a Model
and
returns a view name as a String
.
Defining a controller with @Controller
The @Controller
annotation acts as a stereotype for the annotated class, indicating its role. The dispatcher scans such annotated classes for mapped methods and detects @RequestMapping
annotations (see the next section).
Mapping Requests With @RequestMapping
You use the @RequestMapping
annotation to map URLs such as /appointments
onto an
entire class or a particular handler method. Typically the class-level annotation maps a
specific request path (or path pattern) onto a form controller, with additional
method-level annotations narrowing the primary mapping for a specific HTTP method
request method ("GET", "POST", etc.) or an HTTP request parameter condition.
The following example from the Petcare sample shows a controller in a Spring MVC application that uses this annotation:
@Controller
@RequestMapping("/appointments")
public class AppointmentsController {
private final AppointmentBook appointmentBook;
@Autowired
public AppointmentsController(AppointmentBook appointmentBook) {
this.appointmentBook = appointmentBook;
}
@RequestMapping(method = RequestMethod.GET)
public Map<String, Appointment> get() {
return appointmentBook.getAppointmentsForToday();
}
@RequestMapping(path = "/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
@RequestMapping(path = "/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
@RequestMapping(method = RequestMethod.POST)
public String add(@Valid AppointmentForm appointment, BindingResult result) {
if (result.hasErrors()) {
return "appointments/new";
}
appointmentBook.addAppointment(appointment);
return "redirect:/appointments";
}
}
In the above example, @RequestMapping
is used in a number of places. The first usage is
on the type (class) level, which indicates that all handler methods in this controller
are relative to the /appointments
path. The get()
method has a further
@RequestMapping
refinement: it only accepts GET
requests, meaning that an HTTP GET
for
/appointments
invokes this method. The add()
has a similar refinement, and the
getNewForm()
combines the definition of HTTP method and path into one, so that GET
requests for appointments/new
are handled by that method.
The getForDay()
method shows another usage of @RequestMapping
: URI templates. (See
[mvc-ann-requestmapping-uri-templates]).
Composed @RequestMapping Variants
Spring Framework 4.3 introduces the following method-level composed variants of the
@RequestMapping
annotation that help to simplify mappings for common HTTP methods and
better express the semantics of the annotated handler method. For example, a
@GetMapping
can be read as a GET
@RequestMapping
.
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
URI Template Patterns - @PathVariable
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
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
.
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
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.
URI Template Patterns with Regular Expressions
Sometimes you need more precision in defining URI template variables. Consider the URL
"/spring-web/spring-web-3.0.5.jar"
. How do you break it down into multiple parts?
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:
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String extension) {
// ...
}
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
media type. For example:
@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
using constants provided in MediaType
such as APPLICATION_JSON_VALUE
and
APPLICATION_JSON_UTF8_VALUE
.
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
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:
@GetMapping(path = "/pets/{petId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
// implementation omitted
}
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 as APPLICATION_JSON_VALUE
and APPLICATION_JSON_UTF8_VALUE
.
Request Parameters and Header Values
You can narrow request matching through request parameter conditions such as
"myParam"
, "!myParam"
, or "myParam=myValue"
. The first two test for request
parameter presence/absence and the third for a specific parameter value. Here is an
example with a request parameter value condition:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
The same can be done to test for request header presence/absence or to match based on a specific request header value:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@GetMapping(path = "/pets", headers = "myHeader=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}