Feign Stub擋板和Mock

Seifon發表於2019-01-28

前言:

在專案開發中,會有呼叫第三方介面的場景。當開發時,對方不願意提供測試伺服器給我們呼叫,或者有的介面會按呼叫次數進行計費。當聯調時,第三方的測試伺服器也可能會出現不穩定,如果他們的服務掛了,我們就一直等著服務恢復,那麼這就相當影響效率了。如果我們在開發時,就定義一個擋板或者mock服務,在發起呼叫時,不直接調到第三方介面,而是調到我們自己的擋板程式碼或者mock服務,這樣就可以避免這些問題了。

優勢:

  • 擋板程式碼,不需要侵入業務程式碼,可以根據入參做一些動態結果返回
  • 不需要專門開發一個擋板服務,並且在每次啟動客戶端都先啟動擋板服務
  • 可以自由選擇使用擋板還是Mock資料

Demo詳細程式碼,已經提交到Github,歡迎star

Demo地址: github.com/Seifon/Feig…


一、下面我就以一個第三方SMS簡訊介面來做演示:

首先,我們寫一個Feign客戶端介面,正常呼叫第三方介面:

1.定義一個SMS簡訊的Feign客戶端介面:
import cn.seifon.example.feignstubmock.dto.YunxunSmsReqDto;
import cn.seifon.example.feignstubmock.dto.YunxunSmsRespDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * @Author: Seifon
 * @Description:
 * @Date: Created in 10:24 2019/1/7
 */
@FeignClient(name = "smsclient", url = "${sms.url}", primary = false)
public interface YunxunSmsFeign {

    /**
     *
     * @param request
     * @return {"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}
     * @return {"code":"107","msgId":"","time":"20190125162358","errorMsg":"手機號碼格式錯誤"}
     */
    @PostMapping("/msg/variable/json")
    YunxunSmsRespDto send(@RequestBody YunxunSmsReqDto request);
}

複製程式碼

注意:@FeignClient註解裡面的primary屬性一定要設定為false,這是為了防止在開啟Feign擋板時,出現多個Feign客戶端導致啟動報錯。

2.寫一個單元測試:
import cn.seifon.example.feignstubmock.dto.YunxunSmsReqDto;
import cn.seifon.example.feignstubmock.dto.YunxunSmsRespDto;
import cn.seifon.example.feignstubmock.feign.YunxunSmsFeign;
import com.alibaba.fastjson.JSON;
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.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class FeignStubMockApplicationTests {
    @Autowired
    private YunxunSmsFeign yunxunSmsFeign;

    @Test
    public void feignStubMockTest() {
        YunxunSmsReqDto yunxunSmsReqDto=new YunxunSmsReqDto();
        yunxunSmsReqDto.setAccount("XXXXXXX");
        yunxunSmsReqDto.setPassword("XXXXXXX");
        yunxunSmsReqDto.setMsg("登入驗證碼:{$var},請不要對非本人透露。");
        yunxunSmsReqDto.setParams("13011112222,123456");
        yunxunSmsReqDto.setReport("true");

        YunxunSmsRespDto send = yunxunSmsFeign.send(yunxunSmsReqDto);
        
        //列印結果
        System.out.println(JSON.toJSON(send));
    }

}
複製程式碼
3.1.我們輸入一個正確的手機號,拿一個成功的結果:
2019-01-28 11:17:56.718 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] ---> POST http://smssh1.253.com/msg/variable/json HTTP/1.1
2019-01-28 11:17:56.719 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] Content-Type: application/json;charset=UTF-8
2019-01-28 11:17:56.720 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] Content-Length: 160
2019-01-28 11:17:56.720 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] 
2019-01-28 11:17:56.721 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] {"account":"XXXXXX","password":"XXXXXXX","msg":"登入驗證碼:{$var},請不要對非本人透露。","params":"17311112222,123456","report":"true"}
2019-01-28 11:17:56.721 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] ---> END HTTP (160-byte body)
2019-01-28 11:17:56.958 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] <--- HTTP/1.1 200 OK (236ms)
2019-01-28 11:17:56.960 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] connection: keep-alive
2019-01-28 11:17:56.962 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] content-length: 109
2019-01-28 11:17:56.963 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] content-type: application/json;charset=UTF-8
2019-01-28 11:17:56.965 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] date: Mon, 28 Jan 2019 03:17:56 GMT
2019-01-28 11:17:56.966 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] 
2019-01-28 11:17:56.971 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] {"code":"0","failNum":"0","successNum":"1","msgId":"19012811175621982","time":"20190128111756","errorMsg":""}
2019-01-28 11:17:56.972 DEBUG 6920 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] <--- END HTTP (109-byte body)
{"code":"0","failNum":"0","successNum":"1","msgId":"19012811175621982","time":"20190128111756","errorMsg":""}
複製程式碼

