【踩过的坑】Spring响应数据封装统一对象返回,调用方报空指针

背景:Spring版本4.3.22;微服务A提供接口供其他服务调用,奇葩点:A服务响应封装了统一对象返回,对象如下:

@Getter
@Setter
@NoArgsConstructor
public class Response {
    /** 响应状态码 **/
    private Integer code;

    /** 异常消息 **/
    private String message;

    /** 请求流水号 **/
    private String requestNo;

    /** 响应数据 **/
    private Object data;

    /** 业务码 **/
    private String bizCode;

    public Response(String requestNo, Integer code, Object data) {
        this.code = code;
        this.data = data;
        this.requestNo = requestNo;
    }

    public Response(String requestNo, Integer code, String bizCode, Object data) {
        this.code = code;
        this.data = data;
        this.bizCode = bizCode;
        this.requestNo = requestNo;
    }
}

封装响应对象关键代码:

public class CustomResponseMessageConverter extends MappingJackson2HttpMessageConverter {

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        if (object instanceof Response) {
            super.writeInternal(object, type, outputMessage);
        } else {
            checkRequestId();
            super.writeInternal(new Response(RequestIdUtils.getRequestId().toString(), HttpStatus.OK.value(), object), type, outputMessage);
        }
    }

    private void checkRequestId() {
        UUID uuid = RequestIdUtils.getRequestId();
        if (uuid == null) {
            RequestIdUtils.generateRequestId();
        }
    }
}

配置不是本文都重点(省略@configuration配置)

通俗的说:统一封装一个对象(上面的response)响应给调用方,调用方收到对象在解码出(实际用到有效数据为Response对象中的data)觉得多此一举?说实话我也觉得没必要封装这一统一对象,完全没必要。

正常情况

响应对象(自定义对象、Integer、Boolean、int、boolean等)都会封装统一Response对象返回

原因是调用了自定义的CustomResponseMessageConverter进行响应封装,外层调用可以正常解析,不会出现空指针

出现的问题:

微服务提供的接口返回String类型时,其实是没有封装统一Response对象返回,外层调用报空指针

问题出现在哪里?稍微熟悉Spring的都知道Spring封装了很多xxxConverter用来处理转化,

扫描二维码关注公众号,回复: 11112465 查看本文章

有个例外:当响应类型是String时调用的converter是:org.springframework.http.converter.StringHttpMessageConverter

并不是org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

来看看StringHttpMessageConverter相关源码

	@Override
	protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
		if (this.writeAcceptCharset) {
			outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
		}
		Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
		StreamUtils.copy(str, charset, outputMessage.getBody());
	}

直接将响应的字符串拷贝到“输出流”,并没有调用自定义的CustomResponseMessageConverter,这也就是为什么没有封装统一的response对象返回的原因。

好了问题找到了,给出解决思路:

1.不直接返回String,进行一次封装(封装之后已不是String对象,会调用自定义Converter)【不建议采用

2.自定义一个Converter继承StringHttpMessageConverter重写writeInternal()方法,【不建议采用,麻烦

这样做之后你会发现仍然错误,因为spring记录接口返回字符串的长度,最终会进行截取(虽然重写writeInternal()方法封装了Response,但是最终被截取了);因此还需要重写getContentLength()方法,调用方需要重写decode方法

写在最后,强力建议微服务架构中,各个中间服务提供者不要封装统一响应对象,当其中一些微服务没有封装统一响应对象返回(即直接返回响应),一些服务又统一封装响应对象时,处理起来很棘(e)手(xin),深恶痛绝此做法!!!

发布了43 篇原创文章 · 获赞 13 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_41070393/article/details/91433728