在之前的博文《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。
OpenFeign提供的该模块支持表单application/x-www-form-urlencoded 和 multipart/form-data 两种编码。
在使用前需要添加依赖:
io.github.openfeign.form
feign-form
3.8.0
之后可以直接配置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 processors;
public FormEncoder() {
this(new Default());
}
public FormEncoder(Encoder delegate) {
this.delegate = delegate;
List 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支持两种类型:
在配置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。
在上传文件时需要使用该类型的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);
在上传文件时,文件可以支持几种不同的方法参数类型进行传输。
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,可以将所有的请求参数组合到一起,使用@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