Java系列-SpringBoot-1-入参及出参扩展XML支持

Java系列-SpringBoot-1-入参及出参扩展XML支持

前言

现在Java后端项目基本都使用json格式请求和响应数据,因此一般的配置返回json格式数据便能够满足需求。但仍有一些场景需要支持xml格式的请求和响应数据,针对此类需求,
本文在SpringBoot框架上进行配置说明。

注:原spring mvc使用xml配置也支持此配置。

环境

  • SpringBoot 版本:2.2.8.RELEASE

相关知识点

Http请求Header中Accept和Content-Type的区别

Http报头分为通用报头,请求报头,响应报头和实体报头。

请求方的http报头结构:通用报头|请求报头|实体报头

响应方的http报头结构:通用报头|响应报头|实体报头

  • Accept:属于请求头,代表发送端(客户端)希望接受的数据类型。
    • application/xml
    • application/json;java枚举:MediaType
  • Content-Type:代表发送端(客户端|服务器)发送的实体数据的数据类型
    • application/xml
    • application/json;java枚举:MediaType

常见的媒体格式类型如下:

  • text/html : HTML格式
  • text/plain :纯文本格式
  • text/xml : XML格式
  • image/gif :gif图片格式
  • image/jpeg :jpg图片格式
  • image/png:png图片格式
  • multipart/form-data : 表单中进行文件上传时使用该格式

以application开头的媒体格式类型:

  • application/xhtml+xml :XHTML格式
  • application/xml : XML数据格式
  • application/atom+xml :Atom XML聚合格式
  • application/json : JSON数据格式
  • application/pdf :pdf格式
  • application/msword : Word文档格式
  • application/octet-stream : 二进制流数据(如常见的文件下载)
  • application/x-www-form-urlencoded :
    中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

Spring中consumes和produces的区别

SpringMvc中如@RequestMapping之类的注解(@PostMapping等)有consumesproduces这两个属性;

  • consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
  • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
  • params: 指定request中必须包含某些参数值是,才让该方法处理
  • headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/api/v1")
public class ApiTestController {
@PostMapping(value = "/pay",
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public ApiResponse<TestResponse> testPay(@Validated @RequestBody TestRequest request) {
log.info("测试post方法:【{}】", JSON.toJSONString(request));
TestResponse response = TestResponse.builder()
.field1("field1")
.field2("field2")
.field3("field3")
.build();
return ApiResponse.ok("请求成功", "1234", "业务成功", response);
}
}

SpringMVC输出格式

在SpringMVC使用ContentNegotiation来判断用户请求希望得到什么格式的数据。

ContentNegotiation 设置了三种方式来设置相应数据的格式,优先级由上至下:

  • 通过请求URL后缀:/list.html 返回html格式;
  • 通过请求的参数:/list?format=html 该设置默认不开启,默认key是format。
  • 通过HTTP Header的Accept:Accept:application/xml

本文在springboot中配置扩展xml格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// SpringMvc中配置(如需修改默认配置,在SpringBoot中也可这样配置)
// ApiMessageConverterConfig extends WebMvcConfigurerAdapter
@Configuration
public class ApiMessageConverterConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
// 是否通过请求Url的扩展名来决定media type,默认为false(不支持),扩展名指的xxx.json、xxx.xml等形式
.favorPathExtension(true)
// favorParameter:这里是是否启用参数支持,默认是false(不支持);例如 /user/{userid}?format=json
.favorParameter(true)
.parameterName("mediaType")
// 不检查Accept请求头 ,默认是false(不忽略),即请求时指定的contentType:application/json等
.ignoreAcceptHeader(false)
/* 设置默认的media type */
.defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
// mediaTypes:配置扩展名到mimeType的映射,json和xml的映射;
.mediaType("xml", MediaType.APPLICATION_XML)
.mediaType("html", MediaType.TEXT_HTML)
.mediaType("json", MediaType.APPLICATION_JSON);
}
}

pom.xml中引入依赖:

1
2
3
4
5
<!-- 加入jackson-dataformat-xml依赖,Spring Boot就会自动引入MappingJackson2XmlHttpMessageConverter的实现:-->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

请求实体类配置,需要使用上述依赖中@JacksonXmlRootElement@JacksonXmlProperty注解:
注:xml格式的必须指定根节点;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JacksonXmlRootElement(localName = "request")
public class TestRequest extends ApiPayV1Request<TestResponse> implements Serializable {
private static final long serialVersionUID = -3135169148798610260L;

@JacksonXmlProperty(localName = "field1")
private String field1;
private String field2;
private String field3;

@Override
public Class<TestResponse> getResponseClass() {
return TestResponse.class;
}
}

响应实体类配置,同上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@JacksonXmlRootElement(localName = "root")
public class ApiResponse<T> implements Serializable {
private static final long serialVersionUID = -3798634638472229369L;

/**
* 是否成功
*/
@JacksonXmlProperty(localName = "success")
private boolean success;
/**
* 返回状态编码
*/
@JacksonXmlProperty(localName = "code")
private String code;
/**
* 返回状态描述
*/
@JacksonXmlProperty(localName = "message")
private String message;
/**
* 返回业务执行状态码
* 需要特殊处理的
*/
@JacksonXmlProperty(localName = "sub_code")
private String sub_code;
/**
* 返回业务执行状态描述
* 需要特殊处理的
*/
@JacksonXmlProperty(localName = "sub_msg")
private String sub_msg;
/**
* 接口调用时间
*/
@JacksonXmlProperty(localName = "time")
@JsonFormat(pattern = "yyyyMMddHHmmssSSS", timezone = "GMT+8")
private Timestamp time;
/**
* 接口返回数据
*/
@JacksonXmlProperty(localName = "data")
private T data;

/**
* 接口签名
*/
@JacksonXmlProperty(localName = "sign")
private String sign;

}

参考资料