java 报文配置spring_从零搭建Spring Cloud Gateway网关(三)——报文结构转换

本文介绍了如何在Spring Cloud Gateway中实现报文结构转换。通过配置URL和转换规则,利用接口和抽象类处理不同类型的报文,实现了动态修改报文结构的功能。
摘要由CSDN通过智能技术生成

背景

作为网关,有些时候可能报文的结构并不符合前端或者某些服务的需求,或者因为某些原因,其他服务修改报文结构特别麻烦、或者需要修改的地方特别多,这个时候就需要走网关单独转换一次。

实现

话不多说,直接上代码。

首先,我们定义好配置:

package com.lifengdi.gateway.properties.entity;

import lombok.Data;

import org.springframework.util.CollectionUtils;

import java.util.*;

/**

* 需要转换报文结构的URL地址配置类

*

* @author: Li Fengdi

* @date: 2020-7-11 16:57:07

*/

@Data

public class MessageTransformUrl {

// 接口地址,多个地址使用英文逗号分隔

private String[] paths;

/**

*

格式

*

新字段:老字段

*

若新老字段一致,可以只配置新字段

*/

private List fields;

/**

*

返回体类型,默认为json

*

可配置的类型参见{@link com.lifengdi.gateway.enums.TransformContentTypeEnum}

*

如需自定义配置,可以继承{@link com.lifengdi.gateway.transform.AbstractMessageTransform}类,

* 或者实现{@link com.lifengdi.gateway.transform.IMessageTransform}接口类,重写transform方法

*/

private String contentType;

private Set pathList;

public Set getPathList() {

if (CollectionUtils.isEmpty(pathList) && Objects.nonNull(paths)) {

setPathList(new HashSet<>(Arrays.asList(paths)));

}

return pathList;

}

}

package com.lifengdi.gateway.properties;

import com.lifengdi.gateway.properties.entity.MessageTransformUrl;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

import java.util.List;

/**

* 报文结构转换参数配置

* @author: Li Fengdi

* @date: 2020-7-11 16:55:53

*/

@Component

@ConfigurationProperties(prefix = "trans")

@Data

public class MessageTransformProperties {

private List urlList;

}

在yaml文件中的配置如下:

# 报文转换配置

trans:

url-list:

- paths: /jar/api/cockpit

content-type: application/json

fields:

# 新字段:老字段,若新老字段一致,可以只配置新字段

- code:rs

- msg:rsdesp

- data:resultMessage

- paths: /war/api/delivertool

fields:

- code:rs

- msg:rsdesp

- data:resultMessage

这里呢,大家也可以根据需要,放入数据库或者其他可以动态修改的地方,这里只是图方便,所以直接放在yaml文件中。

其次我们定义一个报文转换接口类,方便后续的扩展。这个接口很简单,只有一个transform()方法,主要功能就是转换报文结构。

package com.lifengdi.gateway.transform;

import com.lifengdi.gateway.properties.entity.MessageTransformUrl;

/**

* 报文结构转换接口

*

* @author: Li Fengdi

* @date: 2020-7-11 16:57:07

*/

public interface IMessageTransform {

/**

* 转换报文结构

*

* @param originalContent 需要转换的原始内容

* @param transformUrl MessageTransformUrl

* @return 转换后的结构

*/

String transform(String originalContent, MessageTransformUrl transformUrl);

}

然后我们再增加一个抽象类,这个类主要提供一个解耦的作用,也是为了方便后续进行扩展。

package com.lifengdi.gateway.transform;

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.annotation.Resource;

/**

* 报文转换抽象类

*

* @author: Li Fengdi

* @date: 2020-7-11 16:57:07

*/

public abstract class AbstractMessageTransform implements IMessageTransform {

@Resource

protected ObjectMapper objectMapper;

/**

* ResponseResult转JSON

*

* @param object 需要转换为json的对象

* @return JSON字符串

*/

public String toJsonString(Object object) throws JsonProcessingException {

return objectMapper.writeValueAsString(object);

}

}

这个类非常简单,只有一个toJsonString()方法,主要作用就是将对象转换成json字符串。

接着我们继续来写一个实现类,主要功能就是实现JSON类型的报文的结构转换,如果需要其他类型的报文的同学,可以自定义开发。

package com.lifengdi.gateway.transform.impl;

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.databind.JsonNode;

import com.fasterxml.jackson.databind.node.ObjectNode;

import com.lifengdi.gateway.properties.entity.MessageTransformUrl;

import com.lifengdi.gateway.transform.AbstractMessageTransform;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.StringUtils;

import org.springframework.stereotype.Service;

import java.util.List;

/**

* application/json类型转换实现类

* @author: Li Fengdi

* @date: 2020-7-11 16:57:07

*/

@Service

@Slf4j

public class JsonMessageTransformImpl extends AbstractMessageTransform {

@Override

public String transform(String originalContent, MessageTransformUrl transformUrl) {

if (StringUtils.isBlank(originalContent)) {

return originalContent;

}

try {

// 原始报文转换为JsonNode

JsonNode jsonNode = objectMapper.readTree(originalContent);

List fields = transformUrl.getFields();

// 创建新的JSON对象

ObjectNode rootNode = objectMapper.createObjectNode();

fields.forEach(field -> {

String[] fieldArray = field.split(":");

String newFiled = fieldArray[0];

String oldField = fieldArray.length > 1 ? fieldArray[1] : newFiled;

if (jsonNode.has(oldField)) {

rootNode.set(newFiled, jsonNode.get(oldField));

}

});

return toJsonString(rootNode);

} catch (JsonProcessingException e) {

log.error("application/json类型转换异常,originalContent:{},transformUrl:{}", originalContent, transformUrl);

return originalContent;

}

}

}

