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.

@Controller
public class HelloWorldController {

	@RequestMapping("/helloWorld")
	public String helloWorld(Model model) {
		model.addAttribute("message", "Hello World!");
		return "helloWorld";
	}
}

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
	}

}

results matching ""

    No results matching ""