1 请求映射
1.1 restful风格
请求映射是通过编写一个controller并在每一个方法上加上requestmapping完成的。在没有使用restful风格之前,我们进行对用户的增删改查操作定义的请求路径往往是:获取用户/getUser、删除用户/deleteUser、增加用户/addUser、修改用户/updateUser等,由于请求路径太多,请求路径名起到最后往往不知道起什么,在引入restful风格后解决了这一问题。
restful风格是使用Http请求方式动词表示对资源的操作,GET——查找用户、DELETE——删除用户、PUT——更改用户、POST——添加用户,具体使用如下:
@RestController
public class HelloController {
@RequestMapping(value = "/user",method = RequestMethod.GET)
//等同于@GetMapping("/user")
public String getUser() {
return "get user";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
//等同于@PostMapping("/user")
public String postUser() {
return "post user";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
//等同于@DeleteMapping("/user")
public String deleteUser() {
return "delete user";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
//等同于@PutMapping("/user")
public String putUser() {
return "put user";
}
}
这样写后两个方式delete和put并不支持,都被当成get方式处理,通过给springmvc中配置HiddenHttpMethodFilter解决。
<body>
<form action="/user" method="get">
<input type="submit" value="get方式"/>
</form>
<form action="/user" method="post">
<input type="submit" value="post方式"/>
</form>
<form action="/user" method="delete">
<input type="submit" value="delete方式"/>
</form>
<form action="/user" method="put">
<input type="submit" value="put方式"/>
</form>
</body>
通过查看源码,springboot中的自动配置类通过OrderedHiddenHttpMethodFilter方法向容器中注册了HiddenHttpMethodFilter组件,但需要手动在配置文件中修改spring.mvc.hiddenmethod.filter.enabled为true,另外要将form表单里的method设为POST并加入一个隐藏项name为_method,值为相应的PUT、DELETE最终实现表单的restful风格。
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = {"enabled"}
//默认是不开启的,通过prefix和name在配置文件将其配置为true,
//使HiddenHttpMethodFilter组件生效
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
<body>
<form action="/user" method="get">
<input type="submit" value="get方式"/>
</form>
<form action="/user" method="post">
<input type="submit" value="post方式"/>
</form>
<form action="/user" method="post">
<input name="_method" value="DELETE" type="hidden">
<input type="submit" value="delete方式"/>
</form>
<form action="/user" method="post">
<input name="_method" value="PUT" type="hidden">
<input type="submit" value="put方式"/>
</form>
</body>
restful原理:简而言之就是表单提交的请求会被HiddenHttpMethodFilter拦截,如果是post方式且没有错误,经过处理会根据请求携带的_method的值对post方式进行包装,将请求方式更改为对应的_method的值,如果是客户端工具,可以直接发送delete、put请求,无需HiddenHttpMethodFilter的处理。
1.2 请求映射原理
SpringMVC的功能分析都是从DispatcherServlet类的doDispatch方法入手,此方法调用getHandler(processedRequest)对获取到的所有请求使用for循环逐个匹配容器中的handlerMappings。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 找到当前请求使用哪个Handler(Controller的方法)处理
mappedHandler = getHandler(processedRequest);
//HandlerMapping:处理器映射。/xxx->>xxxx
常见的HandlerMapping有:RequestMappingHandlerMapping——保存了所有@RequestMapping和handler的映射规则,WelcomePageHandlerMapping——欢迎页的映射规则。
总结下来,请求进来,挨个尝试所有的HandlerMapping看是否能匹配,如果能就找到这个请求对应的handler,如果不能就找下一个 HandlerMapping。
特别地,如果我们需要一些自定义的映射处理,也可以自己给容器中放HandlerMapping,即自定义 HandlerMapping
2 常用参数注解
2.1 @PathVariable
在Restful请求风格中,使用路径的方式进行参数的传递,@PathVariable注解是用来获取url路径中参数的值。
另外,如果参数的位置有一个map<String,String>,那么会将所有的路径变量的key和value放进map中。
@GetMapping("/car/{carId}/owner/{username}")
public Map<String,Object> car(@PathVariable("carId") String id,
@PathVariable("username") String name,
@PathVariable Map<String,String> pv) {
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("pv",pv);
return map;
}
2.2 @RequestHeader
@RequestHeader注解用来获取请求头的值,另外,如果参数的位置有一个map<String,String>,那么会将所有的请求头的key和value放进map中。
@GetMapping("/header")
public Map<String,Object> header(@RequestHeader("user-agent") String useragent,
@RequestHeader Map<String,String> headers) {
Map<String,Object> map = new HashMap<>();
map.put("useragent",useragent);
map.put("headers",headers);
return map;
}
2.3 @RequestParam
@RequestParam注解用来获取请求参数的值,另外,如果参数的位置有一个map<String,String>,那么会将所有的@RequestParam的key和value放进map中。如果方法参数名和路径变量名一样会自动匹配可以省略该注解。
@GetMapping("/param")
public Map<String,Object> param(@RequestParam("age") Integer age,
@RequestParam("interests") List<String> interests,
@RequestParam Map<String,String> params) {
Map<String,Object> map = new HashMap<>();
map.put("age",age);
map.put("interests",interests);
map.put("params",params);
return map;
}
2.4 @RequestBody
@RequestBody注解用来获取请求体的值,post请求才有请求体
@PostMapping("save")
public Map<String,Object> save(@RequestBody String request) {
Map<String, Object> map = new HashMap<>();
map.put("request",request);
return map;
}
2.5 @RequestAttribute
@RequestAttribute用来获取request域的值,两种获取方式:①直接@RequestAttribute注解 ②HttpServletRequest对象的getAttribute方法。
@Controller
public class DemoController {
@RequestMapping("/to1")
public String to1(HttpServletRequest request) {
request.setAttribute("msg","ok了");
return "/to2";
}
@ResponseBody
@RequestMapping("/to2")
public String to2(@RequestAttribute("msg") String message,HttpServletRequest request) {
String anno = "注解方式得到" + message;
String normal = "普通方式得到" + request.getAttribute("msg");
return anno + "|||" + normal;
}
}
一般都是存到request域中,在页面用EL表达式取
2.6 @MatrixVariable
url重写解决页面开发中cookie禁用时获取session里面的内容的问题,是把cookie的值使用矩阵变量的方式进行传递
矩阵变量必须绑定在路径变量中,其使用步骤如下:
(1)springboot默认禁用矩阵变量,需要手动开启,有两种方法。
第一种是通过自定义配置类向容器注册WebMvcConfigurer组件替代默认的组件来开启矩阵变量
@Configuration
public class MyConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
第二种是让配置类实现WebMvcConfigurer,重写configurePathMatch方法
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//不移除分号后面的内容,使矩阵变量功能生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
(2)使用,一定要把矩阵变量绑定在路径变量中!!
//访问路径/car;brand=bmw,byd/son;age=12;name=xp
//访问路径也可以写成/car;brand=bmw;brand=byd/son;age=12;name=xp
@GetMapping("/{traffic}/{relation}")
public Map<String,Object> boss(@PathVariable("traffic") String traffic,
@PathVariable("relation") String relation,
@MatrixVariable("brand") List<String> brands,
@MatrixVariable("age") Integer age,
@MatrixVariable("name") String name) {
Map<String, Object> map = new HashMap<>();
map.put("relation",relation);
map.put("traffic",traffic);
map.put("name",name);
map.put("age",age);
map.put("brands",brands);
return map;
}
如果矩阵变量的名字重复可以用下面这种方式解决
3 原理简析
springmvc除了在参数位置处标注注解的参数可以用参数解析器解析外,方法位置也允许传入servlet api(WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId)、复杂参数(Map、Model(给map、model放数据会被放在request的请求域,相当于调用了 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder)、自定义对象参数,都可以用参数解析器解析。
页面提交的请求数据(GET、POST)会自动和自定义对象参数的属性(也就是POJO类的参数)进行绑定
@RequestMapping("/user")
public User user(User user) {
return user;
}
public class User {
private String name;
private int age;
private Pet pet;
}
<body>
<form action="/user" method="post">
用户名<input name="name"><br>
年龄<input name="age"><br>
宠物名<input name="pet.name"><br>
<input type="submit" value="提交">
</form>
</body>
可以自定义转换器,通过向容器注册WebMvcConfigurer组件重写相应的方法即可