41

I am working with Spring 4.0.7

About Spring MVC, for research purposes, I have the following:

@RequestMapping(value="/getjsonperson", 
                method=RequestMethod.GET, 
                produces=MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody Person getJSONPerson(){
    logger.info("getJSONPerson - getjsonperson");
    return PersonFactory.createPerson();
}

@RequestMapping(value="/getperson.json", method=RequestMethod.GET)
public @ResponseBody Person getPersonJSON(){
    logger.info("getPerson - getpersonJSON");
    return PersonFactory.createPerson();
}

Each one works fine, observe both for JSON, with and without extension:

  • /getjsonperson
  • /getperson.json

Same for XML

@RequestMapping(value="/getxmlperson",
                method=RequestMethod.GET,
                produces=MediaType.APPLICATION_XML_VALUE
                )
public @ResponseBody Person getXMLPerson(){
    logger.info("getXMLPerson - getxmlperson");
    return PersonFactory.createPerson();
}

@RequestMapping(value="/getperson.xml", method=RequestMethod.GET)
@ResponseBody
public Person getPersonXML(){
    logger.info("getPerson - getpersonXML");
    return PersonFactory.createPerson();
}

Each one works fine, observe both for XML, with and without extension:

  • /getxmlperson
  • /getperson.xml

Now about Restful I have the following:

@RequestMapping(value="/person/{id}/", 
                method=RequestMethod.GET,
                produces={MediaType.APPLICATION_JSON_VALUE, 
                          MediaType.APPLICATION_XML_VALUE})
public ResponseEntity<Person> getPersonCustomizedRestrict(@PathVariable Integer id){
    Person person = personMapRepository.findPerson(id);
    return new ResponseEntity<>(person, HttpStatus.FOUND);//302     
}

Observe the MediaType, it is mixed, for JSON and XML

Through RestTemplate I can indicate the Accept value

    if(type.equals("JSON")){
        logger.info("JSON");
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    }
    else if(type.equals("XML")){
        logger.info("XML");
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
    }
    ….

    ResponseEntity<Person> response =
                restTemplate.exchange("http://localhost:8080/spring-utility/person/{id}/customizedrestrict",
                                      HttpMethod.GET,
                                      new HttpEntity<Person>(headers),  
                                      Person.class,
                                       id
                                     ); 

Until here, therefore I am able to use one URL/URI to get some data in either XML or JSON formats. It works fine

My problem is with Spring MVC … just consider

@RequestMapping(value="/{id}/person", 
                method=RequestMethod.GET,
                produces={MediaType.APPLICATION_JSON_VALUE,  
                          MediaType.APPLICATION_XML_VALUE})
public @ResponseBody Person getPerson(@PathVariable Integer id){
    return personMapRepository.findPerson(id);
}

I can call or activate that handler method (@RequestMapping) through:

  1. jQuery working with Ajax, I am able to indicate the Accept value (JSON for example)
  2. Poster, through the Headers button, I can set the Accept

Question One:

But for a common link? how I can set the Accept value? is possible?

I thought in other way to around this problem.

  • http://localhost:8080/spring-utility/person/getpersonformat?format=json
  • http://localhost:8080/spring-utility/person/getpersonformat?format=xml

Observe:

  • ?format

Therefore

@RequestMapping(value="/getpersonformat", 
                method=RequestMethod.GET,
                produces={MediaType.APPLICATION_JSON_VALUE,  
                          MediaType.APPLICATION_XML_VALUE})
public @ResponseBody Person getPerson(@RequestParam String format){
    return personMapRepository.findPerson(id);
}

Question Two:

What code for the method shown above must be added to customize the return type format? I mean, JSON or XML, Is possible?

I thought in the following:

@RequestMapping(value="/getpersonformataltern",
        method=RequestMethod.GET
        produces={MediaType.APPLICATION_JSON_VALUE, 
                  MediaType.APPLICATION_XML_VALUE}
        )
public ResponseEntity<Person> getPersonFormat(@RequestParam String format){
    logger.info("getPersonFormat - format: {}", format);
    HttpHeaders httpHeaders = new HttpHeaders();
    if(format.equals("json")){
        logger.info("Ok JSON");
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    }
    else{
        logger.info("Ok XML");
        httpHeaders.setContentType(MediaType.APPLICATION_XML);
    }
    return new ResponseEntity<>(PersonFactory.createPerson(), httpHeaders, HttpStatus.OK);
}

But:

If I execute the URL:

  • http://localhost:8080/spring-utility/person/getpersonformataltern?format=json

I get

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
    <id>1</id>
    <firstName>Manuel</firstName>
    <lastName>Jordan</lastName>
…
</person>

Yes in XML!

Note: I can confirm the Console prints Ok JSON

If I execute the URL:

  • http://localhost:8080/spring-utility/person/getpersonformataltern?format=xml

I get

This XML file does not appear to have any style information associated with it. 
The document tree is shown below.

<person>
    <id>1</id>
    <firstName>Manuel</firstName>
    <lastName>Jordan</lastName> 
    …
</person>

Question Three

What code for the method shown above must be added to fix the JSON output? I don't know what is wrong or is missing..

There are three questions.

Thank You

Alpha

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    Map<String,MediaType> mediaTypes = new LinkedHashMap<>();
    mediaTypes.put("json", MediaType.APPLICATION_JSON);
    mediaTypes.put("xml", MediaType.APPLICATION_XML);
    configurer.mediaTypes(mediaTypes);
    configurer.defaultContentType(MediaType.TEXT_HTML);
}
Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
  • Look into content negotiation. – Sotirios Delimanolis Oct 31 '14 at 15:38
  • See `Alpha` section. It works, remember I am working with .json and .xml in the URL too. – Manuel Jordan Oct 31 '14 at 18:39
  • If you want to use URLs you cannot set the content-type you could only do that if you control the call (as in JavaScript). One thing you could do is setting the default content-type on the server to JSON (instead of HTML as you have now). Not being able to control the content-type is kind of logical because a link from HTML should result in HTML. – M. Deinum Nov 06 '14 at 06:37
  • @M.Deninum Oh I see, sadly is not possible. Thanks by your reply – Manuel Jordan Nov 06 '14 at 12:49
  • 1
    you can always use curl to test in either formatted responses you want. For JSON use curl -H "Accept: application/json" and for XML use curl -H "Accept: application/xml" – Micho Rizo Jan 14 '15 at 20:27
  • Understood, thank you!, BTW in Firefox I use the plugin named 'Poster' – Manuel Jordan Jan 29 '15 at 16:49

