1.概述
HTTP(HyperText Transfer Protocol),超文本传输协议。超文本(Hypertext)是一种结构化的文本,其中包含了超链接(Hyperlink)的能力,通过超链接可以在不同文档之间创建关联和跳转。
传统的文本是线性的,按照一定的顺序排列的,而超文本则打破了线性结构,允许文本中的某些词、短语或图像与其他文档或资源之间建立关联。这些关联通过超链接来实现,用户可以点击超链接来跳转到其他相关的文档、网页、图片、视频或其他媒体资源。
超文本的一个重要特征是非线性性,因为用户可以根据自己的兴趣和需要自由地跳转和浏览相关内容。这种非线性的特性使得超文本成为了构建互联网和万维网(World Wide Web)的基础,为用户提供了丰富的浏览和导航体验。
在整个超文本的来回转跳中,所要传输的数据是五花八门的,文字、图片、视频、音频等等,所以在报文中一定要有一个地方来声明所传输的数据的编码格式,这样才能在收到数据后正确的进行解析。在HTTP报文中,负载该功能的是请求头中的Content-Type属性。其结构如下:
content-type:主题类型;字符编码
content-type:application/json; charset=UTF-8
博主在前面的文章中详细讲解过HTTP的报文结构,不熟悉的同学可以会看一下,很清晰易懂:
HTTP、HTTPS__BugMan的博客-CSDN博客
由于数据类型的五花八门,每一种数据类型都有单独对应的content-type,所以content-type的类型也五花八门、数量众多,有上百种,以下举例一些类型:
文本类型:
text/plain:纯文本
text/html:HTML 文档
text/css:CSS 样式表
text/javascript:JavaScript 脚本
应用程序类型:
application/json:JSON 数据
application/xml:XML 数据
application/pdf:PDF 文档
application/octet-stream:二进制数据流
application/x-www-form-urlencoded:URL 编码的表单数据
application/zip:ZIP 压缩文件
application/x-gzip:GZIP 压缩文件
图片类型:
image/jpeg:JPEG 图像
image/png:PNG 图像
image/gif:GIF 图像
image/svg+xml:SVG 图像
音频/视频类型:
audio/mpeg:MP3 音频
video/mp4:MP4 视频
video/mpeg:MPEG 视频
2.常用类型
由于content-type类型众多,本文只挑几个开发中经常使用的类型来介绍.
2.1.application/x-www-form-urllencoded
application/x-www-form-urllencoded,HTML 表单默认的编码方式,之所以用这种方式而不用json是因为json的数据结构可能会很复杂,需要额外的解析动作。x-www-form-urllencoded中数据以键值对的形式进行编码,并使用特定的字符集进行转义和编码。具体的编码规则如下:
键值对之间使用等号(=)连接,例如:key=value
不同的键值对之间使用与号(&)进行分隔,例如:key1=value1&key2=value2
特殊字符进行转义编码,转义编码使用百分号(%)和两位十六进制表示字符的 ASCII 值,例如:空格编码为 %20,加号编码为 %2B
以下是一个数据示例:
name=John%20Doe&age=25&city=New%2BYork
在上述示例中,有三个键值对:name=John Doe,age=25,city=New+York
HTTP报文如下:
2.2.application/json
application/json,以json格式传输数据。
HTML示例:
HTTP报文如下:
3.Spring MVC支持的编码
3.1.实验
建一个controller:
用form-data传:
能收到数据:
用传
也能收到
用application/json传:
收不到:
3.2.适配器
之所以有上面实验中的情况,是因为Spring MVC 提供了适配器(HttpMessageConverter)来处理不同的请求主体数据编码格式。这些适配器能够自动解析请求主体数据并将其转换为方法参数或对象。发送 POST 请求时,无论使用 form-data 还是 x-www-form-urlencoded 编码格式,Spring MVC 都能够根据请求头中的 Content-Type 自动选择适当的适配器进行解析,并将数据传递给对应的 Controller 方法。
但是如果是传的json或者xml格式的数据的话,spring mvc虽然也准备了对应的适配器,但是并不会直接进行数据的转换而是需要配合@RequestBody注解来声明将复杂结构的结构化数据绑定到实体上:
很多同学会在这里有所疑惑,既然报文里Content-Type已经声明了数据类型,如json这一类复杂的结构化数据为什么不直接转换喃?还要配合@RequestBody来使用。
这其实只是Spring MVC在设计上的一种取舍,其实用COntent-Type来进行识别转换是可行的,这里专门推出注解,是因为使用注解来清晰的显示这个数据接收的是复杂的结构化数据仅此而已。只能说spring mvc选择了这种设计,其实只用content-type进行判断是可以通吃所有情况的。
3.3.自定义适配器
Content-Type数据众多,Spring MVC自带的适配器肯定是无法完全覆盖的,当遇到没有覆盖的情况,可以通过自定义适配器的方式来自定义参数解析逻辑,灵活应对一切情况。
自定义适配器:
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class CustomMessageConverter extends AbstractHttpMessageConverter<CustomObject> { public CustomMessageConverter() { super(MediaType.APPLICATION_CUSTOM); // 自定义的 Content-Type } @Override protected boolean supports(Class<?> clazz) { return CustomObject.class.isAssignableFrom(clazz); } @Override protected CustomObject readInternal(Class<? extends CustomObject> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), StandardCharsets.UTF_8); // 手动解析请求主体内容并转换为 CustomObject 对象 // 这里假设请求主体内容是 JSON 格式 // 使用你喜欢的 JSON 解析库进行解析,比如 Jackson、Gson 等 CustomObject customObject = YourJsonParser.parse(reader, CustomObject.class); return customObject; } @Override protected void writeInternal(CustomObject customObject, HttpOutputMessage outputMessage) throws IOException { // 实现将 CustomObject 对象转换为响应主体内容的逻辑 // 略 } }
注册:
import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 添加自定义的消息转换器 converters.add(new CustomMessageConverter()); } }