Spring Boot 找不到 XML 响应的可接受表示

Posted

技术标签:

【中文标题】Spring Boot 找不到 XML 响应的可接受表示【英文标题】:Spring Boot Could not find acceptable representation for XML responses 【发布时间】:2019-01-23 09:17:22 【问题描述】:

我为这个异常和原因找到了多种解决方案,但没有一个有效,而且看起来不合逻辑。

我有如下的 REST 服务类

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.MediaType;

import java.util.Map;
@RestController
public class UserRestService 

    @RequestMapping(value = "/userLogin", method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody ResponseEntity<Map> login(@RequestBody Map<String, String> name) 
        ResponseEntity<Map> res = null;
        name.values().forEach(System.out::println);
        return new ResponseEntity<Map>(name, HttpStatus.OK);
    

如您所见,我尝试创建一个 POST 请求,请求正文为 JSON,响应为 XML,在此过程中打印请求。这只是一个简单的示例,但是当请求被正确打印时,这意味着它已被接收并编组到 Map。但是响应状态是 406(不可接受)

日志中的异常是

John
30
null
2018-08-16 14:05:48.440  WARN 22820 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-08-16 14:05:48.481  WARN 22820 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

请求头只有

Accept:*/*
Content-Type:application/json

POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.cms</groupId>
    <artifactId>business</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

注意添加以下依赖可以解决问题

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

请求正文 "name":"John", "age":30, "car":null

如果MediaType.APPLICATION_JSON_UTF8_VALUE 用于produces@RequestMapping 属性为 "name":"John", "age":30, "car":null ,则响应为 "name":"John", "age":30, "car":null

如果MediaType.APPLICATION_XML_VALUE 用于@RequestMappingjackson-dataformat-xmlproduces 属性,则响应

<Map>
    <name>John</name>
    <age>30</age>
    <car/>
</Map>

为什么使用 JAXB 会失败?

更新 01

好的,就像@Simon 评论一样,我创建了一个示例对象(类)来作为请求和响应。 效果完全相同。这是更新的代码

public class MockupResponse 

    private String name;
    private Integer age;
    private String car;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Integer getAge() 
        return age;
    

    public void setAge(Integer age) 
        this.age = age;
    

    public String getCar() 
        return car;
    

    public void setCar(String car) 
        this.car = car;
    

@Override
public String toString() 
        StringBuffer sb = new StringBuffer();
        if (null != name)
            sb.append("Name: " + name + "\n");
        if (null != age)
            sb.append("Age: " + age + "\n");
        if (null != car)
            sb.append("Car: " + car + "\n");

        return sb.toString();


服务

@RequestMapping(value = "/userLogin", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody ResponseEntity<MockupResponse> login(@RequestBody MockupResponse sample) 
        System.out.println(sample);

        return new ResponseEntity<MockupResponse>(sample, HttpStatus.OK);
    

【问题讨论】:

因为 JAXB 将对象转换为 XML 而不是映射。为什么不创建一个类而不是使用地图? 谢谢@SimonMartinelli 我正在寻找解释,以后可能会创建一个对象,但正如您所见,这只是一个示例代码。我认为jackson-dataformat-xml 能够将地图转换为优于 JAXB 的优势,对吧? 遗憾的是,用对象而不是地图更新了代码,效果完全相同。 【参考方案1】:

将@XmlRootElement添加到必须像JAXB一样序列化的类

@XmlRootElement
public class MockupResponse 

    private String name;
    private Integer age;
    private String car;
...

然后 XML 序列化工作。

在 Spring Doc 中了解更多信息:

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html

如果您使用 Java 9 及更高版本,则必须将 JAXB 和 javax.activation 添加到依赖项:

    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.3.0.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.3.0.1</version>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
        <version>1.1.1</version>
    </dependency>

【讨论】:

我得到相同的响应,但抛出异常java.lang.NoClassDefFoundError: javax/activation/DataHandler 您必须添加 javax.activation。查看更新的答案 该死的,这个依赖是从 2009 年开始的,并从 JDK 9/10 和 jaxb 中删除。应该注意这一点。感谢@SimonMartinelli 的帮助,非常感谢 很高兴它可以工作。删除整个 JAXB 和激活是一个愚蠢的想法,因为许多框架仍在使用它。

以上是关于Spring Boot 找不到 XML 响应的可接受表示的主要内容,如果未能解决你的问题,请参考以下文章

我们如何在使用logback.xml和MDC的spring-boot日志中获取用户请求,响应和request-id以及process-id?

Spring Boot+JPA+QueryDSL=找不到OrderSpecifier

Java 之 Spring Boot 发送邮箱(解决依赖找不到异常)

spring boot 2.0类找不到EmbeddedServletContainerInitializedEvent

Java 之 Spring Boot 发送邮箱(解决依赖找不到异常)

Spring boot多模块项目编译报错:找不到符号