此時,我們可以根據日誌,看到請求的地址也是第三方的url

3.2.我們輸入一個錯誤的手機號,拿一個失敗的結果:
2019-01-28 11:21:15.300 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] ---> POST http://smssh1.253.com/msg/variable/json HTTP/1.1
2019-01-28 11:21:15.301 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] Content-Type: application/json;charset=UTF-8
2019-01-28 11:21:15.302 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] Content-Length: 152
2019-01-28 11:21:15.302 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] 
2019-01-28 11:21:15.303 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] {"account":"XXXXX","password":"XXXXXXX","msg":"登入驗證碼:{$var},請不要對非本人透露。","params":"173,123456","report":"true"}
2019-01-28 11:21:15.303 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] ---> END HTTP (152-byte body)
2019-01-28 11:21:15.470 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] <--- HTTP/1.1 200 OK (165ms)
2019-01-28 11:21:15.471 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] connection: keep-alive
2019-01-28 11:21:15.473 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] content-length: 87
2019-01-28 11:21:15.474 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] content-type: application/json;charset=UTF-8
2019-01-28 11:21:15.476 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] date: Mon, 28 Jan 2019 03:21:15 GMT
2019-01-28 11:21:15.477 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] 
2019-01-28 11:21:15.483 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] {"code":"107","msgId":"","time":"20190128112115","errorMsg":"手機號碼格式錯誤"}
2019-01-28 11:21:15.484 DEBUG 5288 --- [           main] c.s.e.f.feign.YunxunSmsFeign             : [YunxunSmsFeign#send] <--- END HTTP (87-byte body)
{"code":"107","msgId":"","time":"20190128112115","errorMsg":"手機號碼格式錯誤"}
複製程式碼

當我們知道了兩種情況下出現的結果,那麼我們就可以模擬響應結果啦。小技巧:我們可以先跟對方調介面,把各種響應報文儲存下來,方便後面直接mock資料


二、接下來進入擋板編寫環節:

1.編寫一個YunxunSmsFeignStub類,並實現YunxunSmsFeign介面:
import cn.seifon.example.feignstubmock.dto.YunxunSmsReqDto;
import cn.seifon.example.feignstubmock.dto.YunxunSmsRespDto;
import cn.seifon.example.feignstubmock.feign.YunxunSmsFeign;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Author: Seifon
 * @Description:
 * @Date: Created in 10:24 2019/1/7
 */
@Primary //注意:需要在原Feign介面@FeignClient註解加入primary = false 屬性
@Component
@ConditionalOnProperty(name = "feign-stub.yunxun.sms.mode", havingValue = "stub")
public class YunxunSmsFeignStub implements YunxunSmsFeign {
    private static final Logger LOG = LoggerFactory.getLogger(YunxunSmsFeignStub.class);

    @Override
    public YunxunSmsRespDto send(YunxunSmsReqDto request) {
        YunxunSmsRespDto yunxunSmsRespDto = new YunxunSmsRespDto();

        //模擬正常響應結果
        yunxunSmsRespDto.setCode("0");
        yunxunSmsRespDto.setFailNum("0");
        yunxunSmsRespDto.setSuccessNum("1");
        yunxunSmsRespDto.setMsgId(String.valueOf(RandomUtils.nextLong(19000000000000000L, 19999999999999999L)));
        yunxunSmsRespDto.setTime(DateFormatUtils.format(new Date(), "yyyyMMddHHmmss"));
        yunxunSmsRespDto.setErrorMsg("");

        String params = request.getParams();
        String[] paramSplit = StringUtils.split(params, ",");
        if (paramSplit[0].length() != 11) {
            //模擬錯誤響應結果
            yunxunSmsRespDto.setCode("107");
            yunxunSmsRespDto.setMsgId("");
            yunxunSmsRespDto.setErrorMsg("手機號碼格式錯誤");
        }
        return yunxunSmsRespDto;
    }
}
複製程式碼

注意:必須標註@Primary註解,否則啟動會報錯。@ConditionalOnProperty的作用就是根據application.yaml配置的相關屬性,判斷是否注入Spring容器

2.application.yaml檔案,加入下面的配置:
sms:
    url: 'http://smssh1.253.com'

#yunxun:代表第三方系統名稱,sms:代表業務名稱,mode:代表Stub模式,url:代表mock服務地址
feign-stub:
    yunxun:
        sms:
            mode: 'stub'
複製程式碼
3.為了區分返回的內容是擋板結果,我們可以寫一個AOP切面列印日誌:
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @Author: Seifon
 * @Description:
 * @Date: Created in 10:24 2019/1/7
 */
@Aspect
@Component
public class FeignStubAspect {

    private static final Logger LOG = LoggerFactory.getLogger(FeignStubAspect.class);

    @Pointcut("execution(* cn.seifon.example.feignstubmock..stub.*.*(..))")
    public void pointCut(){}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp){
        String name = StringUtils.join(pjp.getTarget().getClass().getName(), ".", pjp.getSignature().getName());
        LOG.info("-----【{}】---- 進入擋板模式... request: 【{}】", name, JSON.toJSON(pjp.getArgs()));
        try {
            Object proceed = pjp.proceed();
            LOG.info("-----【{}】---- 退出擋板模式... request: 【{}】, response: 【{}】", name, JSON.toJSON(pjp.getArgs()), JSON.toJSON(proceed));
            return proceed;
        } catch (Throwable e) {
            e.printStackTrace();
        }

        return null;
    }

}

