请求方法和原理
在spring boot结合搜请求时,可以在controller中使用RequestMapping
注解。
以下是以表单提交为例说明,完成REST风格的请求格式,如GET、PUT、POST、DELETE请求,代码如下:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
}
注意:在页面表单提交中,只有GET和POST两种提交方式,那怎么完成PUT和DELETE操作呢?
主要是通过一个 核心Filter:在WebMvcAutoConfiguration
中的HiddenHttpMethodFilter
完成。如果看核心源码,会发现他继承自HiddenHttpMethodFilter
,而HiddenHttpMethodFilter会指定一个属性``_method`。具体用法:
- 表单method=post,隐藏域 _method=put
-
SpringBoot中手动开启
开启配置如下:
spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
其中,HiddenHttpMethodFilter和核心源码如下:
package org.springframework.web.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
private static final List<String> ALLOWED_METHODS;
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = "_method";
public HiddenHttpMethodFilter() {
}
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
static {
ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
其中核心方法是doFilterInternal。
综上表单提交REST风格请求原理如下:
- 表单提交会带上_method=PUT
- 请求过来被HiddenHttpMethodFilter拦截
- 判断 请求是否正常,并且是POST。如果是,获取到_method的值。
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
注意:使用客户端工具,如PostMan直接发送Put、delete等方式请求,无需Filter。
REST风格请求
上面说了使用客户端工具,发送Put、delete等方式请求,无需Filter。也提供了其他注解。修改上面的UserController如下:
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController {
// @RequestMapping(value = "/user",method = RequestMethod.GET)
@GetMapping("/user")
public String getUser(){
return "GET-张三";
}
// @RequestMapping(value = "/user",method = RequestMethod.POST)
@PostMapping("/user")
public String saveUser(){
return "POST-张三";
}
// @RequestMapping(value = "/user",method = RequestMethod.PUT)
@PutMapping("/user")
public String putUser(){
return "PUT-张三";
}
// @RequestMapping(value = "/user",method = RequestMethod.DELETE)
@DeleteMapping("/user")
public String deleteUser(){
return "DELETE-张三";
}
}
上面可以看到,很多都是"/user"开头,仅有一步优化,直接把重复路由放在类上,代码如下:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping
public String getUser(){
return "GET-张三";
}
@PostMapping
public String saveUser(){
return "POST-张三";
}
@PutMapping
public String putUser(){
return "PUT-张三";
}
@DeleteMapping
public String deleteUser(){
return "DELETE-张三";
}
}
请求参数
在路径中的参数
例如 /user/1这种,后面的1就是路径中参数。
使用PathVariable
注解获取路径上的参数,如下:
@GetMapping("/{id}")
public String getUser(@PathVariable("id") Integer id){
return "GET-" + id;
}
也可以有多个路径参数,都可以指定名字获取。
同时,也可以使用map接受路径上的所有参数,map泛型必须是<String, String>。如下:
@GetMapping("/{id}/{name}")
public String getUser(@PathVariable Map<String, String> req){
System.out.println(req);
return "GET-" + req.get("id");
}
还可以使用RequestParam
注解,如下:
@GetMapping("/{id}/{name}")
public String getUser(@RequestParam Map<String, String> req){
System.out.println(req);
return "GET-" + req.get("id");
}
路径后的参数
例如user?id=1&name=abc这种在问号后加的参数
使用RequestParam
注解,如下:
@GetMapping("")
public String getUser(@RequestParam Map<String, String> req){
System.out.println(req);
return "GET-" + req.get("id");
}
也可以指定名称接受指定参数,如下:
@GetMapping("")
public String getUser(@RequestParam("name") String name){
System.out.println(name);
return "GET-" + name;
}
post或put请求参数
使用RequestBody
注解,如下:
@PostMapping
public String saveUser(@RequestBody String name){
return "POST-张三";
}
如果参数是一个json格式的,可以自定义一个对象接受。如下:
import lombok.Data;
@Data
public class UpdateUserReq {
private Integer id;
private String name;
}
然后使用这个对象接受我们的JSON参数:
@PutMapping
public String putUser(@RequestBody UpdateUserReq req){
System.out.println(req);
return "PUT-" + req.getName();
}
获取请求头信息
使用RequestHeader
注解,如下:
@PostMapping
public String saveUser(@RequestBody String name,
@RequestHeader("User-Agent") String userAgent){
return "POST-" + name;
}
也可以拿到所有的请求头,如下:
@PostMapping
public String saveUser(@RequestBody String name,
@RequestHeader Map<String, String> header){
return "POST-" + name;
}
获取cookie
使用注解在这里插入代码片
,如下:
@PostMapping
public String saveUser(@RequestBody String name,
@CookieValue("_ga") String gaCookie){
return "POST-" + name;
}
如果获取所有的cookie,也可以直接使用javax.servlet.http.Cookie
类型接受,如下:
@PostMapping
public String saveUser(@RequestBody String name,
Cookie cookie){
return "POST-" + name;
}
上传文件
在spring boot中使用MultipartFile
类进行接受文件类型。
如下:
@PostMapping("/employee/batch")
public void batchCreateEmployee(@RequestPart("headerImg") MultipartFile file) {
System.out.println(file.getName());
return;
}
如果一个对象有多个文件,可使用数组接受,如下:
@PostMapping("/employee/batch")
public void batchCreateEmployee(@RequestPart("photos") MultipartFile[] photos) {
System.out.println(file.getName());
return;
}