1

I have an issue with a POST that I am receiving. I have the following endpoint:

@RequestMapping(value = "/payment", method = POST)
public void saveOrder(@RequestBody PaymentDto paymentDto) throws RequiredFieldException, IOException, MessagingException {
//do something
}

Now, when someone send me POST on this URL, I get the following in response:

{"errorMessage":"Unsupported Media Type",
"errorId":"906f5dc8-0b79-4f91-9eaa-a252e8d5ac76",
"errorDetails":
    {"message":"Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported",
    "exception":"org.springframework.web.HttpMediaTypeNotSupportedException",
    "errors":null
}}

How can I fix that? I cannot change content type header when I send it. It is send from server that I don't control.

uksz
  • 18,239
  • 30
  • 94
  • 161
  • may be duplicate to this one : http://stackoverflow.com/questions/33796218/content-type-application-x-www-form-urlencodedcharset-utf-8-not-supported-for – Minh Sep 08 '16 at 08:38

3 Answers3

1

if Content Type is application/json or application/xml use @RequestBody annotation, if it is application/x-www-form-urlencoded use @ModelAttribute

Anil
  • 191
  • 1
  • 2
  • 11
  • Note also that setting the param to Object will not work as it needs to be parsed to a DTO. Add also consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE} to post mapping – RicardoVallejo Sep 01 '22 at 10:43
1

So far the best alternativity for me was write a converter.

My points to choose this way are:

  • I need to deal with only one endpoint (and only one object) in my API which are different for others endpoints that I have control over them;
  • I have not control about the thirdy party service that call my endpoint;
  • The thirdy party service call in only one way my services, via POST with media type as application/x-www-form-urlencoded.

First: write an object

package xx.xx.xx;

import java.io.Serializable;

public class MyObject implements Serializable {

    private static final long serialVersionUID = -7474838713815803531L;

    private String name;

    private String id;

    ...

    getters

    setters

    ...

}

Second: create a converter to mapping the model

package xx.xx.xx;

import java.io.IOException;
import java.util.Map;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import com.fasterxml.jackson.databind.ObjectMapper;

import xx.xx.xx.MyObject;

public class MyObjectConverter extends AbstractHttpMessageConverter<MyObject> {

    private static final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    protected boolean supports(Class<?> clazz) {
        return (MyObject.class == clazz);
    }

    @Override
    protected MyObject readInternal(Class<? extends MyObject> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        Map<String, String> vals = formHttpMessageConverter.read(null, inputMessage).toSingleValueMap();
        return mapper.convertValue(vals, MyObject.class);
    }

    @Override
    protected void writeInternal(MyObject myObject, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

    }
}

Third: tell to spring use this converter

package xx.xx.xx;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class HppResponseConverterDTOConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(converter());
    }

    private MyObjectConverter converter() {
        MyObjectConverter converter = new MyObjectConverter();
        MediaType mediaType = new MediaType("application", "x-www-form-urlencoded", Charset.forName("UTF-8"));
        converter.setSupportedMediaTypes(Arrays.asList(mediaType));
        return converter;
    }
}

Bonus: this configuration/implementation deal with underscore character inside attributes, in other words: you can use @JsonProperty annotation because this implementation above use ObjectMapper and object Mapper (I think) is the same that spring use to serialize normal POST's with Content-type: application/json.

Fourth and final: using this implementation:

package xx.xx.xx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/my-object-controller")
public class MyObjectController {

    @PostMapping(value = "/something", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public void doSomething(@RequestBody MyObject myObject) {
        doSomethingWithMyObject(myObject);
    }

}

I hope it helps :)

EDIT: I'm using spring boot 2

Marco Blos
  • 933
  • 9
  • 22
0

Actually, what helped was changing @RequestBody to @ModelAttribute. So that my code looked like that:

@RequestMapping(value = "/payment", method = POST)
public void saveOrder(@ModelAttribute PaymentDto paymentDto) throws RequiredFieldException, IOException, MessagingException {
//do something
}
uksz
  • 18,239
  • 30
  • 94
  • 161