複製程式碼
4.1.執行之前寫的單元測試程式碼(輸入一個正確的手機號):
2019-01-28 11:32:51.255  INFO 7488 --- [           main] c.s.e.f.aspect.FeignStubAspect           : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 進入擋板模式... request: 【[{"msg":"登入驗證碼:{$var},請不要對非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】
2019-01-28 11:32:51.975  INFO 7488 --- [           main] c.s.e.f.aspect.FeignStubAspect           : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 退出擋板模式... request: 【[{"msg":"登入驗證碼:{$var},請不要對非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】, response: 【{"code":"0","failNum":"0","successNum":"1","msgId":"19148964234899564","time":"20190128113251","errorMsg":""}】
{"code":"0","failNum":"0","successNum":"1","msgId":"19148964234899564","time":"20190128113251","errorMsg":""}
複製程式碼
4.2.執行之前寫的單元測試程式碼(輸入一個錯誤的手機號):
2019-01-28 11:35:27.177  INFO 15204 --- [           main] c.s.e.f.aspect.FeignStubAspect           : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 進入擋板模式... request: 【[{"msg":"登入驗證碼:{$var},請不要對非本人透露。","password":"XXXXXXX","report":"true","params":"130,123456","account":"XXXXXXX"}]】
2019-01-28 11:35:27.900  INFO 15204 --- [           main] c.s.e.f.aspect.FeignStubAspect           : -----【cn.seifon.example.feignstubmock.feign.stub.YunxunSmsFeignStub.send】---- 退出擋板模式... request: 【[{"msg":"登入驗證碼:{$var},請不要對非本人透露。","password":"XXXXXXX","report":"true","params":"130,123456","account":"XXXXXXX"}]】, response: 【{"code":"107","failNum":"0","successNum":"1","msgId":"","time":"20190128113527","errorMsg":"手機號碼格式錯誤"}】
{"code":"107","failNum":"0","successNum":"1","msgId":"","time":"20190128113527","errorMsg":"手機號碼格式錯誤"}
複製程式碼

以上程式碼就完成了一個stub擋板功能,可有時候,我們已經拿到第三方介面的返回報文,並切不想去寫一大段Stub程式碼。那麼這個時候,我們就可以選擇下面的Mock方式去完成我們的功能。


三、接下來進入Mock環節:

1. 首先準備一個mock服務,這裡我就用自己比較喜歡的一個mock工具(mock-json-server)給大家演示:
1.1 安裝nodejs:
參看官網:http://nodejs.cn/
複製程式碼
1.2 安裝mock-json-server:
npm install -g mock-json-server
複製程式碼
1.3 新建mock資料檔案(命名為:data.json):
{
  "/msg/variable/json": {
    "post": {
      "code":"0",
      "failNum":"0",
      "successNum":"1",
      "msgId":"19012516213625881",
      "time":"20190125162136",
      "errorMsg":""
    }
  }
}
複製程式碼
1.4 執行:
mock-json-server {path}/data.json --port=1240

{path}替換為存放data.json的絕對路徑
複製程式碼
1.5 如果顯示如下結果,就代表mock服務執行成功:
JSON Server running at http://localhost:1240/
複製程式碼

mock-json-server具體使用文件,請參考:www.npmjs.com/package/moc…

2. 準備工作做好後,接下來,就進入Mock正式環節:
2.1 首先,我們定義一個YunxunSmsFeignMock介面,並且繼承YunxunSmsFeign介面
import cn.seifon.example.feignstubmock.feign.YunxunSmsFeign;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

/**
 * @Author: Seifon
 * @Description:
 * @Date: Created in 10:24 2019/1/7
 */
@Primary //注意:需要在原Feign介面@FeignClient註解加入primary = false 屬性
@Component
@ConditionalOnProperty(name = "feign-stub.yunxun.sms.mode", havingValue = "mock")
@FeignClient(name = "smsclient-mock", url = "${feign-stub.yunxun.sms.mockUrl}" ,path = "/")
public interface YunxunSmsFeignMock extends YunxunSmsFeign {

}

複製程式碼

注意:必須標註@Primary註解,否則啟動時會報錯。@FeignClient裡的name屬性不能跟原Feign介面名稱相同,如果相同會啟動報錯。@ConditionalOnProperty的作用就是根據application.yaml配置的相關屬性,判斷是否注入Spring容器

2.2 application.yaml檔案,加入下面的配置:
sms:
    url: 'http://smssh1.253.com'

#生產環境請勿新增此配置。mode說明:''-不開啟, 'mock'-mock模式, 'stub'-stub模式。url說明:只有mock模式需要配置除錯url。fund為第三方機構,repayment是業務名稱
#yunxun:代表第三方系統名稱,sms:代表業務名稱,mode:代表擋板模式,url:代表mock服務地址
feign-stub:
    yunxun:
        sms:
            mode: 'mock'
            mockUrl: "http://localhost:1240"
複製程式碼
2.3 為了區分返回的內容是Mock結果,我們可以寫一個AOP切面列印日誌:
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @Author: Seifon
 * @Description:
 * @Date: Created in 10:24 2019/1/7
 */
@Aspect
@Component
public class FeignMockAspect {

    private static final Logger LOG = LoggerFactory.getLogger(FeignMockAspect.class);

    @Pointcut("execution(* cn.seifon.example.feignstubmock..mock.*.*(..))")
    public void pointCut(){}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp){
        String name = StringUtils.join(pjp.getTarget().getClass().getName(), ".", pjp.getSignature().getName());
        LOG.info("-----【{}】---- 進入Mock模式... request: 【{}】", name, JSON.toJSON(pjp.getArgs()));
        try {
            Object proceed = pjp.proceed();
            LOG.info("-----【{}】---- 退出Mock模式... request: 【{}】, response: 【{}】", name, JSON.toJSON(pjp.getArgs()), JSON.toJSON(proceed));
            return proceed;
        } catch (Throwable e) {
            e.printStackTrace();
        }

        return null;
    }

}
複製程式碼
2.4 執行之前的單元測試類,得到如下結果:
2019-01-28 16:16:35.567  INFO 8976 --- [           main] c.s.e.f.aspect.FeignMockAspect           : -----【com.sun.proxy.$Proxy95.send】---- 進入Mock模式... request: 【[{"msg":"登入驗證碼:{$var},請不要對非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】
2019-01-28 16:16:35.934 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] ---> POST http://localhost:1240/msg/variable/json HTTP/1.1
2019-01-28 16:16:35.935 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] Content-Type: application/json;charset=UTF-8
2019-01-28 16:16:35.936 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] Content-Length: 152
2019-01-28 16:16:35.936 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] 
2019-01-28 16:16:35.937 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] {"account":"XXXXXXX","password":"XXXXXXX","msg":"登入驗證碼:{$var},請不要對非本人透露。","params":"13011112222,123456","report":"true"}
2019-01-28 16:16:35.937 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] ---> END HTTP (152-byte body)
2019-01-28 16:16:36.021 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] <--- HTTP/1.1 200 OK (82ms)
2019-01-28 16:16:36.021 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] access-control-allow-origin: *
2019-01-28 16:16:36.022 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] connection: keep-alive
2019-01-28 16:16:36.023 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] content-length: 109
2019-01-28 16:16:36.023 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] content-type: application/json; charset=utf-8
2019-01-28 16:16:36.024 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] date: Mon, 28 Jan 2019 08:16:36 GMT
2019-01-28 16:16:36.024 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] etag: W/"6d-XqhLoZB8r6IRF2Lb6CWoIVVNhIQ"
2019-01-28 16:16:36.025 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] x-content-type-options: nosniff
2019-01-28 16:16:36.026 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] x-powered-by: Express
2019-01-28 16:16:36.027 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] 
2019-01-28 16:16:36.030 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] {"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}
2019-01-28 16:16:36.030 DEBUG 8976 --- [           main] c.s.e.f.feign.mock.YunxunSmsFeignMock    : [YunxunSmsFeignMock#send] <--- END HTTP (109-byte body)
2019-01-28 16:16:36.227  INFO 8976 --- [           main] c.s.e.f.aspect.FeignMockAspect           : -----【com.sun.proxy.$Proxy95.send】---- 退出Mock模式... request: 【[{"msg":"登入驗證碼:{$var},請不要對非本人透露。","password":"XXXXXXX","report":"true","params":"13011112222,123456","account":"XXXXXXX"}]】, response: 【{"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}】
{"code":"0","failNum":"0","successNum":"1","msgId":"19012516213625881","time":"20190125162136","errorMsg":""}
複製程式碼

說明:此時我們根據日誌,會發現feign呼叫的url已經變為我們的Mock服務地址了。同理,如果要返回失敗結果,只需要修改data.json檔案,再次呼叫後,即可得到我們想要的結果了。


四、結語:

如果有什麼需要改進的地方,或者不正確的地方,請在評論裡面提出並指正。謝謝!

Demo詳細程式碼,已經提交到Github,歡迎star

Demo地址: github.com/Seifon/Feig…

專案結構,如圖:

Feign Stub擋板和Mock

原文地址:www.seifon.cn/2019/01/28/…

相關文章