【问题标题】:Issue with content type in SpringSpring中的内容类型问题
【发布时间】:2017-01-16 02:13:24
【问题描述】:

我收到的 POST 有问题。我有以下端点:

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

现在,当有人在此 URL 上向我发送 POST 时,我会收到以下回复:

{"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
}}

我该如何解决这个问题?发送时无法更改内容类型标题。它是从我无法控制的服务器发送的。

【问题讨论】:

标签: java spring http-headers


【解决方案1】:

到目前为止,对我来说最好的替代方法是编写一个转换器

我选择这种方式的要点是:

  • 我只需要在我的 API 中处理一个端点(和一个对象),这对于我可以控制它们的其他端点是不同的;
  • 我无法控制调用我的端点的第三方服务;
  • 第三方服务仅以一种方式调用我的服务,即通过媒体类型为 application/x-www-form-urlencoded 的 POST。

首先:写一个对象

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

    ...

}

第二:创建一个转换器来映射模型

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 {

    }
}

第三:告诉spring使用这个转换器

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

奖励:此配置/实现处理属性内的下划线字符,换句话说:您可以使用 @JsonProperty 注释,因为上面的此实现使用 ObjectMapper 和对象映射器(我认为)与 spring 用于序列化的相同带有Content-type: application/json 的普通POST。

第四个也是最后一个:使用这个实现:

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

}

希望对你有帮助:)

编辑:我使用的是spring boot 2

【讨论】:

    【解决方案2】:

    实际上,将@RequestBody 更改为@ModelAttribute 很有帮助。所以我的代码看起来像这样:

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

    【讨论】:

      【解决方案3】:

      如果 Content Type 是 application/json 或 application/xml 使用 @RequestBody 注解,如果是 application/x-www-form-urlencoded 使用 @ModelAttribute

      【讨论】:

        猜你喜欢
        • 2019-08-22
        • 2011-08-01
        • 2018-03-01
        • 1970-01-01
        • 2014-09-15
        • 1970-01-01
        • 2016-06-15
        • 2014-02-26
        • 2011-06-04
        相关资源
        最近更新 更多