我在使用 Spring Cloud Openfeign 上传地图时遇到特殊情况<String, MultipartFile>

问题描述 投票:0回答:1
我有以下端点:

// inside @RestController annotated class @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Object> upload(@RequestParam Map<String, MultipartFile> upfiles) throws Exception { return ResponseEntity.ok(new Object()); }
在另一个微服务中,我创建了一个假客户端来触发上述端点:

// inside @FeginClient annotated class @PostMapping( value = "/upload", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE ) ResponseEntity<UploadResponse> uploadDocumentToSign(@Valid @RequestParam Map<String, MultipartFile> upfiles);
上述调用失败并显示消息“无法解析多部分 servlet 请求”
所以我从邮递员那里尝试了,从邮递员那里工作的唯一方法如下:

enter image description here

如您所见,地图作为表单数据放置在主体上,而不是方法定义所暗示的查询参数

所以我尝试了与 feign 客户端相同的方法:

//inside @FeginClient annotated class @PostMapping( value = "/upload", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) ResponseEntity<UploadResponse> uploadDocumentToSign(@Valid @RequestBody Map<String, MultipartFile> upfiles);
但这并不起作用,请求已到达服务器,但 Controller 类中的 Map 为空,即使在创建了 

SpringFormEncoder

 类型的编码器 Bean 之后也是如此。
似乎 
SpringFormEncoder
 不知道如何处理具有 Map
类型主体的多部分/表单数据请求,所以我必须创建自己的 SpringFormEncoder
:

public class DocumentUploadSpringFormEncoder extends SpringFormEncoder { public DocumentUploadSpringFormEncoder() { } public DocumentUploadSpringFormEncoder(Encoder delegate) { super(delegate); } @Override public void encode (Object object, Type bodyType, RequestTemplate template) throws EncodeException { if (bodyType.equals(MultipartFile[].class)) { val files = (MultipartFile[]) object; val data = new HashMap<String, Object>(files.length, 1.F); for (val file : files) { data.put(file.getName(), file); } super.encode(data, MAP_STRING_WILDCARD, template); } else if (bodyType.equals(MultipartFile.class)) { val file = (MultipartFile) object; val data = singletonMap(file.getName(), object); super.encode(data, MAP_STRING_WILDCARD, template); } else if (isMultipartFileCollection(object)) { val iterable = (Iterable<?>) object; val data = new HashMap<String, Object>(); for (val item : iterable) { val file = (MultipartFile) item; data.put(file.getName(), file); } super.encode(data, MAP_STRING_WILDCARD, template); } else if (isMultipartFileMap(object)) { val map = (Map<?, ?>) object; val entrySet = map.entrySet(); val data = new HashMap<String, Object>(); for (Map.Entry entry : entrySet) { val file = (MultipartFile) entry.getValue(); val key = (String) entry.getKey(); data.put(key, file); } super.encode(data, MAP_STRING_WILDCARD, template); } else { super.encode(object, bodyType, template); } } private boolean isMultipartFileCollection (Object object) { if (!(object instanceof Iterable<?> iterable)) { return false; } val iterator = iterable.iterator(); return iterator.hasNext() && iterator.next() instanceof MultipartFile; } private boolean isMultipartFileMap (Object object) { if (!(object instanceof Map<?, ?> map)) { return false; } val entrySet = map.entrySet(); for (Map.Entry entry : entrySet) { if (!(entry.getValue() instanceof MultipartFile)) { return false; } } return true; } }
直到现在一切才正常。
我的问题是:

    这是接受 Map 的方式吗
  1. @RequestParam Map<String, MultipartFile> upfiles
    ,当你实际上需要将其放入正文中时为什么要@RequestParam?
  2. 如果这是正确的,为什么你在控制器中以一种方式定义它,而在客户端使用 spring openfegin (仍然是pring框架......)以另一种方式定义它 附带说明一下,我们使用 openapi,无法通过 swagger UI 到达 enpoint,因为它没有正确调用端点,它尝试将地图放置在查询参数上,因此即使 swagger 也无法理解代码。 :)
spring spring-boot spring-cloud-feign openfeign
1个回答
0
投票
您可以尝试使用@RequestPart来代替@RequestParam吗?

Why @RequestPart? For complex mapping eg:images, text. We can use RequestPart to map the value. Example: // inside @RestController annotated class @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Object> upload(@Valid @RequestPart Map<String, MultipartFile> upfiles) throws Exception { return ResponseEntity.ok(new Object()); } //inside @FeginClient annotated class @PostMapping( value = "/upload", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) ResponseEntity<UploadResponse> uploadDocumentToSign(@Valid @RequestPart Map<String, MultipartFile> upfiles);
    
© www.soinside.com 2019 - 2024. All rights reserved.