Spring MVC Content Negotiation – Spring MVC内容协商

最后修改: 2016年 3月 14日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article describes how to implement content negotiation in a Spring MVC project.

本文介绍了如何在一个Spring MVC项目中实现内容协商。

Generally, there are three options to determine the media type of a request:

一般来说,有三个选项来决定一个请求的媒体类型。

  • (Deprecated) Using URL suffixes (extensions) in the request (eg .xml/.json)
  • Using URL parameter in the request (eg ?format=json)
  • Using Accept header in the request

By default, this is the order in which the Spring content negotiation manager will try to use these three strategies. And if none of these are enabled, we can specify a fallback to a default content type.

默认情况下,这是Spring内容协商管理器将尝试使用这三种策略的顺序。如果这些都没有启用,我们可以指定回退到一个默认的内容类型。

2. Content Negotiation Strategies

2.内容谈判策略

Let’s start with the necessary dependencies – we are working with JSON and XML representations, so for this article, we’ll use Jackson for JSON:

让我们从必要的依赖性开始–我们正在处理JSON和XML表示法,所以在这篇文章中,我们将使用Jackson处理JSON。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.2</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.2</version>
</dependency>

For XML support, we can use either JAXB, XStream, or the newer Jackson-XML support.

对于XML支持,我们可以使用JAXB、XStream或较新的Jackson-XML支持。

Since we have explained the use of the Accept header in an earlier article on HttpMessageConverters, let’s focus on the first two strategies in depth.

由于我们已经在一篇关于HttpMessageConverters的早期文章中解释了Accept头的使用,让我们重点深入探讨前两种策略。

3. The URL Suffix Strategy

3.URL后缀策略

With the Spring Boot 2.6.x version, the default strategy for matching request paths against registered Spring MVC handler mappings has changed from AntPathMatcher to PathPatternParser.

在Spring Boot 2.6.x版本中,针对注册的Spring MVC处理程序映射匹配请求路径的默认策略已经从AntPathMatcher变为PathPatternParser

And as suffix pattern matching is not supported by PathPatternParser, we first need to use the legacy path matcher before making the use of this strategy.

由于PathPatternParser不支持后缀模式匹配,我们首先需要使用传统的路径匹配器,然后再使用这一策略。

We can add spring.mvc.pathmatch.matching-strategy in application.properties file to switch the default back to AntPathMatcher.

我们可以在application.properties文件中添加spring.mvc.pathmatch.match-strategy,将默认值调回AntPathMatcher

By default, this strategy is disabled, we need to enable this by setting spring.mvc.pathmatch.use-suffix-pattern to true in application.properties:

默认情况下,这个策略是禁用的,我们需要在application.properties中把spring.mvc.pathmatch.use-suffix-pattern设置为true来启用它。

spring.mvc.pathmatch.use-suffix-pattern=true
spring.mvc.pathmatch.matching-strategy=ant-path-matcher

Once enabled, the framework can check for a path extension right from the URL to determine the output content type.

一旦启用,框架可以直接从URL检查路径扩展,以确定输出内容类型。

Before going into configurations, let’s have a quick look at an example. We have the following simple API method implementation in a typical Spring controller:

在讨论配置之前,让我们先看看一个例子。在一个典型的Spring控制器中,我们有以下简单的API方法实现。

@RequestMapping(
  value = "/employee/{id}", 
  produces = { "application/json", "application/xml" }, 
  method = RequestMethod.GET)
public @ResponseBody Employee getEmployeeById(@PathVariable long id) {
    return employeeMap.get(id);
}

Let’s invoke it by making use of the JSON extension to specify the media type of the resource:

让我们通过利用JSON扩展指定资源的媒体类型来调用它。

curl http://localhost:8080/spring-mvc-basics/employee/10.json

Here’s what we might get back if we use a JSON extension:

下面是如果我们使用JSON扩展可能得到的结果。

{
    "id": 10,
    "name": "Test Employee",
    "contactNumber": "999-999-9999"
}

And here’s what the request-response will look like with XML:

下面是用XML表示的请求-响应的情况。

