使用 RestTemplate 与对象作为数据和 application/x-www-form-urlencoded 内容类型?

Jos*_* M. 9 java spring spring-boot

我需要MultiValueMap通过RestTemplate内容类型为a的对象(例如不是 a )发布application/x-www-form-urlencoded。当我尝试这样做时...

HttpHeaders headers = new HttpHeaders();
HttpEntity request;

headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED)

// data is some generic type
request = new HttpEntity<>(data, headers);

// clazz is the Class<T> being returned
restTemplate.exchange(url, method, request, clazz)
Run Code Online (Sandbox Code Playgroud)

...我收到以下错误:

org.springframework.web.client.RestClientException:无法写入请求:找不到适合请求类型 [com.whatever.MyRequestPayload] 和内容类型 [application/x-www-form-urlencoded] 的 HttpMessageConverter

这是我在里面看到的restTemplate.getMessageConverters()

消息转换器

为什么我不想提供MultiValueMap? 两个原因:

  1. 这是用于向多个端点发送请求的通用代码,因此添加专门用于的重载x-www-form-urlencoded只会使事情复杂化
  2. 似乎我不应该 - 我只是不知道需要使用哪个 HttpMessageConverter 来支持将对象转换为x-www-form-urlencoded字符串

pup*_*lpg 16

原因:没有转换器可以将您的java对象转换为请求体格式x-www-form-urlencoded

解决方案1:创建这种转换器,正如@Josh M. 所发布的那样。

解决方案2:将你的java对象转换为.springboot中MultiValueMap已经有一个名为的转换器,它会自动转换为请求体格式。FormHttpMessageConverterMultiValueMapx-www-form-urlencoded

因此,在解决方案2中,您所需要做的就是将 java 对象转换为MultiValueMap

        MultiValueMap<String, String> bodyPair = new LinkedMultiValueMap();
        bodyPair.add(K1, V1);
        bodyPair.add(K2, V2);
        bodyPair.add(K3, V3);
        ...
Run Code Online (Sandbox Code Playgroud)

K1, V1, K2, V2, ..., 表示 java 对象中的字段名称和相应的值。需要添加您在 java 类中声明的所有字段。如果字段太多,可以考虑使用Java Reflection。


Jos*_* M. 8

我最终不得不编写一个自定义 HTTP 消息转换器,它接受任何对象并将其作为 www-form-urlencoded 内容写出到请求正文:

用法

RestTemplate template = new RestTemplate(...);

template.getMessageConverters().add(new ObjectToUrlEncodedConverter(mapper));
Run Code Online (Sandbox Code Playgroud)

ObjectToUrlEncodedConverter

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;

public class ObjectToUrlEncodedConverter implements HttpMessageConverter
{
    private static final String Encoding = "UTF-8";

    private final ObjectMapper mapper;

    public ObjectToUrlEncodedConverter(ObjectMapper mapper)
    {
        this.mapper = mapper;
    }

    @Override
    public boolean canRead(Class clazz, MediaType mediaType)
    {
        return false;
    }

    @Override
    public boolean canWrite(Class clazz, MediaType mediaType)
    {
        return getSupportedMediaTypes().contains(mediaType);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes()
    {
        return Collections.singletonList(MediaType.APPLICATION_FORM_URLENCODED);
    }

    @Override
    public Object read(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException
    {
        throw new NotImplementedException();
    }

    @Override
    public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException
    {
        if (o != null)
        {
            String body = mapper
                .convertValue(o, UrlEncodedWriter.class)
                .toString();

            try
            {
                outputMessage.getBody().write(body.getBytes(Encoding));
            }
            catch (IOException e)
            {
                // if UTF-8 is not supporter then I give up
            }
        }
    }

    private static class UrlEncodedWriter
    {
        private final StringBuilder out = new StringBuilder();

        @JsonAnySetter
        public void write(String name, Object property) throws UnsupportedEncodingException
        {
            if (out.length() > 0)
            {
                out.append("&");
            }

            out
                .append(URLEncoder.encode(name, Encoding))
                .append("=");

            if (property != null)
            {
                out.append(URLEncoder.encode(property.toString(), Encoding));
            }
        }

        @Override
        public String toString()
        {
            return out.toString();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Esameldean 只需创建一个 ObjectMapper 实例,如下所示: ObjectMapper mapper = new ObjectMapper(); restTemplate.getMessageConverters().add(new ObjectToUrlEncodedConverter(mapper)); (2认同)

小智 5

您可以尝试使用https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/FormHttpMessageConverter.html

RestTemplate template = new RestTemplate(...);
template.getMessageConverters().add(new org.springframework.http.converter.FormHttpMessageConverter.FormHttpMessageConverter());
Run Code Online (Sandbox Code Playgroud)