首页 > 其他分享 >SpringMVC之内容协商策略

SpringMVC之内容协商策略

时间:2022-10-06 15:11:06浏览次数:52  
标签:请求 SpringMVC 协商 类型 数据格式 public 服务端 策略

内容协商原理

目录

一、引言

所谓的内容协商原理,就是客户端想要发送什么样数据格式的数据,期望服务端返回什么样数据格式的数据。

常用方式:

1、通过请求头中的Content-Type字段告知服务端,本次发送给服务端的是什么类型的数据格式;

2、通过Accept格式告知服务端,服务端需要响应给客户端的数据格式。

双方做了规范,所以就有了内容协商的产生。

二、正常请求

如果是在浏览器上发送的请求,那么对于Accept字段来说,是无法选择的;但是对于一些工具来说,如Postman、APIPost等

可以在发送请求的时候选择:请求方式、携带参数、请求头、请求格式类型等等

适用于精准匹配类型,springmvc中可以让后端开发人员和前端开发人员规定好。

请求数据格式确定和返回值数据格式确定

在请求达到DispatcherServlet的时候,首先需要找到解析出来的RequestMappingInfo信息

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

在这个方法中,会来确定返回值数据格式

request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);

因为RequestMappingInfoHandlerMapping中重写了handleMatch方法

将后端响应的返回值处理信息存储到reqeust作用域对象中,在后面做内容协商的时候会直接从request作用域中获取得到服务端产生的数据格式

if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
    Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
    request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}

但是对于请求的数据格式来说,这里是没有做任何处理的。因为springmvc利用内容协商管理策略可以从请求头、参数、路径中获取得到请求的数据格式信息。

三、内容协商

下面以解析@ResponseBody为例来进行分析:

可以看到获取得到客户端能够接收到的数据格式和服务端返回的数据格式

确定客户端接收数据格式

List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);

利用内容协商管理器来解析请求希望返回的数据格式类型

private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
    throws HttpMediaTypeNotAcceptableException {

    return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
}

基于内容协商策略来确定,当前的请求的数据内容格式

留给了开发者扩展方式,期望开发者给出请求的内容数据格式

看一下接口实现体系

springmvc中提供了基于参数、路径、请求头的几种方式来确定请求的数据格式,而在springmvc中,默认的是基于请求头的方式来确定请求数据内容格式。因为内容协商策略中什么都不添加,那么默认添加的是基于请求头来确定客户端请求的数据格式。

默认基于请求头确定请求数据格式

那么看一下服务端默认基于请求头来确定客户端请求的数据类型格式

可以看到这正是基于Accept请求头获取得到媒体类型,然后按照权重来进行排序,将排序好了媒体类型进行返回。

确定服务端返回数据格式

从上面可看到,可以直接从请求作用域对象中获取得到服务端返回的媒体类型,然后就直接返回。

而不需要再次遍历消息转换器来确定每个消息转换器支持的数据类型。这样子做就相当于是提高响应性能。

选择最佳匹配

得到了客户端的请求类型和服务端能够产生的数据类型,那么接下来得到最佳匹配类型,然后利用消息转换器将对象以最佳匹配格式类型写出去。

选择最佳匹配并进行排序。从这里可以看到如果服务端确定了服务端能够产生的数据类型,那么这里将会减少匹配次数。

如果没有匹配到,那么就说明,不支持该种媒体类型。

将数据写出

1、首先判断是否是GenericHttpMessageConverter;

2、然后再次判断能够将该类型的对象以指定的媒体类型写出去;

3、判断成立,就直接进行写出操作;

四、自定义内容协商

上面提到过,可以使用不同的方式来确定请求的媒体类型:参数、请求头、路径等

那么基于请求头的方式来进行确定

但是首先有一点,需要在配置文件中来进行配置一下:

spring:
  application:
    name: springboot-negotiation-mvc
  mvc:
    contentnegotiation:
      # 开启基于请求参数的内容协商策略。默认携带参数:format
      favor-parameter: true

注:这个format参数开发人员也可以自定义

顺便引入另外一种数据格式:xml

如果是基于请求头的方式,因为xml的权重高于json,所以浏览器不会响应json,而是xml。

但是现在通过基于参数的方式,可以实现自定义化操作。

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

根据上面的源码分析,如果开启了基于参数确定请求的媒体类型,那么应该首先确定基于参数的请求类型的方式

public class ParameterContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy {
	
    // 默认参数是format
	private String parameterName = "format";


	/**
	 * Create an instance with the given map of file extensions and media types.
	 */
	public ParameterContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
		super(mediaTypes);
	}

	// 可以自定义设置
	/**
	 * Set the name of the parameter to use to determine requested media types.
	 * <p>By default this is set to {@code "format"}.
	 */
	public void setParameterName(String parameterName) {
		Assert.notNull(parameterName, "'parameterName' is required");
		this.parameterName = parameterName;
	}

	public String getParameterName() {
		return this.parameterName;
	}
	
	// 可以获取得到请求参数携带的值
	@Override
	@Nullable
	protected String getMediaTypeKey(NativeWebRequest request) {
		return request.getParameter(getParameterName());
	}

}

