Accepting / returning XML/JSON request and response - Spring MVC

前端 未结 5 751
無奈伤痛
無奈伤痛 2021-01-31 04:54

I need to write a rest service which accepts XML/JSON as a input (POST method) and XML/JSON as a output (based on the input format). I have tried a below approach to achieve thi

相关标签:
5条回答
  • 2021-01-31 05:35

    Register a filter that intercepts each request, warp the HttpServletRequest into an implementation of HttpServletRequestWrapper and returns the Content-Type value for Accept header. For example, you can register a filter named SameInSameOutFilter like following:

    @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);
        }
    }
    

    It wraps current request in a 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);
        }
    }
    

    This wrapper tells spring mvc to select a HttpMessageConverter based on request's Content-Type value. If request body's Content-Type is application/xml, then the response would be an XML. Otherwise, the response would be JSON.

    The other solution is to manually set the Accept header along with Content-Type in each request and avoid all these hacks.

    0 讨论(0)
  • 2021-01-31 05:36

    The best practice for handling different data formats with the same controller is to let the framework do all the work of figuring out the marshalling and unmarshalling mechanisms.

    Step 1: Use minimal controller configuration

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

    There is no need to specify consumes and produces here. As an example, consider that you may want this same method to handle other formats in the future such as Google Protocol Buffers, EDI, etc. Keeping the controllers free of consumes and produces will let you add data formats through global configuration instead of having to modify the controller code.

    Step 2: Use ContentNegotiatingViewResolver instead of 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>
    

    Let the view resolver decide how to read incoming data and how to write it back.

    Step 3: Use Accepts and Content-Type HTTP headers

    Hitting your controller with the correct HTTP header values will force ContentNegotiatingViewResolver to marshal and unmarshal data automatically using the appropriate data representations.

    If you want to exchange data in JSON format, set both headers to application/json. If you want XML instead, set both to application/xml.

    If you do not want to use HTTP headers (which ideally you should), you can simply add .json or .xml to the URL and ContentNegotiatingViewResolver will do the rest.


    You can check out my sample app that I created using your code snippets that works fine for JSON and XML.

    0 讨论(0)
  • 2021-01-31 05:39

    i was facing the same problem like yours. Below is my solution and sample.

    Below is maven dependency that you need to include:

            <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>
    

    dispatcher-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>
    

    and my @RequestMapping ( you can use your own request mapping )

    @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 ;
        }
    

    If application/xml is required then, below headers must be present

    Content-Type:application/xml
    Accept:application/xml
    
    0 讨论(0)
  • 2021-01-31 05:43

    Adding to Manish's answer above, if you don't wanna use xml based configuration use this java based configuration instead-

    @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;
    }
    
    0 讨论(0)
  • 2021-01-31 05:57

    If the resource define as below

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

    Then the request should contains 'accept' header ('application/json' or application/xml'),
    then it returns response in json or xml format.

    Sample request :

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

    Sample Student class

    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;
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题