Spring-boot return json 和 xml 来自控制器
Spring-boot return json and xml from controllers
我有一个 spring-boot 1.1.7 应用程序,它在大部分 UI 中使用 Thymeleaf,所以我的控制器的响应并不是真正的问题。但是,现在我需要在用户通过 URL.
提交请求时提供 XML 响应
这是一个典型的请求:
http://localhost:9001/remote/search?sdnName=Victoria&address=123 Maple Ave
这是我的大部分 gradle 配置:
project.ext {
springBootVersion = '1.1.7.RELEASE'
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion")
compile("org.springframework.security:spring-security-web:4.0.0.M1")
compile("org.springframework.security:spring-security-config:4.0.0.M1")
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:2.1.1.RELEASE')
compile("org.springframework.boot:spring-boot-starter-actuator")
compile('com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.5.0')
}
这是我的控制器:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML_VALUE)
public List<Sdn> search(@ModelAttribute SdnSearch sdnSearch) {
List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
return foundSdns;
}
这是我要 Object 编辑的 return:
@Entity
public class Sdn {
@Id
private long entNum;
private String sdnName;
...
//getters & setters here
}
我能够通过 REST 客户端(例如 CocoaREST)接收请求并处理它。但是当我 return SDN 列表时,我得到以下异常,即使我的类路径上确实有 Jackson & jackson-dataformat-xml:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:229)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:301)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:248)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:57)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:299)
我的 REST 客户端包含 "text/xml" 的接受 Header(但老实说,我希望他们不必设置它。理想情况下,对此控制器的任何调用都会得到 XML,无论是否存在 header。
有办法处理吗?我认为媒体转换器已包括在内,只是 return 编辑了控制器告诉他们的任何内容?
解决方案:
请参阅下面我发布的答案。
创建一个新的可能更好class:
public class SdnSearchResult {
private List<Sdn> sdns;
...
}
然后,将需要对现有的 class 进行如下轻微更改:
public interface SdnSearchService {
SdnSearchResult find(SdnSearch sdnSearch);
}
@Controller
public class UISearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping("/search")
public ModelAndView search(@ModelAttribute SdnSearch sdnSearch) {
return new ModelAndView("pages/search/results", "sdns", sdnSearchService.find(sdnSearch).getSdns());
}
}
完成后,另一个控制器必须编码为:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping("/remote/search")
@ResponseBody
public SdnSearchResult search(@RequestBody SdnSearch sdnSearch) {
return sdnSearchService.find(sdnSearch);
}
}
对代码更改的快速解释:
@RequestBody
会自动将整个 HTTP 请求 body 反序列化为一个 SdnSearch
实例。外部应用程序通常会将请求数据提交为 HTTP body,因此 @RequestBody
将确保自动反序列化为 Java object。
@ResponseBody
将根据外部客户端的功能和 class 路径上可用的库自动序列化 return 值。如果 Jackson 在 class 路径上可用并且客户端已指示他们可以接受 JSON,则 return 值将自动作为 JSON 发送。如果 JRE 是 1.7 或更高版本(这意味着 JAXB 包含在 JRE 中)并且客户端已表示他们可以接受 XML,则 return 值将自动发送为 XML .
List<Sdn>
需要更改为 SdnSearchResult
以确保应用程序可以使用单个控制器方法交换 JSON、XML、RSS 和 ATOM 格式,因为XML(和基于 XML 的格式)需要在输出中使用 root-tag,而 List<Sdn>
无法转换为。
完成这些更改后,启动 REST 客户端,例如 Chrome 的 Postman 扩展,并使用以下信息向 /remote/search
提交请求:
- 请求header
Accepts
设置为application/json
。
- 请求 header
Content-Type
设置为 application/json
。
- 请求body设置为JSON字符串
{ "sdnName" : "Victoria", "address" : "123 Maple Ave" }
。
这会给你一个 JSON 回复。
您已将控制器方法标记为生成 application/xml
响应 (produces = MediaType.APPLICATION_XML_VALUE
)。请求的接受 header (Accept: text/xml
) 不匹配,因此 Spring 确定您的 search
方法无法处理请求。
有几种不同的方法可以在服务器上修复此问题,具体取决于您的具体要求:
- 您可以完全删除
produces
属性
- 您可以指定多种媒体类型:
produces = { "application/xml", "text/xml" }
我遇到了完全相同的问题,我在 Spring 文档网站上找到了解决方案:here
在综合中,我将以下依赖项添加到我项目的 pom.xml
中:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
然后我将以下代码块添加到服务必须 return 的 class 中:
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Greeting {...}
它奏效了。
解决方案:我结合使用了以下两个答案(非常感谢!)。我在这里发帖以防其他人需要帮助。
我修改的控制器:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = { "application/xml", "text/xml" }, consumes = MediaType.ALL_VALUE )
@ResponseBody
public SdnSearchResults search(@ModelAttribute SdnSearch sdnSearch) {
List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
SdnSearchResults results = new SdnSearchResults();
results.setSdns( foundSdns );
return results;
}
}
在我的客户端上,我设置了请求 headers:
Content-type: application/text
接受:text/xml
我认为最终问题是我的客户端 headers 设置不正确,所以我可能不必进行其中一些更改。但我喜欢包含结果列表的 SearchResults class 的想法:
@XmlRootElement
public class SdnSearchResults {
private List<Sdn> sdns;
...
}
除了 Michael 在他的 中所说的内容之外,我还在 pom.xml
中添加了以下依赖项
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
出于某种原因,单独使用 jackson-dataformat-xml 并没有帮助。
我还确保在 get 调用中返回 ResponseEntity 并从 RequestMapping 注释中删除了 produces=MediaType。
通过这些更改,我能够获得正确的数据,但我必须在 get 调用期间将 mime 类型的扩展名提供给 REST URL。即,明确指定:http://localhost:8080/hello.xml or http://localhost:8080/hello.json in browser
在我的例子中,我想要 return 一个格式化的 XML 字符串,并且它全部组合成一行。
将 produces = { "application/xml", "text/xml" }
添加到请求映射足以 return 格式化为 XML 的字符串(带缩进)。
示例:
@RequestMapping(method= RequestMethod.GET, value="/generate/{blabla}", produces = { "application/xml", "text/xml" })
public String getBlaBla(@PathVariable("param") String param) throws IOException {
}
祝你好运。
我不确定你的 Spring Boot (1.1.7.RELEASE) 版本,但我使用的是 1.5.2.RELEASE 版本和这个 xml 转换/序列化正如少数答案中提到的那样,在不使用任何杰克逊依赖项的情况下自动发生。
我猜这是因为 org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
自 Spring 引导版本 1.5 以来自动配置。1.RELEASE 并且转换器使用 JRE 的默认 JAXB 实现(因此没有显式 xml 需要转换依赖)。
其次,客户端在请求中设置的 Accept
header 决定了预期输出的格式,因此请求映射如下(即单个端点),
@RequestMapping(method = RequestMethod.GET, value = "/remote/search", produces = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE })
可用于生成 xml 以及 JSON 响应(如果 Accept header 设置为 text/xml
或 application/xml
& application/json
分别.
注意 1:如果 Java class 需要 xml 响应,则需要在根 class 上指定 javax.xml.bind.annotation.XmlRootElement
。这是强制性的。
注 2:json 的 Jackson 已经包含在 Spring 引导中,因此不会明确包含在 json 输出
中
注意 3:接受 header - 输出 匹配关闭由框架自动发生,开发人员无需为此编写任何特定代码。
所以在我看来,如果您只将 XmlRootElement
添加到您的基础 class 并升级您的 Spring 启动版本,您的服务器端就已准备就绪。设置正确 Accept header 的责任在于客户。
非常简单 只需在您的 pom.xml 文件中添加 jackson-dataformat-xml 依赖项,然后您的项目就能够处理 xml/json 请求或响应。添加
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
依赖项并添加 headers content-type 或根据您的需要接受 spring 启动将处理 json/xml 中的请求或响应。如需完整文章,请访问:How to handle xml/json in spring boot
我有一个 spring-boot 1.1.7 应用程序,它在大部分 UI 中使用 Thymeleaf,所以我的控制器的响应并不是真正的问题。但是,现在我需要在用户通过 URL.
提交请求时提供 XML 响应这是一个典型的请求:
http://localhost:9001/remote/search?sdnName=Victoria&address=123 Maple Ave
这是我的大部分 gradle 配置:
project.ext {
springBootVersion = '1.1.7.RELEASE'
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion")
compile("org.springframework.security:spring-security-web:4.0.0.M1")
compile("org.springframework.security:spring-security-config:4.0.0.M1")
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:2.1.1.RELEASE')
compile("org.springframework.boot:spring-boot-starter-actuator")
compile('com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.5.0')
}
这是我的控制器:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML_VALUE)
public List<Sdn> search(@ModelAttribute SdnSearch sdnSearch) {
List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
return foundSdns;
}
这是我要 Object 编辑的 return:
@Entity
public class Sdn {
@Id
private long entNum;
private String sdnName;
...
//getters & setters here
}
我能够通过 REST 客户端(例如 CocoaREST)接收请求并处理它。但是当我 return SDN 列表时,我得到以下异常,即使我的类路径上确实有 Jackson & jackson-dataformat-xml:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:229)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:301)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:248)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:57)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:299)
我的 REST 客户端包含 "text/xml" 的接受 Header(但老实说,我希望他们不必设置它。理想情况下,对此控制器的任何调用都会得到 XML,无论是否存在 header。
有办法处理吗?我认为媒体转换器已包括在内,只是 return 编辑了控制器告诉他们的任何内容?
解决方案: 请参阅下面我发布的答案。
创建一个新的可能更好class:
public class SdnSearchResult {
private List<Sdn> sdns;
...
}
然后,将需要对现有的 class 进行如下轻微更改:
public interface SdnSearchService {
SdnSearchResult find(SdnSearch sdnSearch);
}
@Controller
public class UISearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping("/search")
public ModelAndView search(@ModelAttribute SdnSearch sdnSearch) {
return new ModelAndView("pages/search/results", "sdns", sdnSearchService.find(sdnSearch).getSdns());
}
}
完成后,另一个控制器必须编码为:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping("/remote/search")
@ResponseBody
public SdnSearchResult search(@RequestBody SdnSearch sdnSearch) {
return sdnSearchService.find(sdnSearch);
}
}
对代码更改的快速解释:
@RequestBody
会自动将整个 HTTP 请求 body 反序列化为一个SdnSearch
实例。外部应用程序通常会将请求数据提交为 HTTP body,因此@RequestBody
将确保自动反序列化为 Java object。@ResponseBody
将根据外部客户端的功能和 class 路径上可用的库自动序列化 return 值。如果 Jackson 在 class 路径上可用并且客户端已指示他们可以接受 JSON,则 return 值将自动作为 JSON 发送。如果 JRE 是 1.7 或更高版本(这意味着 JAXB 包含在 JRE 中)并且客户端已表示他们可以接受 XML,则 return 值将自动发送为 XML .List<Sdn>
需要更改为SdnSearchResult
以确保应用程序可以使用单个控制器方法交换 JSON、XML、RSS 和 ATOM 格式,因为XML(和基于 XML 的格式)需要在输出中使用 root-tag,而List<Sdn>
无法转换为。
完成这些更改后,启动 REST 客户端,例如 Chrome 的 Postman 扩展,并使用以下信息向 /remote/search
提交请求:
- 请求header
Accepts
设置为application/json
。 - 请求 header
Content-Type
设置为application/json
。 - 请求body设置为JSON字符串
{ "sdnName" : "Victoria", "address" : "123 Maple Ave" }
。
这会给你一个 JSON 回复。
您已将控制器方法标记为生成 application/xml
响应 (produces = MediaType.APPLICATION_XML_VALUE
)。请求的接受 header (Accept: text/xml
) 不匹配,因此 Spring 确定您的 search
方法无法处理请求。
有几种不同的方法可以在服务器上修复此问题,具体取决于您的具体要求:
- 您可以完全删除
produces
属性 - 您可以指定多种媒体类型:
produces = { "application/xml", "text/xml" }
我遇到了完全相同的问题,我在 Spring 文档网站上找到了解决方案:here
在综合中,我将以下依赖项添加到我项目的 pom.xml
中:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
然后我将以下代码块添加到服务必须 return 的 class 中:
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Greeting {...}
它奏效了。
解决方案:我结合使用了以下两个答案(非常感谢!)。我在这里发帖以防其他人需要帮助。
我修改的控制器:
@Controller
public class RemoteSearchController {
@Autowired
private SdnSearchService sdnSearchService;
@RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = { "application/xml", "text/xml" }, consumes = MediaType.ALL_VALUE )
@ResponseBody
public SdnSearchResults search(@ModelAttribute SdnSearch sdnSearch) {
List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
SdnSearchResults results = new SdnSearchResults();
results.setSdns( foundSdns );
return results;
}
}
在我的客户端上,我设置了请求 headers:
Content-type: application/text 接受:text/xml 我认为最终问题是我的客户端 headers 设置不正确,所以我可能不必进行其中一些更改。但我喜欢包含结果列表的 SearchResults class 的想法:
@XmlRootElement
public class SdnSearchResults {
private List<Sdn> sdns;
...
}
除了 Michael 在他的
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
出于某种原因,单独使用 jackson-dataformat-xml 并没有帮助。 我还确保在 get 调用中返回 ResponseEntity 并从 RequestMapping 注释中删除了 produces=MediaType。
通过这些更改,我能够获得正确的数据,但我必须在 get 调用期间将 mime 类型的扩展名提供给 REST URL。即,明确指定:http://localhost:8080/hello.xml or http://localhost:8080/hello.json in browser
在我的例子中,我想要 return 一个格式化的 XML 字符串,并且它全部组合成一行。
将 produces = { "application/xml", "text/xml" }
添加到请求映射足以 return 格式化为 XML 的字符串(带缩进)。
示例:
@RequestMapping(method= RequestMethod.GET, value="/generate/{blabla}", produces = { "application/xml", "text/xml" })
public String getBlaBla(@PathVariable("param") String param) throws IOException {
}
祝你好运。
我不确定你的 Spring Boot (1.1.7.RELEASE) 版本,但我使用的是 1.5.2.RELEASE 版本和这个 xml 转换/序列化正如少数答案中提到的那样,在不使用任何杰克逊依赖项的情况下自动发生。
我猜这是因为 org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
自 Spring 引导版本 1.5 以来自动配置。1.RELEASE 并且转换器使用 JRE 的默认 JAXB 实现(因此没有显式 xml 需要转换依赖)。
其次,客户端在请求中设置的 Accept
header 决定了预期输出的格式,因此请求映射如下(即单个端点),
@RequestMapping(method = RequestMethod.GET, value = "/remote/search", produces = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE })
可用于生成 xml 以及 JSON 响应(如果 Accept header 设置为 text/xml
或 application/xml
& application/json
分别.
注意 1:如果 Java class 需要 xml 响应,则需要在根 class 上指定 javax.xml.bind.annotation.XmlRootElement
。这是强制性的。
注 2:json 的 Jackson 已经包含在 Spring 引导中,因此不会明确包含在 json 输出
中注意 3:接受 header - 输出 匹配关闭由框架自动发生,开发人员无需为此编写任何特定代码。
所以在我看来,如果您只将 XmlRootElement
添加到您的基础 class 并升级您的 Spring 启动版本,您的服务器端就已准备就绪。设置正确 Accept header 的责任在于客户。
非常简单 只需在您的 pom.xml 文件中添加 jackson-dataformat-xml 依赖项,然后您的项目就能够处理 xml/json 请求或响应。添加
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
依赖项并添加 headers content-type 或根据您的需要接受 spring 启动将处理 json/xml 中的请求或响应。如需完整文章,请访问:How to handle xml/json in spring boot