springboot~feign模擬multipart/form-data
阿新 • • 發佈:2020-12-29
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);