首页 > 解决方案 > 使用 Json 请求部分假装多部分

问题描述

我在一项服务中有 Feign 客户,使用一种方法

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
 MyDto uploadDocument(@RequestPart("file") MultipartFile file,
                               @RequestPart("myDto") String myDto);

我在另一个服务中有一个控制器

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<MyDto> uploadDocument(@RequestParam("file") MultipartFile file,
                                                      @RequestPart("myDto") MyDto myDto) {
.... some code here
    }

我面临的问题是 Feign 发送 myDto 与 Content-type : text/plain 我有 HttpMediaTypeNotSupportedException

是否可以@RequestPart("myDto") String myDto使用 Content-type 发送: application/json ?

预期的原始请求:

----------------------------boundary
Content-Disposition: form-data; name="file"; filename="fileName"
<file>
----------------------------boundary
Content-Disposition: form-data; name="myDto"
**Content-Type: application/json**
{"myDto": ""}

当前原始请求:

----------------------------boundary
Content-Disposition: form-data; name="file"; filename="fileName"
<file>
----------------------------boundary
Content-Disposition: form-data; name="myDto"
**Content-Type: text/plain**
{"myDto": ""}

标签: javaspring-cloud-feignfeign

解决方案


通过替换 feign-form PojoWriter 设法解决了这个问题。默认情况下,它将对象的每个字段序列化为单独的部分。

    @Bean
    public Encoder feignEncoder () {
        return new MyFormEncoder(objectMapper, new SpringEncoder(messageConverters));
    }
public class MyFormEncoder extends SpringFormEncoder {
    /**
     * Constructor with specified delegate encoder.
     *
     * @param delegate  delegate encoder, if this encoder couldn't encode object.
     */
    public MyFormEncoder(ObjectMapper objectMapper, Encoder delegate) {
        super(delegate);

        val processor = (MultipartFormContentProcessor) getContentProcessor(MULTIPART);
        processor.addFirstWriter(new MyPojoWriter(objectMapper));
    }
}
@FieldDefaults(level = PRIVATE, makeFinal = true)
public class MyPojoWriter extends AbstractWriter {

    private ObjectMapper objectMapper;

    public MyPojoWriter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean isApplicable(Object object) {
        return isUserPojo(object);
    }

    @Override
    protected void write(Output output, String key, Object value) throws EncodeException {
        var data = "";
        try {
            data = objectMapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
        }
        val string = new StringBuilder()
                .append("Content-Disposition: form-data; name=\"").append(key).append('"').append(CRLF)
                .append("Content-Type: application/json; charset=").append(output.getCharset().name()).append(CRLF)
                .append(CRLF)
                .append(data)
                .toString();

        output.write(string);
    }

    private boolean isUserPojo(@NonNull Object object) {
        val type = object.getClass();
        val typePackage = type.getPackage();
        return typePackage != null && typePackage.getName().startsWith("com.my-package.");
    }
}

推荐阅读