而在构造函数中,看看看看对应的处理方式

	public MappingMediaTypeFileExtensionResolver(@Nullable Map<String, MediaType> mediaTypes) {
		if (mediaTypes != null) {
			Set<String> allFileExtensions = new HashSet<>(mediaTypes.size());
			mediaTypes.forEach((extension, mediaType) -> {
				String lowerCaseExtension = extension.toLowerCase(Locale.ENGLISH);
				this.mediaTypes.put(lowerCaseExtension, mediaType);
				addFileExtension(mediaType, lowerCaseExtension);
				allFileExtensions.add(lowerCaseExtension);
			});
			this.allFileExtensions.addAll(allFileExtensions);
		}
	}

无非是将能够产生的媒体类型保存起来而已,在用到的时候将存入进去的取出来而已

下面就是要来编写,消息转换器,支持将对应的媒体类型写出的操作

public class PersonHttpMessageConverter implements HttpMessageConverter<User> {
    @Override
    public boolean canRead(Class clazz, MediaType mediaType) {
        return false;
    }

    /**
     * 这里分为了两个地方来进行使用!
     * 第一次使用:判断能不能写,这里的媒体类型为null;
     * 第二次使用:能不能写出User这种数据类型,以自定义的方式写出
     *
     * @param clazz
     * @param mediaType
     * @return
     */
    @Override
    public boolean canWrite(Class clazz, MediaType mediaType) {
        // 如果返回值类型是这种类型的就可以写出去
        return clazz.isAssignableFrom(User.class);
    }

    /**
     * 当前的消息转换器能够支持的媒体类型,在需要写的时候可以获取得到对应的类型
     *
     * @return
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        // 支持的媒体类型
        return MediaType.parseMediaTypes("application/lg");
    }

    @Override
    public void write(User user, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        StringJoiner stringJoiner = new StringJoiner(";");
        String resultData = stringJoiner.add(user.getId().toString()).add(user.getName()).toString();
        OutputStream body = outputMessage.getBody();
        body.write(resultData.getBytes(StandardCharsets.UTF_8));
    }

    @Override
    public User read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }


}

然后需要将该类型匹配到web容器中来:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new PersonHttpMessageConverter());
    }


    /**
     * @param configurer
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
        Map<String, MediaType> mediaTypeMap = new HashMap<>();
        mediaTypeMap.put("lg", MediaType.parseMediaType("application/lg"));
        mediaTypeMap.put("xml", MediaType.parseMediaType("application/xml"));
        mediaTypeMap.put("json", MediaType.parseMediaType("application/json"));
        ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(mediaTypeMap);
        // 开启请求头参数
        configurer.favorParameter(true);
        configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy, headerContentNegotiationStrategy));
    }
}

总结

1、首先通过@ReqeustMapping中的produces确定服务端产生类型;
2、通过内容协商策略判断客户端的请求数据内容格式;
3、得到最佳匹配媒体类型;
4、调用消息转换器找到能够将类型写出的转换器,然后将其写出;

标签:请求,SpringMVC,协商,类型,数据格式,public,服务端,策略
From: https://www.cnblogs.com/likeguang/p/16757663.html

相关文章

  • 玩转华为ENSP模拟器系列 | 两个网关之间通过IKE方式协商IPSec VPN隧道(采用预共享密钥
    素材来源:华为防火墙配置指南一边学习一边整理试验笔记,并与大家分享,侵权即删,谢谢支持!附上汇总贴:​​玩转华为ENSP模拟器系列|合集_COCOgsta的博客-CSDN博客_华为模拟器实验......
  • 招聘策略
    /*题目第一行整数表示招聘部门个数deptNum,1<=deptNum<=10随后deptNum行依次表示每个部门的要求,记录于deptDemands数组中,deptDemands[i]表示变化为i的部门的目......
  • SpringMVC笔记
    一、SpringMVC简介1、什么是MVCMVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分M:Model,模型层,指工程中的JavaBean,作用是处理数据JavaBean分为两类:一类称......
  • 2022最新SpringMVC面试题附完整答案
    SpringMVC面试题一、单选题1.下列关于SpringMVC说法正确的是BA.SpringMVC和Spring没有关系B.SpringMVC是一个控制层框架,复制接收和处理请求C.SpringMVC可以脱离Spring单独......
  • 算法导论(第4章 分治策略)
    目录4.1最大子数组问题暴力求解方法问题变换使用分治策略的求解方法分治算法的分析4.2矩阵乘法的Strassen算法一个简单的分治算法Strassen方法4.3用代入法求解递归式做......
  • SpringMvc拦截器
                   ......
  • 面试官:Redis 过期删除策略和内存淘汰策略有什么区别?
    作者:小林coding​大家好,我是小林。Redis的「内存淘汰策略」和「过期删除策略」,很多小伙伴容易混淆,这两个机制虽然都是做删除的操作,但是触发的条件和使用的策略都是不同的。......
  • 【操作系统-内存】页面分配策略和页面置换算法
    目录0基本概念1页面分配策略1.1页面分配的策略1.2页面置换的策略1.3分配和置换的策略组合2页面调入策略2.1页面调入的时机2.2页面调入的位置3页面置换算法3.1最......
  • 彻底搞懂 Http 缓存策略,切记死背概念! 错误点
    https://juejin.cn/post/6907592506779631623 又查了查,似乎是chromium版本更新,把一些策略改了,这里写的一些是老版本的情况 起码在chrome上,有很多地方不对......
  • 不相交数据结构中的启发式策略
    目录启发式策略(heuristic)元启发式策略(metaheuristic)启发式策略(heuristic)是一类在求解某个具体问题时,在可以接受的时间和空间内能给出其可行解,但又不保证求得最优解(以及可......