OpenFeign學習(六):OpenFign進行表單提交參數或傳輸文件

說明

在之前的博文《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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章