Solicitação de primavera
1. Visão geral
Neste artigo, vamos nos concentrar em uma das principais anotações emSpring MVC – @RequestMapping.
Simplificando, a anotação é usada para mapear solicitações da Web para os métodos do Spring Controller.
Leitura adicional:
Servir recursos estáticos com o Spring
Como mapear e manipular recursos estáticos com o Spring MVC - use a configuração simples, depois a 3.1 mais flexível e, finalmente, os novos resolvedores de recursos 4.1.
Introdução aos Formulários no Spring MVC
Aprenda a trabalhar com formulários usando o Spring MVC - mapeando uma entidade básica, enviando, exibindo erros.
Conversores de mensagens HTTP com o Spring Framework
Como configurar o HttpMessageConverters para uma API REST com Spring e como usar esses conversores com o RestTemplate.
2. @RequestMapping Básico
Vamos começar com um exemplo simples - mapeando uma solicitação HTTP para um método usando alguns critérios básicos.
2.1. @RequestMapping - por caminho
@RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
@ResponseBody
public String getFoosBySimplePath() {
return "Get some Foos";
}
Para testar esse mapeamento com um comandocurl simples, execute:
curl -i http://localhost:8080/spring-rest/ex/foos
2.2. @RequestMapping - o método HTTP
O parâmetro HTTPmethod temno default - então, se não especificarmos um valor, ele será mapeado para qualquer solicitação HTTP.
Aqui está um exemplo simples, semelhante ao anterior - mas desta vez mapeado para uma solicitação HTTP POST:
@RequestMapping(value = "/ex/foos", method = POST)
@ResponseBody
public String postFoos() {
return "Post some Foos";
}
Para testar o POST por meio de um comandocurl:
curl -i -X POST http://localhost:8080/spring-rest/ex/foos
3. RequestMappinge cabeçalhos HTTP
3.1. @RequestMapping com o atributoheaders
O mapeamento pode ser reduzido ainda mais especificando um cabeçalho para a solicitação:
@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET)
@ResponseBody
public String getFoosWithHeader() {
return "Get some Foos with Header";
}
Para testar a operação, vamos usar o suporte de cabeçalhocurl:
curl -i -H "key:val" http://localhost:8080/spring-rest/ex/foos
E até mesmo vários cabeçalhos por meio do atributoheader de@RequestMapping:
@RequestMapping(
value = "/ex/foos",
headers = { "key1=val1", "key2=val2" }, method = GET)
@ResponseBody
public String getFoosWithHeaders() {
return "Get some Foos with Header";
}
Podemos testar isso com o comando:
curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/spring-rest/ex/foos
Observe que para a sintaxecurl para separar a chave do cabeçalho e o valor do cabeçalho são dois pontos, o mesmo que na especificação HTTP, enquanto no Spring o sinal de igual é usado.
3.2. @RequestMapping Consome e Produz
O método de mapeamentomedia types produced by a controller merece atenção especial - podemos mapear uma solicitação com base em seu cabeçalhoAccept por meio do atributo headers@RequestMapping introduzido acima:
@RequestMapping(
value = "/ex/foos",
method = GET,
headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
return "Get some Foos with Header Old";
}
A correspondência para essa maneira de definir o cabeçalhoAccept é flexível - ela usa contém em vez de igual, portanto, uma solicitação como a seguinte ainda seria mapeada corretamente:
curl -H "Accept:application/json,text/html"
http://localhost:8080/spring-rest/ex/foos
A partir do Spring 3.1, o@RequestMapping annotation now has the produces and the consumes attributes, especificamente para este propósito:
@RequestMapping(
value = "/ex/foos",
method = RequestMethod.GET,
produces = "application/json"
)
@ResponseBody
public String getFoosAsJsonFromREST() {
return "Get some Foos with Header New";
}
Além disso, o tipo antigo de mapeamento com o atributoheaders será automaticamente convertido para o novo mecanismoproduces começando com Spring 3.1, então os resultados serão idênticos.
Isso é consumido viacurl da mesma maneira:
curl -H "Accept:application/json"
http://localhost:8080/spring-rest/ex/foos
Além disso,produces também oferece suporte a vários valores:
@RequestMapping(
value = "/ex/foos",
method = GET,
produces = { "application/json", "application/xml" }
)
Tenha em mente que estes - a maneira antiga e a nova maneira de especificar o cabeçalhoaccept - são basicamente o mesmo mapeamento, então o Spring não os permitirá juntos - ter ambos os métodos ativos resultaria em:
Caused by: java.lang.IllegalStateException: Ambiguous mapping found.
Cannot map 'fooController' bean method
java.lang.String
org.example.spring.web.controller
.FooController.getFoosAsJsonFromREST()
to
{ [/ex/foos],
methods=[GET],params=[],headers=[],
consumes=[],produces=[application/json],custom=[]
}:
There is already 'fooController' bean method
java.lang.String
org.example.spring.web.controller
.FooController.getFoosAsJsonFromBrowser()
mapped.
Uma nota final sobre o novo mecanismoproduceseconsumes - eles se comportam de maneira diferente da maioria das outras anotações: quando especificado no nível de tipo,the method level annotations do not complement but override as informações de nível de tipo.
E, claro, se você quiser se aprofundar na construção de uma API REST com Spring -check outthe new REST with Spring course.
4. RequestMapping com variáveis de caminho
Partes do URI de mapeamento podem ser vinculadas a variáveis por meio da anotação@PathVariable.
4.1. Único@PathVariable
Um exemplo simples com uma variável de caminho único:
@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
@PathVariable("id") long id) {
return "Get a specific Foo with id=" + id;
}
Isso pode ser testado comcurl:
curl http://localhost:8080/spring-rest/ex/foos/1
Se o nome do parâmetro do método corresponder exatamente ao nome da variável de caminho, isso pode ser simplificado porusing @PathVariable with no value:
@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
@PathVariable String id) {
return "Get a specific Foo with id=" + id;
}
Observe que@PathVariable se beneficia da conversão automática de tipo, então também poderíamos ter declarado o id como:
@PathVariable long id
4.2. Múltiplos@PathVariable
URI mais complexo pode precisar mapear várias partes do URI paramultiple values:
@RequestMapping(value = "/ex/foos/{fooid}/bar/{barid}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariables
(@PathVariable long fooid, @PathVariable long barid) {
return "Get a specific Bar with id=" + barid +
" from a Foo with id=" + fooid;
}
Isso é facilmente testado com umcurl da mesma maneira:
curl http://localhost:8080/spring-rest/ex/foos/1/bar/2
4.3. @PathVariable com RegEx
Expressões regulares também podem ser usadas ao mapear@PathVariable; por exemplo, vamos restringir o mapeamento para aceitar apenas valores numéricos paraid:
@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET)
@ResponseBody
public String getBarsBySimplePathWithPathVariable(
@PathVariable long numericId) {
return "Get a specific Bar with id=" + numericId;
}
Isso significa que os seguintes URIs corresponderão:
http://localhost:8080/spring-rest/ex/bars/1
Mas isso não vai:
http://localhost:8080/spring-rest/ex/bars/abc
5. RequestMapping com parâmetros de solicitação
@RequestMapping permitemapping of URL parameters with the @RequestParam annotation fácil. __
Agora, estamos mapeando uma solicitação para um URI, como:
http://localhost:8080/spring-rest/ex/bars?id=100
@RequestMapping(value = "/ex/bars", method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(
@RequestParam("id") long id) {
return "Get a specific Bar with id=" + id;
}
Em seguida, estamos extraindo o valor do parâmetroid usando a anotação@RequestParam(“id”) na assinatura do método do controlador.
Para enviar uma solicitação com o parâmetroid, usaremos o suporte de parâmetro emcurl:
curl -i -d id=100 http://localhost:8080/spring-rest/ex/bars
Neste exemplo, o parâmetro foi vinculado diretamente sem ter sido declarado primeiro.
Para cenários mais avançados,@RequestMapping can optionally define the parameters - como mais uma maneira de restringir o mapeamento da solicitação:
@RequestMapping(value = "/ex/bars", params = "id", method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(
@RequestParam("id") long id) {
return "Get a specific Bar with id=" + id;
}
Mapeamentos ainda mais flexíveis são permitidos - vários valores deparams podem ser definidos, e nem todos eles precisam ser usados:
@RequestMapping(
value = "/ex/bars",
params = { "id", "second" },
method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParams(
@RequestParam("id") long id) {
return "Narrow Get a specific Bar with id=" + id;
}
E, claro, uma solicitação para um URI, como:
http://localhost:8080/spring-rest/ex/bars?id=100&second=something
Sempre será mapeado para a melhor correspondência - que é a correspondência mais estreita, que define os parâmetrosidesecond.
6. RequestMapping Casos de canto
6.1. @RequestMapping - Múltiplos caminhos mapeados para o mesmo método de controlador
Embora um único valor de caminho@RequestMapping seja geralmente usado para um único método de controlador, essa é apenas uma boa prática, não uma regra rígida e rápida - há alguns casos em que o mapeamento de várias solicitações para o mesmo método pode ser necessário. Nesse caso,the value attribute of @RequestMapping does accept multiple mappings, não apenas um:
@RequestMapping(
value = { "/ex/advanced/bars", "/ex/advanced/foos" },
method = GET)
@ResponseBody
public String getFoosOrBarsByPath() {
return "Advanced - Get some Foos or Bars";
}
Agora, esses dois comandos curl devem atingir o mesmo método:
curl -i http://localhost:8080/spring-rest/ex/advanced/foos
curl -i http://localhost:8080/spring-rest/ex/advanced/bars
6.2. @RequestMapping - Vários métodos de solicitação HTTP para o mesmo método de controlador
Várias solicitações usando diferentes verbos HTTP podem ser mapeadas para o mesmo método do controlador:
@RequestMapping(
value = "/ex/foos/multiple",
method = { RequestMethod.PUT, RequestMethod.POST }
)
@ResponseBody
public String putAndPostFoos() {
return "Advanced - PUT and POST within single method";
}
Comcurl, ambos agora atingirão o mesmo método:
curl -i -X POST http://localhost:8080/spring-rest/ex/foos/multiple
curl -i -X PUT http://localhost:8080/spring-rest/ex/foos/multiple
6.3. @RequestMapping - Um substituto para todas as solicitações
Para implementar um fallback simples para todas as solicitações usando um método HTTP específico - por exemplo, para um GET:
@RequestMapping(value = "*", method = RequestMethod.GET)
@ResponseBody
public String getFallback() {
return "Fallback for GET Requests";
}
Ou mesmo para todos os pedidos:
@RequestMapping(
value = "*",
method = { RequestMethod.GET, RequestMethod.POST ... })
@ResponseBody
public String allFallback() {
return "Fallback for All Requests";
}
6.4. Erro de mapeamento ambíguo
O erro de mapeamento ambíguo ocorre quando o Spring avalia dois ou mais mapeamentos de solicitação iguais para diferentes métodos de controlador. Um mapeamento de solicitação é o mesmo quando possui o mesmo método HTTP, URL, parâmetros, cabeçalhos e tipo de mídia. Este, por exemplo, é um mapeamento ambíguo:
@GetMapping(value = "foos/duplicate")
public String duplicate() {
return "Duplicate";
}
@GetMapping(value = "foos/duplicate")
public String duplicateEx() {
return "Duplicate";
}
A exceção lançada geralmente tem mensagens de erro ao longo destas linhas:
Caused by: java.lang.IllegalStateException: Ambiguous mapping.
Cannot map 'fooMappingExamplesController' method
public java.lang.String org.example.web.controller.FooMappingExamplesController.duplicateEx()
to {[/ex/foos/duplicate],methods=[GET]}:
There is already 'fooMappingExamplesController' bean method
public java.lang.String org.example.web.controller.FooMappingExamplesController.duplicate() mapped.
Uma leitura cuidadosa da mensagem de erro aponta para o fato de que o Spring é incapaz de mapear o métodoorg.example.web.controller.FooMappingExamplesController.duplicateEx() porque ele tem um mapeamento conflitante com umorg.example.web.controller.FooMappingExamplesController.duplicate(). já mapeado
O snippet de código abaixo não resultará em erro de mapeamento ambíguo porque os dois métodos retornam tipos de conteúdo diferentes:
@GetMapping(value = "foos/duplicate/xml", produces = MediaType.APPLICATION_XML_VALUE)
public String duplicateXml() {
return "Duplicate";
}
@GetMapping(value = "foos/duplicate/json", produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateJson() {
return "Duplicate";
}
A maneira óbvia de resolver isso é atualizar o URL atribuído a um dos dois métodos envolvidos.
7. Novos atalhos de mapeamento de solicitação
Spring Framework 4.3 introduziu anotações de mapeamento HTTPa few new, todas baseadas em@RequestMapping:
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
Essas novas anotações podem melhorar a legibilidade e reduzir a verbosidade do código. Vejamos essas novas anotações em ação, criando uma API RESTful que suporta operações CRUD:
@GetMapping("/{id}")
public ResponseEntity> getBazz(@PathVariable String id){
return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK);
}
@PostMapping
public ResponseEntity> newBazz(@RequestParam("name") String name){
return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK);
}
@PutMapping("/{id}")
public ResponseEntity> updateBazz(
@PathVariable String id,
@RequestParam("name") String name) {
return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity> deleteBazz(@PathVariable String id){
return new ResponseEntity<>(new Bazz(id), HttpStatus.OK);
}
Um mergulho mais profundo neles pode ser encontradohere.
8. Configuração da mola
A configuração do Spring MVC é bastante simples - considerando que nossoFooController é definido no seguinte pacote:
package org.example.spring.web.controller;
@Controller
public class FooController { ... }
Precisamos simplesmente de uma classe@Configuration para habilitar o suporte MVC completo e configurar a varredura de caminho de classe para o controlador:
@Configuration
@EnableWebMvc
@ComponentScan({ "org.example.spring.web.controller" })
public class MvcConfig {
//
}
9. Conclusão
Este artigo se concentra no@RequestMapping annotation in Spring - discutindo um caso de uso simples, o mapeamento de cabeçalhos HTTP, vinculando partes do URI com@PathVariablee trabalhando com parâmetros de URI e a anotação@RequestParam.
Se você gostaria de aprender como usar outra anotação principal no Spring MVC, você pode explorarthe @ModelAttribute annotation here.
O código completo do artigo está disponívelon GitHub. Como é um projeto do Maven, deve ser fácil importar e executar como está.