htshame
htshame

Reputation: 7330

XML response from API with PathVariable

I have the API:

@GetMapping(path = "/users/{userId}")
public ResponseEntity<UserDTO> getUserById(@PathVariable(value = "userId") Long userId) {
    //logic here
}

it returns JSON response, as it should.

And there's another app that I don't have access to, and it calls my API as, for example, GET /users/123.xml in order to receive XML response.

But in this case, my API fails with 400 error, because it cannot parse 123.xml into Long.

Option @GetMapping(value = {"/users/{userId}", "/users/{userId}.xml"}) fails with the same error.

What can I do to respond with XML syntax when calling /{userId}.xml and in the mean time, respond with JSON syntax when calling /{userId}?

EDIT:

I want it to do without specifically adding 'Accept' headers, and without writing any additional logic, that'll parse {userId}.xml and then set the appropriate response type.

Upvotes: 1

Views: 809

Answers (3)

talalUcef
talalUcef

Reputation: 124

That's can be done by using a ContentNegotiationConfigurer, you can configure it as follow :

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
                .defaultContentType(MediaType.APPLICATION_JSON)
                .mediaType("xml", MediaType.APPLICATION_XML)
                .mediaType("json", MediaType.APPLICATION_JSON);
    }
}

It should work fine with your endpoint :

@GetMapping(path = "/users/{userId}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public ResponseEntity<UserDTO> getUserById(@PathVariable(value = "userId") Long userId) {
    return new ResponseEntity<>(userService.get(userId), HttpStatus.OK);
}

Upvotes: 2

Piotr Podraza
Piotr Podraza

Reputation: 2029

As an owner of the API you should declare what kind of a responses you are able to produce - in your case it is either JSON or XML:

@GetMapping(path = "/users/{userId}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public ResponseEntity<UserDTO> getUserById(@PathVariable(value = "userId") Long userId) {
    return new ResponseEntity<>(userService.get(userId), HttpStatus.OK);
}

Any client of the API can now choose which response format is preferred using Accept header - for example Accept: application/xml. Spring will respect that and return response in requested format.

To make it work you need to add additional dependency that will be used by Spring to produce XML responses:

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

If you really need to go in /users/123.xml direction you'll have to change userId type to String and parse it yourself like this:

@GetMapping(path = "/users/{userId}")
public ResponseEntity<UserDTO> getUserById(@PathVariable(value = "userId") String userId) {
    if (hasXMLExtension(userId)) {
        return ResponseEntity
                  .ok()
                  .contentType(MediaType.XML)
                  .body(requestdUser);
    } else {
        return ResponseEntity
                  .ok()
                  .contentType(MediaType.JSON)
                  .body(requestdUser);
    }
}

Upvotes: 1

soumitra goswami
soumitra goswami

Reputation: 891

The easiest solution that I can think right off will be to have an optional Request parameter "responseType" with default value as json, and if someone wants XML response, they can call the url like :GET /users/123?responseType=xml Since the default value of the parameter will be 'json' and it will have property "required= false", you wouldn't need to worry in use cases where json response is desired, and if someone wants XML response, they can add the optional RequestParam. Also, I guess you will need to specify produces with json and xml return types for the controller to let spring-boot know that it can produce different kinds of responses, something like -

@RequestMapping(value = "/users/{userid}", method = RequestMethod.GET, 
produces = { MediaType.APPLICATION_JSON_VALUE, 
MediaType.APPLICATION_XML_VALUE }, consumes = MediaType.ALL_VALUE)
public ResponseEntity<User> getuserById(@PathVariable String 
userid,@RequestParam(required= 
false,defaultValue="json",name="responseType"),@RequestHeader ("content-type") String 
contentType)
)

EDIT : You can use either the request param or the Request header, I provided both in the example for your reference

Upvotes: 1

Related Questions