改造springboot支持application/xml请求,并同时处理json和xml入参
需求说明:
由于对接,涉及到请求方使用xml做入参,特在前人的基础上进行整理,如下:
资料来源
1.导包
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.7</version>
</dependency>
- 配置转换器
@Configuration
public class WebConfig implements WebMvcConfigurer {
//省略部分代码
.
.
.
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
builder.indentOutput(true);
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
}
注意:参考资料中使用的是实现WebMvcConfigurationSupport ,但会导致WebMvcConfigurer 的实现被覆盖,同时接口层面入参必须使用@RequestParam才能接收,固此处改成单一实现WebMvcConfigurer
3.封装实体 示例如下(这里的xml没有固定格式开头<?xml……>)
//根节点
@JacksonXmlRootElement(localName = "document")
@Data
@NoArgsConstructor
@AllArgsConstructor
//自定义xml入参
public class CreditLoanApproveAckNotifyDto {
//包含的子节点 使用内部类接收
@JacksonXmlElementWrapper(localName ="request")
private Request request;
@JacksonXmlProperty(localName = "signature")
private String signature;
@Data
//自定义子节点
@NoArgsConstructor
@AllArgsConstructor
public static class Request {
@JacksonXmlElementWrapper(localName ="head")
private Header head;
@JacksonXmlElementWrapper(localName ="body")
private NotifyDomain body;
}
}
资料来源
注意:这里的自定义子节点内部类,必须使用static并加上构造方法才能被jackson实例化,完成解析,否则会提示:can only instantiate non-static inner class by using default, no-argument constructor
4.接口改造
@PostMapping(value = "/test",
consumes = MediaType.APPLICATION_XML_VALUE,
produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public void getCameraImageTest(@RequestBody CreditLoanApproveAckNotifyDto dto) {
这样就完成了对xml入参的支持.
后续:因为是跟设备供应商对接,更操蛋的出现了,参数类型是application/xml,一个接口有两个请求体,分别是不同的功能被设备供应商整合在一起(其中一个是应我们要求后加的),之前版本的设备用的是一个请求体,有一个参数类型可以区分,且是json的,这一版本用xml,后加的功能请求体内部格式是json(直接拿上一版本的请求体,tm都不改一下),供应商说,“你们拦截一下,自己解析不就行了”,*了狗了,改造如下:(仅适用于自行提取的参数较少的场景,如果需要的参数较多和复杂,建议还是让对方改请求比较合适)
1.过滤器+配置类拦截该接口,其中过滤器主要代码如下
private static final String XML_PREFIX = "<";
private static final String JSON_PREFIX = "{";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = null;
if (servletRequest instanceof HttpServletRequest) {
request = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) servletRequest);
}
if (request == null) {
chain.doFilter(servletRequest, servletResponse);
return;
}
String contentType = request.getContentType();
//获取请求参数
String body = getValue(request);
if (StringLocalUtil.isEmpty(body)) {
chain.doFilter(servletRequest, servletResponse);
return;
}
body = body.trim();
Long timeStamp = System.currentTimeMillis() / 1000;
//接收自己解析后的参数
CustomParam customerParam =null;
//转换请求参数为map
Map parameterMap = new HashMap(request.getParameterMap());
if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) {
//人脸请求
if (body.startsWith(XML_PREFIX)) {
parameterMap.put("type", new String[]{"1"});
//自定义解析xml readStringXml()就是一个解析xml的方法
customerParam = readStringXml(body);
} else if (body.startsWith(JSON_PREFIX)) {
body = body.replace("\t", "");
//自定义解析json getCameraByJson()就是一个jackson解析的方法
customerParam = getCameraByJson(body);
parameterMap.put("type", new String[]{"3"});
}
}
if (customerParam != null&&customerParam .getTime()!=null) {
//获取解析后的参数 这是示例而已
timeStamp = customerParam .getTime;
}
//放入参数并重装成新的请求
parameterMap.put("timeStamp", new String[]{timeStamp.toString()});
ParameterRequestWrapper newRequest = new ParameterRequestWrapper(request, parameterMap);
//把请求传递下去
chain.doFilter(newRequest, servletResponse);
}
private static String getValue(HttpServletRequest request) {
String valueStr = "";
try {
StringBuffer sb = new StringBuffer();
InputStream is = request.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String s = "";
while ((s = br.readLine()) != null) {
sb.append(s);
}
valueStr = sb.toString();
} catch (IOException e) {
e.printStackTrace();
valueStr = "";
}
return valueStr;
}
辅助的参数解析:
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
private Map params;
public ParameterRequestWrapper(HttpServletRequest request, Map newParams) {
super(request);
this.params = newParams;
}
@Override
public Map getParameterMap() { return params; }
@Override
public Enumeration getParameterNames() {
Vector l = new Vector(params.keySet());
return l.elements();
}
@Override
public String[] getParameterValues(String name) {
Object v = params.get(name);
if (v == null) {
return null;
} else if (v instanceof String[]) {
return (String[]) v;
} else if (v instanceof String) {
return new String[]{(String) v};
} else {
return new String[]{v.toString()};
}
}
@Override
public String getParameter(String name) {
Object v = params.get(name);
if (v == null) {
return null;
} else if (v instanceof String[]) {
String[] strArr = (String[]) v;
if (strArr.length > 0) {
return strArr[0];
} else {
return null;
}
} else if (v instanceof String) {
return (String) v;
} else {
return v.toString();
}
}
}
原来的接口:(上面的依赖也都去掉了)
@PostMapping(value = "/test")
public void getCameraImageTest(String timeStamp) {……}
结束了……