Spring Boot QuickStart (3) - Web & Restful
环境:Spring Boot 1.5.4
基于 Spring Boot 可以快速创建一个Web & Restful 应用,在开始应用之前,至少要了解以下用法:
定义路由,定义 HTTP 方法
获取Header、GET、POST、路径等参数
Cookie、Session操作
应用一个模板引擎,选择 Thymeleaf
获取表单数据,以及文件上传数据
完成一个登陆、登出、注册流程
增加以下两个依赖即可完成构建:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
注意:当前版本默认选择的 Thymeleaf 是 2.x 版本的,对html 标签闭合性要求比较高,虽然可以通过设置 mode,改变解析方式,但是还要引入额外的 nekoHTML,所以很蛋疼。不过可以切换到 3.x 版本,兼容性未知。
<properties> <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version> </properties>
注解
一旦添加了 spring-boot-starter-web 依赖 Spring Boot 会判断这是一个Web 应用,并启动一个内嵌的Servlet容器(默认是Tomcat)用于处理HTTP请求。
Spring 框架中关于 Web 应用有大量的注解:
@Controller
注解一个 Web 控制器类,框架会将 Servlet 容器里收到的 HTTP 请求根据路径分发给对应的 Controller 类进行处理
@RestController
注解一个 Restful 控制器,默认会自动返回 JSON
@RequestMapping
注解一个路由,如果定义在类上,相当于一个路由组,最终路由是类+方法路由,参数有路由规则和 HTTP 方法,同时还有一些简写形式如:@GetMapping
,@PutMapping
@PathVariable
注解路径参数,如 /user/{id}
@RequestParam
注解请求参数,如 ?user=a 或 post['user'] = a
@RequestBody
注解请求体
@RequestHeader
注解请求header头
@CookieValue
注解一个Cookie值
@SessionAttribute
注解一个Session值
路由,方法
下面代码对应了两个路由:
/
使用 @ResponseBody 注解,所以返回了文本串/hello
返回表示模板的字符串,将会使用 resources/templates/hello.html 模板渲染
@Controller public class IndexController { @RequestMapping("/") @ResponseBody public String index() { return "Spring Boot Index"; } @RequestMapping("/hello") public String hello(Model model) { model.addAttribute("title", "Spring Boot"); return "hello"; } }
这段代码对应了 /rest/ 路由。注意 @RequestMapping 中空字符串与 "/" 的区别。
使用空,则 /rest 与 /rest/ 都可以访问,使用 / 则必须通过 /rest 访问:
@RestController @RequestMapping("/rest") public class RestfulController { @RequestMapping("") public String index() { return "Hello Rest"; } }
hello.html:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title th:text="'Hello, ' + ${title}">title</title> </head> <body> <h1 th:text="'Hello, ' + ${title}">h1</h1> </body> </html>
请求参数
单一参数
@RequestParam
可以用来注解一个请求参数,默认会合并 GET、POST 请求名相同的参数,变成一个数组,所以下面的 text2 会进行数组 -> String 的转型。
@RequestMapping(value = "/get", method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody public String get(@RequestParam("text1") String text1, @RequestParam("text2") String text2) { return text1 + "/" + text2; }
在这里不使用注解同样可以获得数据,那为什么要使用注解呢? 因为 @RequestParam
主要提供了一些额外的功能:
如参数名->变量名的映射,required 检查等,更严谨一些。
@RequestMapping(value = "/getsimple", method = {RequestMethod.GET, RequestMethod.POST}) @ResponseBody public String getSimple(String text1, String text2) { return text1 + "/" + text2; }
参数列表
除了使用 @RequestParam
注解获取单个参数意外,还可以获取一个参数列表,但这种方式,POST 不会合并 GET 中的同名参数:
@RequestMapping("/getmap") @ResponseBody public String getMap(@RequestParam Map<String, Object> gets) { return gets.toString(); }
额外参数列表
除了注解获取数据以外,我们还可以通过向 Controller 中的方法,注入 Servlet 相关的类,来获取一些额外的参数,比如客户端 IP 地址
@RequestMapping("/request") @ResponseBody public String request(HttpServletRequest request) { HashMap<String, String> requests = new HashMap<>(); requests.put("Method", request.getMethod()); requests.put("QueryString", request.getQueryString()); requests.put("RequestURI", request.getRequestURI()); requests.put("getRequestURL", request.getRequestURL().toString()); requests.put("RemoteAddr", request.getRemoteAddr()); return requests.toString(); }
Cookie
Cookie 的操作其实和 Spring 没有太大的关系,不过Spring 提供了一个 @CookieValue
注解用来快速获取 Cookie 值
通过 HttpServletResponse 设置页面的 Cookie:
@RequestMapping("/setcookie") @ResponseBody public String setCookie(HttpServletResponse response) { Cookie cookie1 = new Cookie("cookie1", "value1"); cookie1.setMaxAge(1800); Cookie cookie2 = new Cookie("cookie2", "value2"); cookie2.setMaxAge(3600); response.addCookie(cookie1); response.addCookie(cookie2); return "cookie set ok"; }
通过 HttpServletRequest 或 @CookieValue 注解获取 Cookie:
@RequestMapping("/getcookie") @ResponseBody public String getCookie(HttpServletRequest request, @CookieValue(value = "cookie1", required = false) String cookie1) { HashMap<String, String> map = new HashMap<>(); Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { map.put(cookie.getName(), cookie.getValue()); } } logger.info(cookie1); return map.toString(); }
清空Cookie,就是重新设置cookie的值与过期时间:
@RequestMapping("/delcookie") @ResponseBody public String delCookie(HttpServletRequest request, HttpServletResponse response) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { // setValue只是清空了value,cookie还在 cookie.setValue(null); cookie.setMaxAge(0); response.addCookie(cookie); } } return "delete ok"; }
Session
Session的相关操作,通过 HttpSession、HttpServletRequest、@SessionAttribute 来完成。
设置 Session:
@RequestMapping("/setsession") @ResponseBody public String setSession(HttpSession session) { session.setAttribute("session1", "value1"); session.setAttribute("session2", "value2"); return ""; }
获取Session:
@RequestMapping("/getsession") @ResponseBody public String getSession( HttpServletRequest request, HttpSession httpSession, @SessionAttribute(value = "session1", required = false) String session1) { HttpSession session = request.getSession(); String session2 = (String)session.getAttribute("session2"); String http_session1 = (String)httpSession.getAttribute("session1"); logger.info(http_session1); logger.info(session1); logger.info(session2); HashMap<String, String> sessionMap = new HashMap<>(); Enumeration<String> sessions = session.getAttributeNames(); while(sessions.hasMoreElements()) { String key = sessions.nextElement(); sessionMap.put(key, (String)session.getAttribute(key)); } return sessionMap.toString(); }
删除Session:
@RequestMapping("/delsession") @ResponseBody public String delSession(HttpSession httpSession) { httpSession.removeAttribute("session1"); httpSession.removeAttribute("session2"); return "delete session ok"; }
模板引擎
在模板引擎之前,要了解怎么向模板中传递数据,于是有这三种姿势:
Map,这是一个Java原生类型 ModelMap,这是一个类 Model,这是一个接口,其实现类为 ExtendedModelMap,继承了 ModelMap 类
这三个都可以在方法参数中直接注入使用,暂时不知道这三个有什么区别,用起来差不多。
@RequestMapping("/model") public String model(Model model, ModelMap modelMap, Map<String, Object> map) { model.addAttribute("title1", "model_title"); modelMap.addAttribute("title2", "modelMap_title"); map.put("title2", "map_title"); User user = new User(1, "test"); model.addAttribute("user", user); return "model"; }
除了上面的用法,还可以使用 ModelAndView 手动渲染模板,效果是一样的:
@RequestMapping("/modelandview") public ModelAndView modelAndView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("model"); modelAndView.addObject("title1", "title1"); modelAndView.addObject("title2", "title2"); User user = new User(1, "test"); modelAndView.addObject("user", user); return modelAndView; }
最后 SpringBoot 默认可以集成好几种模板引擎,现在主要使用 thymeleaf。
@ModelAttribute 注解
这个注解有点复杂。可以注解到方法上,也可以注解到方法参数上
注解到方法参数
大概意思是通过模型中获取,这个模型是什么呢?大概通过一些途径(可能来自Session?可能来自请求参数?可能来自控制器注解到方法上的 @ModelAttribute)
完成自动填充,并且自动传递到模板中,这是 Spring MVC 数据绑定。
下面是两个例子:
请求通过 /xxx?id=1&name=张三,能够自动进行映射,并且传到模板中,并且还能自动进行输出格式转换,如下面第一个例子,受 @ResponseBody 影响,直接输出了 JSON
@RequestMapping("/getmodel") @ResponseBody public User getModel(@ModelAttribute User user) { return user; } @RequestMapping("/modelattribute") public String modelAttribute(@ModelAttribute User user) { return "model"; }
注解到方法
将这个控制器的方法,变成一个非请求处理的方法,在其它请求方法(RequestMapping)被调用前首先调用该方法,并将返回的数据放到 Model 中
有什么用呢?
估计是用来生成初始化数据的,比如生成一个带有一些默认数据的表单?在进入控制器之前对数据进行一些整理和清洗?
常用配置
自动 trim 参数
大多数 PHP 框架都有自动 trim GET/POST 参数的功能
在 Spring 里面,可以借助 @InitBinder 注解可以完成这种事情,我们定义一个控制器基类,方便接受请求的控制器继承
public class BaseController { @InitBinder protected void initBinder(WebDataBinder binder) { StringTrimmerEditor stringtrimmer = new StringTrimmerEditor(true); binder.registerCustomEditor(String.class, stringtrimmer); } }
server
server.address 绑定地址
server.port 绑定端口
server.compression.enabled 是否开启压缩
server.compression.mime-types 压缩的类型
server.compression.min-response-size 压缩的阈值
server.tomcat.* access日志,日志目录,线程数等
server.session.cookie.* SessionCookie相关配置