curl http://localhost:8080/spring-mvc-basics/employee/10.xml

The response body:

响应机构。

<employee>
    <contactNumber>999-999-9999</contactNumber>
    <id>10</id>
    <name>Test Employee</name>
</employee>

Now, if we do not use any extension or use one that is not configured, the default content type will be returned:

现在,如果我们不使用任何扩展名或使用未配置的扩展名,将返回默认内容类型。

curl http://localhost:8080/spring-mvc-basics/employee/10

Let’s now have a look at setting up this strategy – with both Java and XML configurations.

现在让我们来看看如何设置这个策略–用Java和XML配置。

3.1. Java Configuration

3.1. Java配置

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(true).
    favorParameter(false).
    ignoreAcceptHeader(true).
    useJaf(false).
    defaultContentType(MediaType.APPLICATION_JSON); 
}

Let’s go over the details.

让我们来看看细节。

First, we’re enabling the path extensions strategy. It’s also worth mentioning that as of Spring Framework 5.2.4, the favorPathExtension(boolean) method is deprecated in order to discourage the use of path extensions for content negotiations.

首先,我们要启用路径扩展策略。还值得一提的是,从Spring Framework 5.2.4开始,favorPathExtension(boolean) 方法已被废弃,以便不鼓励在内容协商中使用路径扩展。

Then, we’re disabling the URL parameter strategy as well as the Accept header strategy – because we want to only rely on the path extension way of determining the type of the content.

然后,我们要禁用URL参数策略以及Accept头策略–因为我们想只依靠路径扩展方式来确定内容的类型。

We’re then turning off the Java Activation Framework; JAF can be used as a fallback mechanism to select the output format if the incoming request doesn’t match any of the strategies we configured. We’re disabling it because we’re going to configure JSON as the default content type. Please note that the useJaf() method is deprecated as of Spring Framework 5.

然后我们要关闭Java激活框架;如果传入的请求不符合我们配置的任何策略,JAF可以作为一种回退机制来选择输出格式。我们关闭它是因为我们要把JSON配置为默认的内容类型。请注意,useJaf()方法从Spring Framework 5开始被废弃

And finally – we are setting up JSON to be the default. That means if none of the two strategies are matched, all incoming requests will be mapped to a controller method that serves JSON.

最后,我们将JSON设置为默认值。这意味着如果两个策略都不匹配,所有传入的请求将被映射到一个提供JSON的控制器方法。

3.2. XML Configuration

3.2.XML配置

Let’s also have a quick look at the same exact configuration, only using XML:

让我们也快速看一下同样的配置,只是使用XML。

<bean id="contentNegotiationManager" 
  class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="true" />
    <property name="favorParameter" value="false"/>
    <property name="ignoreAcceptHeader" value="true" />
    <property name="defaultContentType" value="application/json" />
    <property name="useJaf" value="false" />
</bean>

4. The URL Parameter Strategy

4.URL参数策略

We’ve used path extensions in the previous section – let’s now set up Spring MVC to make use of a path parameter.

我们已经在上一节中使用了路径扩展–现在让我们设置Spring MVC来利用路径参数。

We can enable this strategy by setting the value of the favorParameter property to true.

我们可以通过将favorParameter属性的值设置为true来启用这一策略。

Let’s have a quick look at how that would work with our previous example:

让我们快速看一下这将如何与我们之前的例子一起工作。

curl http://localhost:8080/spring-mvc-basics/employee/10?mediaType=json

And here’s what the JSON response body will be:

而这里是JSON响应体的内容。

{
    "id": 10,
    "name": "Test Employee",
    "contactNumber": "999-999-9999"
}

If we use the XML parameter, the output will be in XML form:

如果我们使用XML参数,输出将是XML形式的。

curl http://localhost:8080/spring-mvc-basics/employee/10?mediaType=xml

The response body:

响应机构。

<employee>
    <contactNumber>999-999-9999</contactNumber>
    <id>10</id>
    <name>Test Employee</name>
</employee>

Now let’s do the configuration – again, first using Java and then XML.

