接受/返回 XML/JSON 请求和响应 - Spring MVC

Posted

技术标签:

【中文标题】接受/返回 XML/JSON 请求和响应 - Spring MVC【英文标题】:Accepting / returning XML/JSON request and response - Spring MVC 【发布时间】:2016-05-18 04:12:54 【问题描述】:

我需要编写一个接受 XML/JSON 作为输入(POST 方法)和 XML/JSON 作为输出(基于输入格式)的休息服务。我尝试了以下方法来实现这一点,但没有帮助。Endpoint 方法同时接受 XML/JSON,但在响应时它总是根据 @RequestMapping -produces 中指定的顺序给出 JSON 或 XML。任何帮助将不胜感激.

我的端点方法:

@RequestMapping(value = "/getxmljson", method = RequestMethod.POST,produces="application/json","application/xml",
        consumes="application/json", "application/xml")
public @ResponseBody Student processXMLJsonRequest(@RequestBody Student student)
        throws Exception 
    System.out.println("*************Inside Controller");
    return student;

POJO 类:Student.java

import java.io.Serializable;
import java.util.ArrayList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@XmlRootElement(name = "student")
@XmlType(propOrder = "id", "name", "graduationTime", "courses")
@JsonPropertyOrder("id", "name", "graduationTime", "courses")
public class Student implements Serializable 
    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private String graduationTime;
    private ArrayList<Course> courses = new ArrayList<Course>();

    @XmlElement
    public int getId()  return id; 
    @XmlElement
    public String getName()  return name; 
    @XmlElement
    public String getGraduationTime()  return graduationTime; 
    @XmlElement
    public ArrayList<Course> getCourses()  return courses; 

    public void setId(int value)  this.id = value; 
    public void setName(String value)  this.name = value; 
    public void setGraduationTime(String value)  this.graduationTime = value; 
    public void setCourses(ArrayList<Course> value)  this.courses = value; 

    @JsonIgnore
    public String toString() 
        return this.name + " - "
                + graduationTime == null? "Unknown" : graduationTime.toString();
    

    public Student() 
    public Student(int id, String name, String graduationTime) 
        this.id = id;
        this.name = name;
        this.graduationTime = graduationTime;
    

POJO 类:Course.java

import java.io.Serializable;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@XmlRootElement(name = "course")
@XmlType(propOrder = "courseName", "score")
@JsonPropertyOrder("courseName", "score")
public class Course implements Serializable 
    private static final long serialVersionUID = 1L;

    private String courseName;
    private Integer score;

    public @XmlElement String getCourseName()  return courseName; 
    public @XmlElement Integer getScore()  return score; 

    public void setCourseName(String value)  courseName = value; 
    public void setScore(Integer value)  score = value; 

    public Course() 
    public Course(String courseName, Integer score) 
        this.courseName = courseName;
        this.score = score;
    

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:sws="http://www.springframework.org/schema/web-services"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:oxm="http://www.springframework.org/schema/oxm"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/web-services
        http://www.springframework.org/schema/web-services/web-services-2.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
       http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd
       http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-2.5.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing 
        infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the $webappRoot/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Configure to plugin JSON as request and response in method handler -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="jsonMessageConverter" />
                <beans:ref bean="xmlMessageConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>

    <!-- Configure bean to convert JSON to POJO and vice versa -->
    <beans:bean id="jsonMessageConverter"
        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    </beans:bean>

    <beans:bean id="xmlMessageConverter"
        class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter">
    </beans:bean>

    <beans:bean id="restTemplate" class="org.springframework.web.client.RestTemplate">

    </beans:bean>

    <beans:bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
    <context:component-scan base-package="com.test" />

</beans:beans>

Json 输入:


"id":2014,
"name":"test",
"graduationtime":"09/05/2014",
"courses":[

"courseName":"Math",
"score":150
,

"courseName":"Che",
"score":150

]

XML 输入:

<?xml version="1.0" encoding="UTF-8" ?>
<student>
<id>2014</id>
<name>test</name>
<graduationTime>09/05/2014</graduationTime>
<courses>
    <courseName>Math</courseName>
    <score>150</score>
</courses>
<courses>
    <courseName>Che</courseName>
    <score>150</score>
</courses>
</student>

【问题讨论】:

看看这个样本。 howtodoinjava.com/spring/spring-restful/… 【参考方案1】:

注册一个拦截每个请求的过滤器,将HttpServletRequest 变形为HttpServletRequestWrapper 的实现,并为Accept 标头返回Content-Type 值。例如,您可以注册一个名为SameInSameOutFilter 的过滤器,如下所示:

@Component
public class SameInSameOutFilter extends GenericFilterBean 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException 
        SameInSameOutRequest wrappedRequest = new SameInSameOutRequest((HttpServletRequest) request);
        chain.doFilter(wrappedRequest, response);
    

它将当前请求包装在SameInSameOutRequest

public class SameInSameOutRequest extends HttpServletRequestWrapper 
    public SameInSameOutRequest(HttpServletRequest request) 
        super(request);
    

    @Override
    public String getHeader(String name) 
        if (name.equalsIgnoreCase("accept")) 
            return getContentType();
        

        return super.getHeader(name);
    

