Spring boot(4) web开发(2) 请求处理、响应处理、模板引擎
参照教程:https://www.bilibili.com/video/BV19K4y1L7MT
1.请求映射
1.1 rest使用与原理
1.1.1 使用
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
- 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- 核心Filter;HiddenHttpMethodFilter
用法: 表单method=post,隐藏域 _method=put
SpringBoot中手动开启
spring:mvc:hiddenmethod:filter:enabled: true
Controller:
@RestController
public class HelloController {@GetMapping("/user")public String getUser(){return "GET-张三";}@PostMapping("/user")public String saveUser(){return "POST-张三";}@PutMapping("/user")public String putUser(){return "PUT-张三";}@DeleteMapping("/user")public String deleteUser(){return "DELETE-张三";}}
测试页面:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>测试</title>
</head>
<body>
<h1>测试REST风格</h1>
<form method="post" action="/user"><input type="submit" value="POST"></form>
<form method="get" action="/user"><input type="submit" value="GET"></form>
<form method="post" action="/user"><input type="hidden" name="_method" value="put"><input type="submit" value="PUT">
</form>
<form method="post" action="/user"><input type="hidden" name="_method" value="delete"><input type="submit" value="DELETE">
</form>
</body>
</html>
自定义把_method 这个名字换成我们自己喜欢的:自定义HiddenHttpMethodFilter
@Configuration
public class WebConfiguration {@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();hiddenHttpMethodFilter.setMethodParam("_m");return hiddenHttpMethodFilter;}
}
1.1.2 原理
Rest原理(表单提交要使用REST的时候)
表单提交会带上**_method=PUT**
请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到**_method**的值。
- 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的.
为什么需要手动开启,以及可以自己配置HiddenHttpMethodFilter?
为什么拦截的是_method?
为什么表单请求方式需要是POST?
Rest使用客户端工具,
- 如PostMan直接发送Put、delete等方式请求,无需Filter
1.2 请求映射原理
FrameworkServlet继承HttpServlet并重写了doGet()方法:
而doGet()最终还是要执行processRequest()这个方法,而processRequest()内部的核心方法是doService():
doService()是一个抽象方法:
protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
在DispatcherServlet中有doService()的实现,而doService()中的核心是调用本类的doDispatch()方法,所以研究doDispatch()方法是最终的方法
对Spring MVC功能分析从DispatcherServlet的doDispatch()方法开始:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Object dispatchException = null;try {processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;//找到当前请求使用哪个Handler(Controller的方法)处理mappedHandler = this.getHandler(processedRequest);//HandlerMapping:处理器映射。/xxx->>xxxx
打断点,发送请求并进入这个方法:
可以看到,这个方法内部是将所有的handlerMappings用迭代器进行匹配:
这些handlerMappings有(index页发送的是Get请求):
进入第一个mapping,我们可以看到RequestMapping中有我们在控制器写的各种处理请求的方法
进入getHandler()方法,可以看到调用了getHandlerInternal()来处理:
进入getHandlerInternal()方法,可以看到调用了父类的getHandlerInternal()方法,再次打断点
在这个方法里再进行找路径以及请求方法:
总结:所有的请求映射都在HandlerMapping中。
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
- 如果有就找到这个请求对应的handler
- 如果没有就是下一个 HandlerMapping
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
2.普通参数与基本注解
2.1 注解
@PathVariable、@RequesHeader
@GetMapping("/car/id/{id}/owner/{owner}")public Map<String, Object> getCar(@PathVariable("id") Integer id,@PathVariable("owner") String owner,@PathVariable Map<String, String> kv,@RequestHeader("host") String host,@RequestHeader Map<String, String> headers){Map map = new HashMap();map.put("id", id);map.put("owner", owner);map.put("kv", kv);map.put("host", host);map.put("headers", headers);return map;}//前端页面:<a href="/car/id/11/owner/lisi">测试@PathVariable和@RequestHeader</a><br/>
@RequestParam、@CookieValue
@GetMapping("/person")public Map<String, Object> getInfo(@RequestParam("id") Integer id, @RequestParam Map<String, String> info,@CookieValue("_ga") Cookie cookie, @CookieValue("_ga") String _ga){Map map = new HashMap();map.put("id", id);map.put("info", info);System.out.println(cookie.getName() + "---->");return map;}//<a href="/person?id=12&username=zhangsan&age=18">测试@RequestParam</a><br/>
@RequestBody
post方法请求才有请求体:
@PostMapping("/save")public Map postMethod(@RequestBody String content){Map<String,Object> map = new HashMap<>();map.put("content",content);return map;}<form action="/save" method="post">姓名: <input type="text" name="username"><br/>密码: <input type="text" name="password"><br/><input type="submit" value="submit">
</form>
@RequestAttribute
获取请求域的值
@Controller
public class RequestTestController {@GetMapping("/goto")public String goToPage(HttpServletRequest request){request.setAttribute("username", "zhangsan");request.setAttribute("password", 1234567);return "forward:/success";}@ResponseBody@GetMapping("/success")public Map success(@RequestAttribute("username") String username,@RequestAttribute("password") Integer password,HttpServletRequest request){Map map = new HashMap();map.put("username", username);map.put("password", password);map.put("username_re", request.getAttribute("username"));map.put("password_re", request.getAttribute("password"));return map;}
}
@MatrixVariable与UrlPathHelper
矩阵变量:
配置:
@Configuration
public class WebConfiguration implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}
}
或者:
@Configuration
public class WebConfiguration{@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer(){@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}};}
}
测试:
//1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd//2、SpringBoot默认是禁用了矩阵变量的功能// 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。// removeSemicolonContent(移除分号内容)支持矩阵变量的//3、矩阵变量必须有url路径变量才能被解析@GetMapping("/cars/{path}/{info}")public Map carsSell(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,@PathVariable("path") String path,@MatrixVariable("price") Integer price){Map<String,Object> map = new HashMap<>();map.put("low",low);map.put("brand",brand);map.put("path",path);map.put("price", price);return map;}// /boss/1;age=20/2;age=10@GetMapping("/boss/{bossId}/{empId}")public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){Map<String,Object> map = new HashMap<>();map.put("bossAge",bossAge);map.put("empAge",empAge);return map;}
2.2 Servlet API
WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
2.3 复杂参数
Map、**Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、**Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
Map<String,Object> map, Model model, HttpServletRequest request 都是可以给request域中放数据,
request.getAttribute();
2.4 自定义参数
可以自动类型转换与格式化,可以级联封装。
/*** 姓名: <input name="userName"/> <br/>* 年龄: <input name="age"/> <br/>* 生日: <input name="birth"/> <br/>* 宠物姓名:<input name="pet.name"/><br/>* 宠物年龄:<input name="pet.age"/>*/
@Data
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;}@Data
public class Pet {private String name;private String age;}
自定义converter
//1、WebMvcConfigurer定制化SpringMVC的功能@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Pet>() {@Overridepublic Pet convert(String source) {// 啊猫,3if(!StringUtils.isEmpty(source)){Pet pet = new Pet();String[] split = source.split(",");pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}};}
3.响应处理
3.1 响应json
jackson.jar+@ResponseBody
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
web场景自动引入了json场景<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope></dependency>
给前端自动返回json数据
@ResponseBody@GetMapping("/pet")public Pet getPet(Pet pet){return pet;}<form action="/pet" method="get">姓名:<input type="text" name="name" value="cat"><br/>年龄:<input type="text" name="age" value="18"><br/><input type="submit" value="submit">
</form>
支持的返回值类型
ModelAndView
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;
3.2 内容协商
根据客户端接收能力不同,返回不同媒体类型的数据
3.2.1 引入xml依赖
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>
3.2.2 使用postman测试接受不同类型的数据
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
xml:
json:
4.模板引擎thymeleaf
4.1 thymeleaf简介
Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.
现代化、服务端Java模板引擎
4.2 基本语法
1.表达式
2.字面量
文本值: ‘one text’ , ‘Another one!’ **,…**数字: 0 , 34 , 3.0 , 12.3 **,…**布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格
3.文本操作
- 字符串拼接: +
- 变量替换: |The name is ${name}|
4.数学运算
运算符: + , - , * , / , %
5.布尔运算
运算符: and , or
一元运算: ! , not
6.比较运算
比较: > , < , >= , <= ( gt , lt , ge , le **)**等式: == , != ( eq , ne )
7.条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
8.特殊操作
无操作: _
4.3 设置属性值-th:attr
设置单个值
<form action="subscribe.html" th:attr="action=@{/subscribe}"><fieldset><input type="text" name="email" /><input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/></fieldset>
</form>
设置多个值
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
替代写法:
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">
4.4 迭代
<tr th:each="prod : ${prods}"><td th:text="${prod.name}">Onions</td><td th:text="${prod.price}">2.41</td><td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"><td th:text="${prod.name}">Onions</td><td th:text="${prod.price}">2.41</td><td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
4.5 条件运算
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<div th:switch="${user.role}"><p th:case="'admin'">User is an administrator</p><p th:case="#{roles.manager}">User is a manager</p><p th:case="*">User is some other thing</p>
</div>
4.6 属性优先级
4.7 thymeleaf使用
1.引入starter
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
2.自动配好了thymeleaf
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration { }
自动配好的策略
- 1、所有thymeleaf的配置值都在 ThymeleafProperties
- 2、配置好了 SpringTemplateEngine
- 3、配好了 ThymeleafViewResolver
- 4、我们只需要直接开发页面
public static final String DEFAULT_PREFIX = "classpath:/templates/";public static final String DEFAULT_SUFFIX = ".html"; //xxx.html
3.页面开发
html引入命名空间: xmlns:th=“http://www.thymeleaf.org”
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1 th:text="${msg}">哈哈</h1>
<h2><a href="www.atguigu.com" th:href="${link}">去百度</a> <br/><a href="www.atguigu.com" th:href="@{link}">去百度2</a>
</h2>
</body>
</html>
4.8 应用
1.路径构建
<form class="form-signin" action="index.html" method="post" th:action="@{/login}">
这是前端登录页面的表格,点击提交后会请求到相应的controller来处理:
@PostMapping("/login")public String main(User user, HttpSession session, Model model){String username = user.getUsername();String password = user.getPassword();if("deserts".equals(username) && "123".equals(password)){session.setAttribute("user", user);return "redirect:/main.html";}else{model.addAttribute("msg", "账号或者密码错误");return "login";}}@GetMapping("main.html")public String mainPage(HttpSession session, Model model){Object user = session.getAttribute("user");if (user != null){return "main";}else{model.addAttribute("msg", "请重新登录");return "login";}}
直接写入标签内的文本:
<a href="#">[[${session.user.username}]]</a>
2.错误回显
前面处理的用户登录的controller在用户登录失败或着没登录直接访问main页面时,应该返回登录页及给出提示信息,通过model设置request域的值,从前端页面取出来,达到回显的效果
<label style="color:green;" th:text="${msg}"/>
3.抽取重复页面
这里主要参照官网,区分三种抽取的方式。抽取的片段:
<footer th:fragment="copy">© 2011 The Good Thymes Virtual Grocery
</footer
页面引入抽取片段的三种方式:
<body>...<div th:insert="footer :: copy"></div><div th:replace="footer :: copy"></div><div th:include="footer :: copy"></div></body>
会导致的结果:
<body>...<div><footer>© 2011 The Good Thymes Virtual Grocery</footer></div><footer>© 2011 The Good Thymes Virtual Grocery</footer><div>© 2011 The Good Thymes Virtual Grocery</div></body>
区别:insert是插入原引入标签的内部,replace是将原标签给替换成碎片部分的标签和内容,include是去掉标签将碎片内容包含在原标签中。
4.数据渲染(遍历)
controller:
@GetMapping("/dynamic_table")public String dynamic_table(Model model){List<User> users = Arrays.asList(new User("aaa", "111"), new User("bbb", "222"),new User("ccc", "333"), new User("ddd", "444"));model.addAttribute("users", users);return "table/dynamic_table";}
页面:
<table class="display table table-bordered" id="hidden-table-info"><thead><tr><th class="hidden-phone">#</th><th class="hidden-phone">用户名</th><th class="hidden-phone">密码</th></tr></thead><tbody><tr class="gradeX" th:each="user,status:${users}"><td class="hidden-phone" th:text="${status.count}"></td><td class="center hidden-phone">[[${user.username}]]</td><td class="center hidden-phone" th:text="${user.password}"></td></tr></tbody></table>
效果:
Spring boot(4) web开发(2) 请求处理、响应处理、模板引擎相关推荐
- Spring Boot(5) web开发(3)拦截器、文件上传、异常处理
Spring Boot(5) web开发(3)拦截器.文件上传.异常处理 学习视频: https://www.bilibili.com/video/BV19K4y1L7MT?p=49&spm_ ...
- Spring Boot之Web开发
Web开发 简单了解 快速上手 JSON 的支持 RESTful 的支持 支持热部署 快速上手 拦截器与过滤器 自定义过滤器 示例 自定义拦截器 示例 打包部署 多环境配置 服务器配置 项目打包 简单 ...
- Spring Boot与web开发
Spring Boot与web开发 1. 2. 3. 4. 5. 6. 7. 8. 9. 0. 四.Web开发 1.简介 使用SpringBoot: 1).创建SpringBoot应用,选中我们需要的 ...
- 【spring boot】第3篇:spring boot 进行 web 开发
spring boot 对 web 静态资源的管理 经过前两篇文章我们知道spring boot最大的功能就是自动配置.主要是通过以下两个类实现该功能: xxxxAutoConfiguration : ...
- Gradle进阶:1: 结合spring boot进行web开发
在前面的文章中介绍了如何使用gradle的基础知识,这篇文章在某种程度上才是真正意义上的第一个hello world,这里使用一个简单的spring boot的例子,通过gradle进行编译和构建以及 ...
- spring boot 整合web开发之文件上传、静态资源访问、异常处理、返回JSON数据
目录 springboot 整合web开发 返回json数据 静态资源访问 文件上传 全局异常 1.返回json数据 springboot默认的是jackson-databind做为json处理器.也 ...
- Spring Boot与Web开发简介||SpringBoot对静态资源的映射规则
Web开发 1.简介 使用SpringBoot: 1).创建SpringBoot应用,选中我们需要的模块: 2).SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运 ...
- SpringBoot资料合集-04【Spring Boot与Web开发】
1.SpringMVC快速使用 1.基于restful http接口 的CURD 2.调用rest http接口 3.通过postman调用 4.通过swagger调用 2.SpringMVC自动配置 ...
- Spring boot的Web开发
开发步骤 第一步,创建SpringBoot应用,选中我们需要的模块 第二步,SpringBoot已经默认,将这些场景配置好了 只需要在配置文件中,指定少量配置就可以运行起来 第三步,自己编写业务代码 ...
- spring boot 整合web开发(二)
目录 自定义错误页 CORS支持(前端跨域请求) 拦截器HandlerInterceptor 启动系统任务 springboot配置AOP 整合servlet.filter.listener 下图为本 ...
最新文章
- OEM 按钮乱码解决办法
- 30万奖金等你拿,“信也科技杯”第五届数据解决方案应用大赛火热报名中!...
- 百度地图标点点击变色_《和平精英》版本爆料第三弹:雪地洞穴开启!组队标点功能升级~...
- 山东专升本access知识点_全国各省份每年的专升本考试大纲啥时候公布?考纲公布之前你该做什么?...
- java调用存储过程同时获取[返回参数]和[结果集]
- 黑科技揭秘:如何通过阿里云超算,使得汽车仿真效率提升25%
- ASCII码从小到大排序(字典序)
- php上传完没进度条_php使用APC实现实时上传进度条功能
- 用于PC的骁龙1000近似实锤了,高通员工LinkedIn泄露天机
- 使用 Repeater方式和完全静态页面使用AJAX读取和提交数据
- Spark Structured Steaming 聚合、watermark 以及 window操作,结合输出模式的研究总结
- [ThinkPHP]打开页面追踪调试
- 第一章ASP程序设计概述
- Android常用的工具方法
- Java实现anagram算法
- 云:SD-WAN不断增长的驱动力
- STM32F427利用FSMC接口访问FPGA的SRAM(3)—— STM32F427访问FPGA的SRAM
- python不同方式爬qq音乐(get)及网易云(post)某歌手单曲歌名
- Nginx面试题(史上最全 + 持续更新)
- Android开发真实谎言:个人无空间nbsp;…
热门文章
- MATLAB图像处理之图像边缘提取
- linux如何判断网线插入_快速鉴定网线质量的常用方法
- Linux中MongoDB创建数据库
- Spring Data Jpa 报错org.hibernate.LazyInitializationException: could not initialize proxy
- IDEA中Maven项目中界面右边的Maven Projects中子项目出现灰色
- C语言求超级素数的个数,求一个输出a-b的超级素数程序(c)
- python怎么保存为nii文件_Ubuntu+python将nii图像保存成png格式
- MySQL5.7收集表统计信息时对该表上增删改查的影响
- Oracle中字符串转义问题总结
- css未生效,css不生效是什么原因