springboot~feign模拟multipart/form-data

Posted Gerald Newton

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot~feign模拟multipart/form-data相关的知识,希望对你有一定的参考价值。

openfeign介绍

在微服务设计里,服务之间的调用是很正常的,通常我们使用httpClient来实现对远程资源的调用,而这种方法需要知识服务的地址,业务接口地址等,而且需要等他开发完成后你才可以去调用它,这对于集成开发来说,不是什么好事 ,产生了A业务与B业务的强依赖性,那么我们如何进行解耦呢,答案就是openfeign框架,它与是springcloudy里的一部分。

    springcloud的服务消费者指的就是服务间的调用,实现的方式有两种:一种就是上一章讲的restTemplate+ribbon,另一种就是本章要讲的feign,feign默认集成了ribbon,所以feign也默认实现了负载均衡。

服务发现/注册里的服务名

通过服务名来进行请求的发送要比配置域名发http更直观,并且你不需要知道它的域名和端口,这也是各个微服务之前直观调用的一种方式,而且A服务可以不依赖于B服务,只要知道接口签名即可。

graph TD
B(服务b)-->C(eureka注册中心)
D-->|在服务a中建立client服务名为服务b|E(openfeign服务端)
A(服务a)-->|配置某个服务中心的服务名称|D(调用服务b的某个接口)
D-->C

添加包引用

'org.springframework.cloud:spring-cloud-starter-openfeign'

添加配置bootstrap.yml

feign:
  client:
    config:
      default:
        connectTimeout: 2000
        readTimeout: 10000

2 定义profile相关配置

//默认的一些文件路径的配置
sourceSets 
    integTest 
        java.srcDir file('src/test/java')
        resources.srcDir file('src/test/resources')
    


task integTest(type: Test) 
    testClassesDirs = sourceSets.test.output.classesDirs
    classpath = sourceSets.test.runtimeClasspath

定义服务接口

定义伪方法,就是服务里的方法,你要知识方法参数和它的返回值,实现不用管,只在单元测试里MOCK就可以.

package test.lind.javaLindDay.feignClientDemo;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * 模拟其他服务.
 */
@Profile("!integTest")
@FeignClient(name = "serviceName",primary=false)
public interface MockClient 
  @GetMapping(path = "/balanceSheet/clientCode")
  String balanceSheet(String clientCode);

Profile的作用

profile就是环境变量,你在类上通过ActiveProfile去激活它,在使用它时,有过Profile注解来使用上,上面代码中MockClient对象不能在integTest环境下使用。

添加MOCK实现,它是自动注入的,所以声明@Bean注解

它是为了在单元测试环境下使用client,而又不希望与外部 网络资源通讯,所以需要mock一下本地资源去实现client.

package test.lind.javaLindDay;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import test.lind.javaLindDay.feignClientDemo.MockClient;

@Configuration
@Profile("integTest")
public class MockClientTest 
  @Bean
  @Primary
  public MockClient mockClient() 
    MockClient client = mock(MockClient.class);
    when(client.balanceSheet(
        anyString()))
        .thenReturn("OK");
    return client;
  

添加单元测试,注意在单元测试上一定要指定它的环境变量

package test.lind.javaLindDay;

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import test.lind.javaLindDay.feignClientDemo.MockClient;

@RunWith(SpringRunner.class)
@SpringBootTest
//指定profile环境
@ActiveProfiles("integTest")
public class JavaLindDayApplicationTests 

  @Autowired
  MockClient mockClient;

  @Test
  public void testMockClient() 
    assertEquals(mockClient.balanceSheet("OK"), "OK");
  

运行测试后,MockClient将会被注入,它将使用Mock实现类,因为只有Mock实现类的Profile是指向integtest环境的。
有了openfeign,以后开发服务对服务调用就可以解耦了!

feignClient发送multipart/form-data请求

需要先安装插件,默认是不能发送文件流的
<dependencies>
    ...
    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form</artifactId>
        <version>3.3.0</version>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form-spring</artifactId>
        <version>3.3.0</version>
    </dependency>
    ...
</dependencies>
添加bean
@FeignClient(name = "file-upload-service", configuration = FileUploadServiceClient.MultipartSupportConfig.class)
public interface FileUploadServiceClient extends IFileUploadServiceClient 

    public class MultipartSupportConfig 

        @Autowired
        private ObjectFactory<HttpMessageConverters> messageConverters;

        @Bean
        public Encoder feignFormEncoder() 
            return new SpringFormEncoder(new SpringEncoder(messageConverters));
        
    

如果不需要Spring标准的编码,也可以这样实现

@FeignClient(name = "file-upload-service", configuration = FileUploadServiceClient.MultipartSupportConfig.class)
public interface FileUploadServiceClient extends IFileUploadServiceClient 

    public class MultipartSupportConfig 

        @Bean
        public Encoder feignFormEncoder() 
            return new SpringFormEncoder();
        
    

添加注解
   // File parameter
   @RequestLine("POST /send_photo")
   @Headers("Content-Type: multipart/form-data")
   void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") File photo);

   // byte[] parameter
   @RequestLine("POST /send_photo")
   @Headers("Content-Type: multipart/form-data")
   void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") byte[] photo);

   // FormData parameter
   @RequestLine("POST /send_photo")
   @Headers("Content-Type: multipart/form-data")
   void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") FormData photo);

在此我向大家推荐一个架构学习交流圈。交流学习微信:539413949(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

以上是关于springboot~feign模拟multipart/form-data的主要内容,如果未能解决你的问题,请参考以下文章

Springboot中调用外部接口的三种方式

SpringBoot 集成Feign的使用

源码模拟Feign RPC动态代理的实现

SpringBoot如何使用Feign实现远程接口调用

企业分布式微服务云SpringCloud SpringBoot mybatis 服务消费者(Feign)

模拟RPC调用(Feign)