现在我们来做配置–同样,首先使用Java,然后是XML。

4.1. Java Configuration

4.1. Java配置

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(false).
    favorParameter(true).
    parameterName("mediaType").
    ignoreAcceptHeader(true).
    useJaf(false).
    defaultContentType(MediaType.APPLICATION_JSON).
    mediaType("xml", MediaType.APPLICATION_XML). 
    mediaType("json", MediaType.APPLICATION_JSON); 
}

Let’s read through this configuration.

让我们读一读这个配置。

First, of course, the path extension and the Accept header strategies are disabled (as well as JAF).

首先,当然,路径扩展和Accept头策略被禁用(以及JAF)。

The rest of the configuration is the same.

其余的配置都是一样的。

4.2. XML Configuration

4.2.XML配置

<bean id="contentNegotiationManager" 
  class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false" />
    <property name="favorParameter" value="true"/>
    <property name="parameterName" value="mediaType"/>
    <property name="ignoreAcceptHeader" value="true" />
    <property name="defaultContentType" value="application/json" />
    <property name="useJaf" value="false" />

    <property name="mediaTypes">
        <map>
            <entry key="json" value="application/json" />
            <entry key="xml" value="application/xml" />
        </map>
    </property>
</bean>

Also, we can have both strategies (extension and parameter) enabled at the same time:

另外,我们可以同时启用两种策略(扩展和参数)

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(true).
    favorParameter(true).
    parameterName("mediaType").
    ignoreAcceptHeader(true).
    useJaf(false).
    defaultContentType(MediaType.APPLICATION_JSON).
    mediaType("xml", MediaType.APPLICATION_XML). 
    mediaType("json", MediaType.APPLICATION_JSON); 
}

In this case, Spring will look for path extension first, if that is not present then will look for path parameter. And if both of these are not available in the input request, then the default content type will be returned back.

在这种情况下,Spring将首先寻找路径扩展,如果不存在,那么将寻找路径参数。如果这两个参数在输入请求中都不存在,那么默认的内容类型将被返回。

5. The Accept Header Strategy

5.接受标头策略

If the Accept header is enabled, Spring MVC will look for its value in the incoming request to determine the representation type.

如果Accept头被启用,Spring MVC将在传入的请求中寻找其值以确定表示类型。

We have to set the value of ignoreAcceptHeader to false to enable this approach and we’re disabling the other two strategies just so that we know we’re only relying on the Accept header.

我们必须将ignoreAcceptHeader的值设置为false来启用这种方法,我们禁用其他两种策略,只是为了让我们知道我们只依赖Accept头。

5.1. Java Configuration

5.1.Java配置[/strong]

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(true).
    favorParameter(false).
    parameterName("mediaType").
    ignoreAcceptHeader(false).
    useJaf(false).
    defaultContentType(MediaType.APPLICATION_JSON).
    mediaType("xml", MediaType.APPLICATION_XML). 
    mediaType("json", MediaType.APPLICATION_JSON); 
}

5.2. XML Configuration

5.2.XML配置

<bean id="contentNegotiationManager" 
  class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="true" />
    <property name="favorParameter" value="false"/>
    <property name="parameterName" value="mediaType"/>
    <property name="ignoreAcceptHeader" value="false" />
    <property name="defaultContentType" value="application/json" />
    <property name="useJaf" value="false" />

    <property name="mediaTypes">
        <map>
            <entry key="json" value="application/json" />
            <entry key="xml" value="application/xml" />
        </map>
    </property>
</bean>

Finally, we need to switch on the content negotiation manager by plug-in it into the overall configuration:

最后,我们需要将内容协商管理器插入到整个配置中,从而打开它。

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />

6. Conclusion

6.结论

And we’re done. We looked at how content negotiation works in Spring MVC and we focused on a few examples of setting that up to use various strategies to determine the content type.

然后我们就完成了。我们研究了内容协商在Spring MVC中是如何工作的,我们着重介绍了几个设置的例子,以使用各种策略来确定内容类型。

The full implementation of this article can be found over on GitHub.

本文的完整实现可以在GitHub上找到over