0


响应处理-前后端数据交互、内容协商

数据交互浅析:

众所周知,前后端分离项目的前端我们是利用Jquery实现ajax异步请求与后端进行数据交互:意思就是从前端页面中取出数据,请求到对应的controller层方法上,然后后端对前端请求的数据进行业务逻辑处理,最后将对应的状态返回给前端;

HTTPMessageConverter原理:

将你项目中的实体类数据转为JSON数据或者倒转,利用读写方法;

设计restful风格的API,通过json数据进行前后端交互:数据如何解析的?SpringMVC中启动时会自动配置一些HttpMessageConverter,那么什么作用呢?能够支持json数据类型,当后端接收到请求时会判断是否能读,能读则读,返回结果时判断是否能写,能写则写;

public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> var1, @Nullable MediaType var2);

    boolean canWrite(Class<?> var1, @Nullable MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

1、那么问题来了读归读写归写,但是你的数据得转换,那么转换数据是不是得需要Converter,那么在WebMvcConfigurationSupport类中,里面有一个非常关键的方法:addDefaultHttpMessageConverters();

protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
        stringConverter.setWriteAcceptCharset(false);

        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(stringConverter);
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new SourceHttpMessageConverter<Source>());
        messageConverters.add(new AllEncompassingFormHttpMessageConverter());

        if (romePresent) {
            messageConverters.add(new AtomFeedHttpMessageConverter());
            messageConverters.add(new RssChannelHttpMessageConverter());
        }

        if (jackson2XmlPresent) {
            ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build();
            messageConverters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper));
        }
        else if (jaxb2Present) {
            messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
        }

        if (jackson2Present) {
            ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
            messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
        }
        else if (gsonPresent) {
            messageConverters.add(new GsonHttpMessageConverter());
        }
    }

由上述可以知道这是用来判断的,找到数据需要的Converter转换器,如果需要的话,就添加到messageConverter中;这里需要注意当我们配置了自己的MessageConverter时(专门储存Converter的地方),addDefaultHttpMessageConverters()方法就不会被SpringMVC调用;

2、getMessageConverter():用来判断converters转换器是用自定义的还是springMVC默认配置的(这里有点迷,isEmpty是判断转换器中的内容吗?):我的理解是:如果转换器为空(意思就是没东西取了),我们就new一个集合(转换器的),然后配置当前的转换器,(这里应该是我们某个数据需要某个转换器时,如果这个转换器没有,就new一个转换器集合,并配置一个对应这种数据的转换器,将数据转换)——也就是自己的转换器;然后后面判断转换器中内容是否为空,若为空,springMVC就会加载addDefaultHttpMessageConverters(),去获取属性对应的转换器;

protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
            configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
                addDefaultHttpMessageConverters(this.messageConverters);
            }
            extendMessageConverters(this.messageConverters);
        }
        return this.messageConverters;
    }

3、对于MessageConverters的处理:

遍历的每一个converter去比较,直至找到Jackson的处理类型;

// 遍历 messageConverters
for (HttpMessageConverter<?> converter : this.messageConverters) {
    Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
        // 上文类关系图处要重点记住的地方,主要判断 MappingJackson2HttpMessageConverter 是否是 GenericHttpMessageConverter 类型
    if (converter instanceof GenericHttpMessageConverter) {
        GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
        if (genericConverter.canRead(targetType, contextClass, contentType)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
            }
            if (inputMessage.getBody() != null) {
                inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                body = genericConverter.read(targetType, contextClass, inputMessage);
                body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
            }
            else {
                body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
            }
            break;
        }
    }
    else if (targetClass != null) {
        if (converter.canRead(targetClass, contentType)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
            }
            if (inputMessage.getBody() != null) {
                inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
                body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
            }
            else {
                body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
            }
            break;
        }
    }
}

4、然后判断读取操作:能读就读,

protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
        throws IOException
    {
        try (JsonParser p = p0) {
            Object result;
            JsonToken t = _initForReading(p);
            if (t == JsonToken.VALUE_NULL) {
                // Ask JsonDeserializer what 'null value' to use:
                DeserializationContext ctxt = createDeserializationContext(p,
                        getDeserializationConfig());
                result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
            } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
                result = null;
            } else {
                DeserializationConfig cfg = getDeserializationConfig();
                DeserializationContext ctxt = createDeserializationContext(p, cfg);
                JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
                if (cfg.useRootWrapping()) {
                    result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
                } else {
                    result = deser.deserialize(p, ctxt);
                }
                ctxt.checkUnresolvedObjectId();
            }
            // Need to consume the token too
            p.clearCurrentToken();
            return result;
        }
    }

此时,解析已经全部完成,那么还有后端的结果(也就是返回给前端的状态)的过程没有完成;

内容协商:

首先,什么是内容协商?

其实就和之前讲前后端数据交互一样的,这里往上面的接;针对标注了@ResponseBody注解处理的方法,我们的服务器会根据客户端的需要来返回不同格式的数据,比如json、xml...

原理:

1.当浏览器发送请求时,请求头(Request Headers)会携带accept信息:(eg:img/webp,img/*等等)

2.然后springboot就会对controller下的方法进行返回数据类型的处理,那么问题来了,怎么样处理的呢?上面就已经说过了,是通过messageConverter消息转换器进行处理的,messageConverter有默认的所有converter,springboot启动时它就会被加载,从getMessageConveter()可以知道优先自定义的messageConverter,如果有的话就进行读写操作;然后数据类型转换之后,springboot会统计所有转换类型格式;

在这里插入图片描述

3.上述两步可以理解我们获取了两种类型,一种客户端的,一种服务器响应的,然后我们进行嵌套遍历一下,就可以得到二者兼容的类型;

在这里插入图片描述

总的来说:

内容协商就是客户端发送请求我们要哪种格式的数据,同时服务器对自己本身多种响应的格式进行兼容处理,然后根据要求返回对应格式;


本文转载自: https://blog.csdn.net/weixin_57128596/article/details/122759862
版权归原作者 Fairy要carry 所有, 如有侵权,请联系我们删除。

“响应处理-前后端数据交互、内容协商”的评论:

还没有评论