3 Answers3

47

Using Accept header is really easy to get the format json or xml from the REST service.

This is my Controller, take a look produces section.

@RequestMapping(value = "properties", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, method = RequestMethod.GET)
    public UIProperty getProperties() {
        return uiProperty;
    }

In order to consume the REST service we can use the code below where header can be MediaType.APPLICATION_JSON_VALUE or MediaType.APPLICATION_XML_VALUE

HttpHeaders headers = new HttpHeaders();
headers.add("Accept", header);

HttpEntity entity = new HttpEntity(headers);

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange("http://localhost:8080/properties", HttpMethod.GET, entity,String.class);
return response.getBody();

Edit 01:

In order to work with application/xml, add this dependency

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Eddú Meléndez
  • 6,107
  • 1
  • 26
  • 35
  • When the time be appropriate, let me test your solution. Now I am in the server side again. Thank you – Manuel Jordan Feb 22 '15 at 19:04
  • 3
    Save my time! forget to add `jackson-dataformat-xml` to pom – bob Mar 20 '16 at 09:50
  • The first produces parameter define default format (for requests without accept header), e.g. with `produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }` endpoint will send XML if client doesn't specify format with `accept` header – foal May 20 '21 at 10:38
2

All your problems are that you are mixing content type negotiation with parameter passing. They are things at different levels. More specific, for your question 2, you constructed the response header with the media type your want to return. The actual content negotiation is based on the accept media type in your request header, not response header. At the point the execution reaches the implementation of the method getPersonFormat, I am not sure whether the content negotiation has been done or not. Depends on the implementation. If not and you want to make the thing work, you can overwrite the request header accept type with what you want to return.

return new ResponseEntity<>(PersonFactory.createPerson(), httpHeaders, HttpStatus.OK);

jljl
  • 21
  • 1
0

I've preferred using the params filter for parameter-centric content-type.. I believe that should work in conjunction with the produces attribute.

@GetMapping(value="/person/{id}/", 
            params="format=json",
            produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Person> getPerson(@PathVariable Integer id){
    Person person = personMapRepository.findPerson(id);
    return ResponseEntity.ok(person);
} 
@GetMapping(value="/person/{id}/", 
            params="format=xml",
            produces=MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<Person> getPersonXML(@PathVariable Integer id){
    return GetPerson(id); // delegate
}
M. Maraist
  • 432
  • 4
  • 7
  • 5
    If the format parameter is used in all the REST URIs among all the rest controllers, the code among will increase manyfold – Rui Jan 27 '17 at 08:30