这个包装器告诉 spring mvc 根据请求的 Content-Type 值选择一个 HttpMessageConverter。如果请求正文的Content-Typeapplication/xml,那么响应将是XML。否则,响应将是JSON

另一种解决方案是在每个请求中手动设置 Accept 标头和 Content-Type 并避免所有这些黑客攻击。

【讨论】:

【参考方案2】:

使用同一个控制器处理不同数据格式的最佳实践是让框架完成所有工作,找出编组和解组机制。

第 1 步:使用最少的控制器配置

@RequestMapping(value = "/getxmljson", method = RequestMethod.POST)
@ResponseBody
public Student processXMLJsonRequest(@RequestBody Student student) 
  return student;

此处无需指定consumesproduces。例如,考虑到您将来可能希望使用相同的方法来处理其他格式,例如 Google Protocol Buffers、EDI 等。保持控制器不受 consumesproduces 的影响,您可以通过全局配置添加数据格式而不必修改控制器代码。

第 2 步:使用ContentNegotiatingViewResolver 而不是RequestMappingHandlerAdapter

  <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
      </list>
    </property>
  </bean>

让视图解析器决定如何读取传入的数据以及如何将其写回。

第 3 步:使用 AcceptsContent-Type HTTP 标头

使用正确的 HTTP 标头值访问您的控制器将强制 ContentNegotiatingViewResolver 使用适当的数据表示自动编组和解组数据。

如果要交换 JSON 格式的数据,请将两个标头都设置为 application/json。如果您想要 XML,请将两者都设置为 application/xml

如果您不想使用 HTTP 标头(理想情况下应该这样做),您只需将 .json.xml 添加到 URL,然后 ContentNegotiatingViewResolver 将完成剩下的工作。


您可以查看我使用您的代码 sn-ps 创建的my sample app,该代码适用于 JSON 和 XML。

【讨论】:

还需要 org.springframework.web.servlet.view.xml.MappingJackson2XmlView 来处理 XML。【参考方案3】:

添加到上面 Manish 的答案,如果您不想使用基于 xml 的配置,请改用这个基于 java 的配置-

@Bean
public ViewResolver contentNegotiatingViewResolver() 
    ContentNegotiatingViewResolver resolver =
            new ContentNegotiatingViewResolver();

    List<View> views = new ArrayList<>();
    views.add(new MappingJackson2XmlView());
    views.add(new MappingJackson2JsonView());

    resolver.setDefaultViews(views);
    return resolver;

【讨论】:

【参考方案4】:

我遇到了和你一样的问题。以下是我的解决方案和示例。

以下是您需要包含的 maven 依赖项:

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.4.3</version>
        </dependency>

调度程序-servlet.xml

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

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

还有我的@RequestMapping(你可以使用自己的请求映射)

@RequestMapping(value = "/testXMLJSON",
            method = RequestMethod.GET, produces = 
                    MediaType.APPLICATION_XML_VALUE,
                    MediaType.APPLICATION_JSON_VALUE )
    @ResponseBody
    public ArtworkContentMessageType testXMLJSON()
    
        //this is GS1 xml standard mapping.
        ArtworkContentMessageType resp = new ArtworkContentMessageType();
        StandardBusinessDocumentHeader standarBusinessDocumentHeader = new StandardBusinessDocumentHeader();
        resp.setStandardBusinessDocumentHeader(standarBusinessDocumentHeader );
        ArtworkContentType artWorkContent = new ArtworkContentType();
        resp.getArtworkContent().add(artWorkContent);

        return resp ;
    

如果需要application/xml,则必须存在以下标题

Content-Type:application/xml
Accept:application/xml

【讨论】:

【参考方案5】:

如果资源定义如下

@GET
@Path("/id")
@Produces(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
public Student getStudent(@PathParam("id") String  id) 
 return student(); // logic to retunrs student object

那么请求应该包含'accept'标头('application/json'或application/xml'), 然后它以 json 或 xml 格式返回响应。

样品请求:

curl -k -X GET -H "accept: application/json" "https://172.17.0.5:8243/service/1.0/222"

学生班级示例

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name = "student")
public class Student 
    private int id;
    private String name;
    private String collegeName;
    private int age;
    @XmlAttribute
    public int getId() 
        return id;
    

    public void setId(int id) 
        this.id = id;
    

    @XmlElement
    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    
    @XmlElement
    public String getCollegeName() 
        return collegeName;
    

    public void setCollegeName(String collegeName) 
        this.collegeName = collegeName;
    

    public int getAge() 
        return age;
    
    @XmlElement
    public void setAge(int age) 
        this.age = age;
    


【讨论】:

以上是关于接受/返回 XML/JSON 请求和响应 - Spring MVC的主要内容,如果未能解决你的问题,请参考以下文章

http协议中各个响应状态返回值的含义

springBoot项目设置统一响应返回

HTTP中请求和响应浅析

Http请求头和常见响应状态码

如何与 Web XML/JSON API 交互?

HTTP返回状态码和错误