5

How can I configure a Spring Boot RestController to accept YAML uploads?

The following results in a 415. I can see from debugging that the MappingJackson2HttpMessageConverter instances in my Spring context only support [application/json;charset=UTF-8, application/*+json;charset=UTF-8]. I can't be the only Spring Boot user trying to do this, and I'm surprised it doesn't just work - most things do in Spring Boot!

I've got the YAML dataformat in my POM:

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

My RestController has a method thus:

@RequestMapping(method=RequestMethod.POST, value="/", consumes="application/yaml")
public String upload(@RequestBody Declaration declaration) {
    //Do stuff
}

And my test:

@Test
public void triggersConvergence() throws Exception {
    ClassPathResource fixture = new ClassPathResource("declaration.yml");
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.add("Content-Type", "application/yaml");
    requestHeaders.add("Accept", "application/json");

    URI uri = new URI("http://127.0.0.1:"+port);
    byte[] bytes = new byte[(int)fixture.contentLength()];
    fixture.getInputStream().read(bytes);
    RequestEntity<byte[]> postRequest = new RequestEntity<byte[]>(bytes, requestHeaders, HttpMethod.POST, uri);

    ResponseEntity<String> response = rest.exchange(postRequest, String.class);
    assertThat(response.getStatusCode(), is(HttpStatus.OK));
    assertThat(response.getBody(), is("Converged org my-lovely-org"));
}
EngineerBetter_DJ
  • 11,630
  • 15
  • 81
  • 158
  • Do you want to upload or do you want to submit an object represented as YAML. As both are totally different questions (and solutions). – M. Deinum May 19 '16 at 16:42

2 Answers2

6

While this functionality is not available in Spring it's easy to add using YAMLMapper it in 2 simple steps:

  1. Define your own HttpMessageConverter that supports Content-Type: application/x-yaml:

    final class YamlJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
        YamlJackson2HttpMessageConverter() {
            super(new YAMLMapper(), MediaType.parseMediaType("application/x-yaml"));
        }
    }
    
  2. Register your converter:

    @Configuration
    public class YamlConfiguration extends WebMvcConfigurerAdapter {
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(new YamlJackson2HttpMessageConverter());
        }
    }
    

Enjoy controller methods consuming and producing application/x-yaml from POJOs.

Community
  • 1
  • 1
miensol
  • 32,791
  • 6
  • 103
  • 105
1

Yes, you can do that.

Maven dependencies:

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>2.5.4</version>
</dependency>

Controller method:

@RequestMapping(value = "/my/endpoint", method = RequestMethod.POST, consumes = "application/x-yaml")
@ResponseBody
public ResponseEntity<MyResponse> receiveYaml(@RequestBody final String yaml) {
    //unserialize yaml
}

Request example using curl:

curl -X POST --header "Content-Type: application/x-yaml" --header "Accept: */*" -d "invoice: 34843
date   : 2001-01-23
bill-to: &id001
    given  : Chris
    family : Dumars
    address:
        lines: |
            458 Walkman Dr.
            Suite #292
        city    : Royal Oak
        state   : MI
        postal  : 48046
ship-to: *id001
product:
    - sku         : BL394D
      quantity    : 4
      description : Basketball
      price       : 450.00
    - sku         : BL4438H
      quantity    : 1
      description : Super Hoop
      price       : 2392.00
tax  : 251.42
total: 4443.52
comments: >
    Late afternoon is best.
    Backup contact is Nancy
    Billsmer @ 338-4338."
humungs
  • 1,104
  • 4
  • 24
  • 43
  • The `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml` is not needed to be able to consume `application/x-yaml` in `receiveYaml(@RequestBody final String yaml)`. – miensol May 20 '16 at 08:56
  • 1
    You are right. But is required if you want to deserialize the yaml using `new ObjectMapper(new YAMLFactory());` – humungs May 20 '16 at 17:36
  • You can declare the param using POJO's type instead of `String`, thus could avoid deserialize explicitly using `ObjectMapper`. BTW, it could support yaml & json at the same time, thus no need to add `consumes = "application/x-yaml"`, unless you only want to accept yaml. – user218867 Jun 10 '19 at 08:13