2 RestFul风格传参
什么是REST? REST(英文:Representational State Transfer,简称REST,意思:表述性状态转换,描述了一个架构样式的网络系统,比如web应用)。它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端和服务端交互类的软件。基于这个风格设计的软件可以更简介,更有层次,更易于实现缓存等机制。
什么是RESTFUL
什么是RESTFUL?满足REST约束条件和原则的应用程序或设计就是RESTFUL。
- 资源:互联网所有的事物都可以被抽象为资源
- 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应 添加、 删除、修改、查询。
更简化的理解,可以认为url路径不是写死的而是可以作为参数传递的
@PathVariable注解
例如我们的请求路径上包含了具体的资源id,那么就会请求到该资源,当然也可以附加一些操作,例如增删改查
@RequestMapping("/restfulParam/{id}") public void restfulParam(@PathVariable("id") Long id) { System.out.println("restfulParam参数为" + id); }
请求的url如下:
http://localhost:8081/base_annotation/user/restfulParam/10
返回结果如下:
3 请求附加信息获取
如果我们想获取请求的一些附加信息,例如 获取请求头中的User-Agent和Cookie信息,可以这么去获取:
@RequestMapping("/headerInfo") public void headerInfo(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String cookieName) { System.out.println("User-Agent:" + userAgent); System.out.println("cookieName" + cookieName); }
请求的url如下:
http://localhost:8081/base_annotation/user/headerInfo
返回结果如下:
4 method属性指定请求类型
我们一般默认的请求方式是Get,但其实也可以有别的请求方式,例如我们用POST去请求接口
@RequestMapping(value="/javaBeanParamPost",method = {RequestMethod.POST}) public void javaBeanParamPost(User user) { System.out.println(user); }
请求的url如下:
http://localhost:8081/base_annotation/user/javaBeanParamPost?id=1&username=tml&age=30&accountIds=50&accountIds=80
返回结果如下:
当然会报错,因为我们方法需要的是POST请求,浏览器请求的是Get,不支持。除了用以上的方式还可以使用如下的注解直接表明方法的属性:
@GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping
5 请求数据乱码问题处理
之前我们遇到乱码的问题都是需要进行手动设置的:
request.setCharacterEncoding("UTF-8");
现在有了框架,整体进行拦截,GET请求框架已经帮我们做好了,针对POST请求我们只需要在web.xml中加入如下配置即可解决乱码问题:
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <!-- 如果有编码格式,设置强制使用上面我们设置的编码格式--> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Controller响应处理
聊完了请求我们再来聊聊响应,我们知道响应返回的一部分是页面,一部分是数据,所以我们分别聊聊数据的返回格式以及页面的跳转方式。
1 返回数据模型格式
我们先重新设置下userInfo.jsp
文件来进行实验,其中Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
<%-- Created by IntelliJ IDEA. User: 13304 Date: 2021/8/31 Time: 11:47 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> modelMap: ${modelMap} <br> model: ${model}<br> modelAndView: ${modelAndView}<br> </body> </html>
1-1 返回ModelAndView
第一种就是我们使用的经典方式ModelAndView:
@RequestMapping("/getUserInfoByModelAndView") public ModelAndView getUserInfoByModelAndView(@RequestParam("name") String username,@RequestParam("age") String age) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("modelAndView", "username: "+username + " age: "+age+" "+LocalDate.now()); modelAndView.setViewName("userInfo"); return modelAndView; }
请求的url为:
http://localhost:8081/base_annotation/user/getUserInfoByModelAndView?name=tml&age=30
返回的结果为:
1-2 返回String类型-通过Model
第二种我们使用Model接口,视图不需要设置:
@RequestMapping("/getUserInfoByStringModel") public String getUserInfo(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username"+username + "age"+age+LocalDate.now()); return "userInfo"; }
请求的url为:
http://localhost:8081/base_annotation/user/getUserInfoByStringModel?name=tml&age=30
返回的结果为:
1-3 返回String类型-通过ModelMap
最后一种我们使用ModelMap:
http://localhost:8081/base_annotation/user/getUserInfoByStringModelMap?name=tml&age=30
请求的url为:
@RequestMapping("/getUserInfoByStringModelMap") public String getUserInfoByStringModelMap(@RequestParam("name") String username,@RequestParam("age") String age, Model model) { model.addAttribute("model", "username: "+username + " age: "+age+" "+LocalDate.now()); return "userInfo"; }
返回的结果为:
除了提到的这几个Model,参数为Map以及List也可以传递,底层就是使用request.setAttribute()在赋值
2 返回页面跳转方式
学习Servlet的时候我们就知道页面的跳转有两种:请求转发和重定向。二者的区别就不再赘述了,在我的另一篇博客中有详细介绍:【Java Web编程 八】深入理解Servlet常用对象,之前是怎么实现的呢?
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //请求转发的写法 RequestDispatcher requestDispatcher=request.getRequestDispatcher("/first.jsp"); requestDispatcher.forward(request,response); //重定向的写法 response.sendRedirect("/first.jsp"); }
那么有了Spring MVC之后又是怎么实现的呢?
2-1 返回到指定页面
首先来看一个标准的返回到指定页面:
@RequestMapping("/getUserInfoByStringModel") public String getUserInfo(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now()); return "userInfo"; }
2-2 请求转发到新页面
然后我们看下请求转发是如何实现的:
@RequestMapping("requestDispatch") public String requestDispatch(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username: "+username + " age: "+age+" "+LocalDate.now()); return "forward:/index.jsp"; }
请求的url为:
http://localhost:8081/base_annotation/user/requestDispatch?name=tml&age=30
返回结果为:
可以看到共享对象被保留在了request的作用域内。
2-3 URL重定向到新页面
我们再来看下重定向如何实现:
@RequestMapping("requestRedirect") public String requestRedirect(@RequestParam("name") String username,@RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username: "+username + " age: "+age+" "+LocalDate.now()); return "redirect:/index.jsp"; }
请求的url为:
http://localhost:8081/base_annotation/user/requestRedirect?name=tml&age=30
返回结果为:
可以看到共享对象由于发了两次请求丢失了。
项目结构及代码清单
整体实践完成后的项目结构如下图所示:
核心的实践代码如下UserController
:
package com.example.base_annotation.controller; import com.example.base_annotation.controller.dto.Account; import com.example.base_annotation.controller.dto.User; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.time.LocalDate; import java.util.Arrays; import java.util.Date; @Controller @RequestMapping("/user") public class UserController { @RequestMapping("requestDispatch") public String requestDispatch(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now()); return "forward:/index.jsp"; } @RequestMapping("requestRedirect") public String requestRedirect(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now()); return "redirect:/index.jsp"; } @RequestMapping("/getUserInfoByStringModel") public String getUserInfo(@RequestParam("name") String username, @RequestParam("age") String age, ModelMap modelMap) { modelMap.addAttribute("modelMap", "username: " + username + " age: " + age + " " + LocalDate.now()); return "userInfo"; } @RequestMapping("/getUserInfoByStringModelMap") public String getUserInfoByStringModelMap(@RequestParam("name") String username, @RequestParam("age") String age, Model model) { model.addAttribute("model", "username: " + username + " age: " + age + " " + LocalDate.now()); return "userInfo"; } @RequestMapping("/getUserInfoByModelAndView") public ModelAndView getUserInfoByModelAndView(@RequestParam("name") String username, @RequestParam("age") String age) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("modelAndView", "username: " + username + " age: " + age + " " + LocalDate.now()); modelAndView.setViewName("userInfo"); return modelAndView; } @RequestMapping("/servletParam") public void servletParam(HttpServletRequest request, HttpServletResponse responser, HttpSession session) { System.out.println(request.getParameter("username")); System.out.println(request); System.out.println(responser); System.out.println(session); } @RequestMapping("/simpleParamMatch") public void simpleParamMatch(String username, int age) { System.out.println("username:" + username); System.out.println("age:" + age); } @RequestMapping("/simpleParamDiff") public void simpleParamDiff(@RequestParam("name") String username, @RequestParam(value = "age") Integer age) { System.out.println("username:" + username); System.out.println("age:" + age); } @RequestMapping("/restfulParam/{id}") public void restfulParam(@PathVariable("id") Long id) { System.out.println("restfulParam参数为" + id); } @RequestMapping("/arrayParam") public void arrayParam(Long[] accountIds) { System.out.println(Arrays.asList(accountIds)); } @RequestMapping("/listParam") public void listParam(User user) { System.out.println(user.getAccountIds()); } @RequestMapping("/dateParam") public void dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) { System.out.println(date); } @RequestMapping("/javaBeanParam") public void javaBeanParam(User user) { System.out.println(user); } @RequestMapping("/javaBeanModelAttributeParam") public String javaBeanModelAttributeParam(@ModelAttribute("userAllies") User user) { System.out.println(user); return "userInfo"; } @RequestMapping("/headerInfo") public void headerInfo(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String cookieName) { System.out.println("User-Agent:" + userAgent); System.out.println("cookieName" + cookieName); } @InitBinder("user") public void initBinderUserType(WebDataBinder binder) { binder.setFieldDefaultPrefix("user."); } @InitBinder("account") public void initBinderCatType(WebDataBinder binder) { binder.setFieldDefaultPrefix("account."); } @RequestMapping("/multiModelParam") public void multiModelParam(User user, Account account) { System.out.println(user); System.out.println(account); } @RequestMapping(value = "/javaBeanParamPost", method = {RequestMethod.POST}) public void javaBeanParamPost(User user) { System.out.println(user); } }
总结一下
整体深入的实践了一遍Spring MVC的控制器,其实就是比照Servlet之前的实现方式进行了一下类比实现,这样可以看出框架的方便之处,为什么说Spring MVC和Spring兼容好呢?还记得学习Spring注解开发时候的Controller么,不就是一种Compnent么。再去从实践的角度看框架,整体上看一个Controller就像我们的某个小模块,这个小模块的各种方法是模块的实现,这和现实也比较契合,每个方法就像之前的每个Servlet里的doGet或doPost,现在我们不需要再为了一个模块写一堆Servlet了,用一个映射的方法代替就行了。再看入参,入参可以统一接受过滤器处理整体解决乱码问题,并且对各种入参类型都有很好的兼容,各种注解代替了我们的各种数据转换实现,给我们省心不少,返回结果页面的显示和跳转也很方便了,使用预置的Model不需要再将对象放到作用域了,因为框架已经替我们做好了,而请求转发和重定向也仅仅只是在返回的字符串上加个单词即可,这就是框架的魅力吧。