这个类继承了AbstractMessageTransform这个类,重写了transform()方法,使用objectMapper、JsonNode、ObjectNode来实现对JSON的解析、转换等工作。

接下来我们定义一个枚举类,方便我们去匹配对应的报文转换实现类。

package com.lifengdi.gateway.enums;

import lombok.Getter;

import org.apache.commons.lang.StringUtils;

import org.springframework.lang.Nullable;

/**

* 报文结构转换转换类型枚举类

*

* @author: Li Fengdi

* @date: 2020-7-11 16:57:07

*/

@Getter

public enum TransformContentTypeEnum {

DEFAULT(null, "jsonMessageTransformImpl")

, APPLICATION_JSON("application/json", "jsonMessageTransformImpl")

;

/**

* 内容类型

*/

private String contentType;

/**

* 报文转换结构实现类

*/

private String transImpl;

TransformContentTypeEnum(String contentType, String transImpl) {

this.contentType = contentType;

this.transImpl = transImpl;

}

/**

* 根据contentType获取对应枚举

*

* 如果contentType为空则返回默认枚举

*

*

* @param contentType contentType

* @return TransformContentTypeEnum

*/

public static TransformContentTypeEnum getWithDefault(@Nullable String contentType) {

if (StringUtils.isNotBlank(contentType)) {

for (TransformContentTypeEnum transformContentTypeEnum : values()) {

if (contentType.equals(transformContentTypeEnum.contentType)) {

return transformContentTypeEnum;

}

}

}

return DEFAULT;

}

}

这个类也很简单,定义枚举,然后一个静态方法,静态方法的作用是根据响应头中的contentType来获取对应的报文结构转换实现类,如果获取不到,则会返回一个默认的实现类,我这里定义的默认的实现类就是我们上边写的JsonMessageTransformImpl类。

最后呢,我们定义一个工厂类,供我们的Filter调用。

package com.lifengdi.gateway.transform;

import com.lifengdi.gateway.enums.TransformContentTypeEnum;

import com.lifengdi.gateway.properties.MessageTransformProperties;

import com.lifengdi.gateway.properties.entity.MessageTransformUrl;

import org.apache.commons.lang.StringUtils;

import org.springframework.stereotype.Component;

import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;

import java.nio.charset.Charset;

import java.util.List;

import java.util.Map;

import java.util.concurrent.atomic.AtomicReference;

/**

* 报文结构转换工厂类

*

* @author: Li Fengdi

* @date: 2020-7-11 16:57:07

*/

@Component

public class MessageTransformFactory {

@Resource

private Map messageTransformMap;

@Resource

private MessageTransformProperties messageTransformProperties;

/**

* 根据contentType获取对应的内容转换实现类

*

* @param contentType 内容类型

* @return 内容转换实现类

*/

private AbstractMessageTransform getMessageTransform(String contentType) {

return messageTransformMap.get(TransformContentTypeEnum.getWithDefault(contentType).getTransImpl());

}

/**

* 报文转换

*

* @param originalContent 原始内容

* @param transformUrl url

* @return 转换后的消息

*/

private String messageTransform(String originalContent, MessageTransformUrl transformUrl) {

String contentType = transformUrl.getContentType();

AbstractMessageTransform messageTransform = getMessageTransform(contentType);

return messageTransform.transform(originalContent, transformUrl);

}

/**

* 判断是否是需要转换报文结构的接口,如果是则转换,否则返回原值

*

* @param path 接口路径

* @param originalContent 原始内容

* @return 转换后的内容

*/

public String compareAndTransform(String path, String originalContent) {

if (StringUtils.isBlank(originalContent)) {

return null;

}

List urlList = messageTransformProperties.getUrlList();

if (CollectionUtils.isEmpty(urlList)) {

return originalContent;

}

return urlList .stream()

.filter(transformUrl -> transformUrl.getPathList().contains(path))

.findFirst()

.map(url -> messageTransform(originalContent, url))

.orElse(originalContent);

}

/**

* 判断是否是需要转换报文结构的接口,如果是则转换,否则返回原值

*

* @param path 接口路径

* @param originalContent 原始内容

* @param originalByteArray 二进制原始内容

* @param charset charset

* @param newResponseBody 新报文内容

* @return 响应体数组数组

*/

public byte[] compareAndTransform(String path, String originalContent, byte[] originalByteArray, Charset charset,

AtomicReference newResponseBody) {

if (StringUtils.isBlank(originalContent)) {

return null;

}

List urlList = messageTransformProperties.getUrlList();

if (CollectionUtils.isEmpty(urlList)) {

return originalByteArray;

}

return urlList.stream()

.filter(transformUrl -> transformUrl.getPathList().contains(path))

.findFirst()

.map(url -> {

String messageTransform = messageTransform(originalContent, url);

if (originalContent.equals(messageTransform)) {

return originalByteArray;

}

newResponseBody.set(messageTransform);

return messageTransform.getBytes(charset);

})

.orElse(originalByteArray);

}

}

这个工厂对外提供的方法只有compareAndTransform()两个方法,主要功能就是判断是否是需要转换报文结构的接口,如果是则转换,否则返回原值。

接下来就是在我们的Filter调用即可。调用代码如下:

content = messageTransformFactory.compareAndTransform(path, responseBody, content, charset, newResponseBody);

最后

上面的只是简单的示例,很多情况都没有考虑进去,大家借鉴即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值