SpringBoot 一篇搞定(Cookie Session 跳转 内容协商 converter解析器 thymeleaf)
Posted HUTEROX
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 一篇搞定(Cookie Session 跳转 内容协商 converter解析器 thymeleaf)相关的知识,希望对你有一定的参考价值。
文章目录
到这一步假设我要处理一些请求过来的特殊参数,或者像Django那样后面的操作,那么我就需要用到这个比较高级的用法,用到servlet提供的一些API接口,例如前面的HttpServletRequest等等。
Cookie处理
设置cookie
这个玩意的话还是很简单的而且昨天的博客也说了怎么玩。
@GetMapping("/change-username")
public String setCookie(HttpServletResponse response) {
// 创建一个 cookie对象
Cookie cookie = new Cookie("username", "Jovan");
cookie.setMaxAge(7 * 24 * 60 * 60); // 7天过期
cookie.setSecure(true); //Https 安全cookie 只通过https传输
//将cookie对象加入response响应
response.addCookie(cookie);
return "Username is changed!";
}
获取cookie
@GetMapping("/all-cookies")
public String readAllCookies(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
return Arrays.stream(cookies)
.map(c -> c.getName() + "=" + c.getValue())
.collect(Collectors.joining(", "));
}
return "No cookies";
}
删除Cookie
若要删除cookie,请将Max-Age指令设置为0并取消其值的设置。您还必须传递用于设置它的相同的其他cookie属性。不要将Max-Age指令值设置为-1。否则,浏览器将把它视为会话cookie。
换句话来说
你的一个cookie是这样的
Cookie cookie = new Cookie("username", "Jovan");
cookie.setMaxAge(7 * 24 * 60 * 60); // 7天过期
cookie.setSecure(true); //Https 安全cookie 只通过https传输
现在删除,你要这样做
Cookie cookie = new Cookie("username",null);
cookie.setMaxAge(0);
cookie.setSecure(true); //Https 安全cookie 只通过https传输
response.addCookie(cookie);
小结
这里的话主要就是用了servlet里面的一些原生的API去做,一个是HttpServletRequest还有就是Response。
Session处理
这个Session是啥我想都不用多说了,这个也是需要利用到Cookie的玩意。那么在SpringBoot里面设置也非常简单。
首先HttpSession是我们的Session对象。
这么用呢也相当简单
@Controller
public class IndexController {
@GetMapping("/index1")
public String index1(HttpSession session){
session.setAttribute("name","Huterox");
return "redirect:/index2";
}
@GetMapping("/index2")
@ResponseBody
public String index2(HttpSession session){
String name = (String) session.getAttribute("name");
return name;
}
}
这样一来就是先了session处理。
作用域
而且这里都记住这个Session和request里面的参数都是由作用域的,request的作用域:是从当前请求到服务器处理请求结束,包括服务器转发到内部的资源路径,也同样可以访问到request中的内容;
所以如果要从当前资源转发到其他资源中,还需要共享数组,就可以使用request.setAttribute(“名称”,Object obj)共享数据;
另外Session的作用域是一次会话,从打开浏览器到关闭浏览器之前,都可以访问到Session中的数据;作用域更大;
如果是重定向,就不能访问到Request域中的共享数据;可以使用session作用域;
Request参数设置
@Controller
public class IndexController {
@GetMapping("/index1")
public String index1(HttpSession session, HttpServletRequest request){
session.setAttribute("name","Huterox");
request.setAttribute("world","Hello world");
return "redirect:/index2";
}
@GetMapping("/index2")
@ResponseBody
public String index2(HttpSession session,HttpServletRequest request){
String name = (String) session.getAttribute("name");
String world = (String) request.getAttribute("world");
return name+world;
}
}
这里大概率你得到的值一定是
Huteroxnull
原因很简单我这里用到是跳转,前面说了它们之间是由作用域的!redirect是跳转,意味着当前的请求处理完了那么进入了下一个请求处理,那么这个时候自然作用不了了。
那么在这里的话我们
return “forward:/index2”;
向前就好了。
这个也是servlet里面进行跳转的方式。
页面跳转
首先这个方式有很多最简单的方式就是
return "forward:/xxx";
或者
return "redirect:/xxx";
但是二者是有区别。
forward的意思是向前的、(按新地址)转寄、促进、前锋的意思,而redirect的意思是改变方向、重新寄送。
这个英文意思基本上就是在Spring当作的意思。
通过Response
我们也可以通过这个来搞定
@GetMapping("/index1")
public void index1(HttpSession session, HttpServletResponse response) throws IOException {
session.setAttribute("name","Huterox");
response.sendRedirect("/index2");
// return "redirect:/index2";
}
但是这样方式有作用域的问题,要么你用session搞定,或者放行,这边可以使用request放行可以保证作用域参数丢失的问题。
通过Request
request.getRequestDispatcher("/index2").forward(request,response);
不过这里也是只用
内容协商
首先:
内容协商的核心之一就是,(Converter)类型转换器!这玩应通过反射等手段实现自动类型的加载转换,同时结合不同的依赖实现不同的类型转换。这一点在实际的操作过程当中相当重要这就意味着可以实现一S端(server)对应不同的客户端
这是很不错的点感觉除了数据库操作麻烦一点其他的要完爆Django
Json返回
首先SpringBoot由于自己在搭建start-web场景的时候自己自带了一个Json的数据转换,所以的话默认在Spring Boot里面就是返回一个Json对象的。
@GetMapping("/jsonget")
@ResponseBody
public String index2(){
User user = new User();
return user;
}
}
XML 返回
这边我们需要先导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
之后我们的服务器后根据客户端请求头
Accept: text/css,*/*;q=0.1
这个字段的要求返回对应的参数。也就是说如果目标客户端需要服务端返回xml那么现在,你只需要搞好这个配置,然后啥也不用管了。
基本原理说明
-
1、判断当前响应头中是否已经有确定的媒体类型。MediaType
-
2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)
-
3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
-
4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。
-
5、客户端需要【application/xml】。服务端能力【json、xml,】
-
6、进行内容协商的最佳匹配媒体类型
-
7、用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
自定义类型转换器
通过前面的原理我们大致知道了其核心其实就是converter所以我们可以自己定义一个。
由于这一步是SpringMVC的活,所以我梦要想要实现这样的效果,自定义MVC的内容,那么我们就必须在我们的配置类里面这样搞
在配置类当作添加 WebMvcConfigurer
之后我们重写一些WebMvcConfigurer里面的一些方法。
那么现在先自定义返回类型
/**
* 自定义的Converter
*/
public class HMessageConverter implements HttpMessageConverter<Person> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Person.class);
}
/**
* 服务器要统计所有MessageConverter都能写出哪些内容类型
* <p>
* application/x-Huterox
*
* @return
*/
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-Huterox");
//类型,自定义的类型名称
}
@Override
public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//自定义协议数据的写出
String data = person.getUserName() + ";" + person.getAge() + ";" + person.getBirth();
//写出去
OutputStream body = outputMessage.getBody();
body.write(data.getBytes());
}
}
重写方法
@Configuration(proxyBeanMethods = false)
public class WebConfig /*implements WebMvcConfigurer*/ {
//WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new HMessageConverter());
}
}
}
通过特定参数获取返回类型
在这里面除了浏览器自带的参数接受形式外,SpringBoot还支持带参数返回,例如
127.0.0.1:8000/index?format=xml
不过在这里需要先开启这个功能
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
现在我们想让我们自己定义的类型也能够通过参数获取,那么同样的我们现在也需要重写。
@Configuration(proxyBeanMethods = false)
public class WebConfig /*implements WebMvcConfigurer*/ {
//WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new HMessageConverter());
}
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//Map<String, MediaType> mediaTypes
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json", MediaType.APPLICATION_JSON);
mediaTypes.put("xml", MediaType.APPLICATION_XML);
mediaTypes.put("H", MediaType.parseMediaType("application/x-Huterox"));
//指定支持解析哪些参数对应的哪些媒体类型
ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
// parameterStrategy.setParameterName("ff");改变参数名字 ?ff=xml
HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(parameterStrategy, headeStrategy));
}
}
这样一来就完成了完整的功能。
参数解析
这个也是很好玩很厉害的东西。
举个例子就是昨天的。
@RestController
public class HelloController {
@PostMapping("/hello")
public String hello(User user) {
return user.toString();
}
}
根据获取的参数SpringBoot会自动封装一个指定的对象。
原理解析
一说到这个又是涉及到自定义的问题了。
首先参数返回到springboot后,准确的来说是springMVC 先进入它的参数解析器-HandlerMethodArgumentResolver
-
HandlerMapping中找到能处理请求的Handler(Controller.method())
-
为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
-
适配器执行目标方法并确定方法参数的每一个值
之后参数解析器-HandlerMethodArgumentResolver
确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
- 当前解析器是否支持解析这种参数
- 支持就调用 resolveArgument
这样一来就完成了对对象的封装。
所以这里面的核心还是说,返回的参数格式里面和我们定义的Bean里面 的属性是否对得到,OK才能进行一系列的操作。
示例
例如我这里有两个Bean
@Data
public class Person {
private String userName;
private Integer age;
private Date birth;
private Pet pet;
}
@Data
public class Pet {
private String name;
private Integer age;
}
现在有这样的表单
<form action="/saveuser" method="post">
姓名: <input name="userName" value="zhangsan"/> <br/>
年龄: <input name="age" value="18"/> <br/>
生日: <input name="birth" value="2019/12/10"/> <br/>
宠物姓名:<input name="pet.name" value="阿猫"/><br/>
宠物年龄:<input name="pet.age" value="5"/>
<input type="submit" value="保存"/>
</form>
@PostMapping("/GetPerson")
public Person saveuser(Person person) {
return person;
}
现在ok
自定义解析器
现在我把表单这样改一下
<form action="/saveuser" method="post">
姓名: <input name="userName" value="zhangsan"/> <br/>
年龄: <input name="age" value="18"/> <br/>
生日: <input name="birth" value="2019/12/10"/> <br/>
宠物: <input name="pet" value="啊猫,3"/>
<input type="submit" value="保存"/>
</form>
显然虽然有pet但是没有具体的映射关系,解析器解析不了。
那么在这里也是我们是定制MVC里面的东西我们需要进入到前面的webconfigurer里面去
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
// 啊猫,3
if (!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;
}
});
}
};
}
}
这样一来就可以解析pet了,因为pet这玩意他是找得到的,只是没法解析。
视图处理
现在终于到了万众瞩目的示图了,看到了熟悉的template这个文件夹
那么同样在这边处理视图的话需要用到对应的引擎,进行解析。这个我们这边先使用的还是
thymeleaf
那么对于视图的处理分一下流程。
1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
**3、任何目标方法执行完成以后都会返回 ModelAndView(**数据和视图地址)。
4.processDispatchResult 处理派发结果(页面改如何响应)
使用thymeleaf
以上是关于SpringBoot 一篇搞定(Cookie Session 跳转 内容协商 converter解析器 thymeleaf)的主要内容,如果未能解决你的问题,请参考以下文章
第二十一篇Flowable之SpringBoot集成FlowableUI