說明
在之前的博文《OkHttp的高級封裝Feign學習(一): Feign註解的使用》中,我簡單介紹了OpenFeign的使用方式。其中在請求傳遞參數時,可以使用@Param和@QueryMap註解。本篇博文我將介紹學習如何使用OpenFeign進行表單參數提交或者傳輸文件。
正文
我們先看下之前示例中只使用@Param和@QueryMap的侷限性:
@Param註解用來解析其他註解中的參數表達式,一般用於restful風格的請求方式中。@QueryMap註解則是用來定義參數的Map集合或者是POJO。這兩種方式定義的參數在請求時都會直接拼接在連接中,如 http:xxx?name1=value1&name2=value2。
在使用時我們希望以POST方式將參數以表單的形式提交。這裏我做了以下嘗試,將請求方式改爲POST,並配置請求頭ContentType爲application/x-www-form-urlencoded,方法的參數使用@Param標記並使用默認的encoder。
@RequestLine("POST /test/hello2")
@Headers("Content-Type: application/x-www-form-urlencoded")
ResultPojo hello2Post(@Param("name") String name);
在執行時拋出異常,默認的ecoder不支持LinkedHashMap的編碼:
feign.codec.EncodeException: class java.util.LinkedHashMap is not a type supported by this encoder.
可以發現只將ContentType設置爲application/x-www-form-urlencoded並不能實現表單提交,我們需要配置特殊的encoder。對此,OpenFeign官方在feign-form項目中爲我們提供了Form Encoder。
Form Encoder
OpenFeign提供的該模塊支持表單application/x-www-form-urlencoded 和 multipart/form-data 兩種編碼。
在使用前需要添加依賴:
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
之後可以直接配置Encoder
HelloService service = Feign.builder()
.logger(new Slf4jLogger())
.logLevel(Logger.Level.FULL)
.client(new OkHttpClient())
// .encoder(new JacksonEncoder())
.encoder(new FormEncoder())
.decoder(new JacksonDecoder())
.requestInterceptor(new HeadersInterceptor())
.errorDecoder(new CustomErrorDecoder())
.retryer(new MyRetryer(5))
.exceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP)
.target(HelloService.class, "http://localhost:8080/");
可以看到我把之前的JacksonEncoder註釋後,新配置了FormEncoder。但是我又希望同時使用這兩種Encoder,OpenFeign對此也進行了支持,FormEncoder裝飾JacksonEncoder,在FormEncoder類內部將另一個Ecoder對象作爲類成員。
public class FormEncoder implements Encoder {
private static final String CONTENT_TYPE_HEADER = "Content-Type";
private static final Pattern CHARSET_PATTERN = Pattern.compile("(?<=charset=)([\\w\\-]+)");
private final Encoder delegate;
private final Map<ContentType, ContentProcessor> processors;
public FormEncoder() {
this(new Default());
}
public FormEncoder(Encoder delegate) {
this.delegate = delegate;
List<ContentProcessor> list = Arrays.asList(new MultipartFormContentProcessor(delegate), new UrlencodedFormContentProcessor());
this.processors = new HashMap(list.size(), 1.0F);
Iterator var3 = list.iterator();
while(var3.hasNext()) {
ContentProcessor processor = (ContentProcessor)var3.next();
this.processors.put(processor.getSupportedContentType(), processor);
}
}
.....
}
可以看到FormEncoder的無參構造函數也會創建默認的Encoder對象。我們可以使用以下方式進行配置:
.encoder(new FormEncoder(new JacksonEncoder()))
注意,在使用FormEncoder時,必須配置ContentType。FormEncoder支持兩種類型:
application/x-www-form-urlencoded
在配置Header後,方法的參數我們可以使用@Param註解或者是參數類POJO
@RequestLine("POST /test/hello2")
@Headers("Content-Type: application/x-www-form-urlencoded")
ResultPojo hello2Post(@Param("name") String name);
@RequestLine("POST /test/hello2")
@Headers("Content-Type: application/x-www-form-urlencoded")
ResultPojo hello2Post2(ParamPojo paramPojo);
與之前相同,Pojo的變量名將被作爲key。
multipart/form-data
在上傳文件時需要使用該類型的Content-Type,OpenFeign也提供了幾種使用方式:
@RequestLine("POST /test/file")
@Headers("Content-Type: multipart/form-data")
String uploadFile(@Param("file") File file);
@RequestLine("POST /test/bytedata")
@Headers("Content-Type: multipart/form-data")
String uploadByteData(@Param("bytedata") byte[] data);
@RequestLine("POST /test/formdata")
@Headers("Content-Type: multipart/form-data")
String uploadByFormData(@Param("file") FormData formData);
@RequestLine("POST /test/formpojo")
@Headers("Content-Type: multipart/form-data")
String uploadByPojo(FilePojo filePojo);
在上傳文件時,文件可以支持幾種不同的方法參數類型進行傳輸。
- File 使用文件的擴展名來檢測Content-Type
- byte[] 字節數組將使用application/octet-stream作爲Content-Type
- FormData 將使用FormData設置的Content-Type和fileName
- 自定義POJO
FormData
FormData是OpenFeign定義的請求參數類,該類有是三個成員變量,分別爲contentType, fileName, byte[] data。
@SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public class FormData {
private String contentType;
private String fileName;
private byte[] data;
...
}
在請求時會根據設置值作爲Content-Type和fileName
FormData formData = new FormData("image/png", "filename.png", myDataAsByteArray);
服務端使用MultipartFile file作爲接受參數,設置的fileName將作爲文件的原始名稱。
自定義POJO
通過自定義POJO,可以將所有的請求參數組合到一起,使用@FormProperty來設置表單參數名稱。服務端使用Multipart file和相應的參數進行接收。
public class FilePojo {
@FormProperty("id")
String id;
@FormProperty("filename")
File file;
public FilePojo() {
}
public FilePojo(String id, File file) {
this.id = id;
this.file = file;
}
}
@RequestMapping("test/form-pojo")
public String uploadPojo(@RequestParam(value = "filename") MultipartFile file,
@RequestParam(value = "id") String id) {
return id + file.getOriginalFilename();
}
至此,使用OpenFeign進行表單提交和文件上傳的使用方式已經學習介紹完畢,更詳細的內容請看官方文檔。
源碼地址:https://github.com/Edenwds/javaweb/tree/master/feigndemo