Feign Form表單POST提交


Form表單的POST提交,調用該類接口最長用的方式就是HttpClient,如果使用Feign,如何實現呢?

首先,看下Http中已Form的形式做Post提交的定義:

-----------------------------------華麗的分割線------------------------------------------

POST提交

HTTP 協議是以 ASCII 碼傳輸,建立在 TCP/IP 協議之上的應用層規范。規范把 HTTP 請求分為三個部分:狀態行、請求頭、消息主體。類似於下面這樣:

<method> <request-URL> <version>
<headers>
<entity-body>

協議規定 POST 提交的數據必須放在消息主體(entity-body)中,但協議並沒有規定數據必須使用什么編碼方式。實際上,開發者完全可以自己決定消息主體的格式,只要最后發送的 HTTP 請求滿足上面的格式就可以。

但是,數據發送出去,還要服務端解析成功才有意義。一般服務端語言如 php、python 等,以及它們的 framework,都內置了自動解析常見數據格式的功能。服務端通常是根據請求頭(headers)中的 Content-Type 字段來獲知請求中的消息主體是用何種方式編碼,再對主體進行解析。所以說到 POST 提交數據方案,包含了 Content-Type 和消息主體編碼方式兩部分。下面就正式開始介紹它們。

application/x-www-form-urlencoded   <---Form表單提交
這應該是最常見的 POST 提交數據的方式了。瀏覽器的原生 form 表單,如果不設置 enctype 屬性,那么最終就會以 application/x-www-form-urlencoded 方式提交數據。請求類似於下面這樣(無關的請求頭在本文中都省略掉了):

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

 

首先,Content-Type 被指定為 application/x-www-form-urlencoded;
其次,提交的數據按照 key1=val1&key2=val2 的方式進行編碼,key 和 val 都進行了 URL 轉碼。
大部分服務端語言都對這種方式有很好的支持。例如 PHP 中,$_POST[‘title’] 可以獲取到 title 的值,$_POST[‘sub’] 可以得到 sub 數組。
很多時候,我們用 Ajax 提交數據時,也是使用這種方式。例如 JQuery 和 QWrap 的 Ajax,Content-Type 默認值都是「application/x-www-form-urlencoded;charset=utf-8」。

multipart/form-data
這又是一個常見的 POST 數據提交的方式。我們使用表單上傳文件時,必須讓 form 的 enctyped 等於這個值。
這種方式一般用來上傳文件,各大服務端語言對它也有着良好的支持。

上面提到的這兩種 POST 數據的方式,都是瀏覽器原生支持的,而且現階段原生 form 表單也只支持這兩種方式。

但是隨着越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行數據交互之后,我們完全可以定義新的數據提交方式,給開發帶來更多便利。

 

application/json
application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端消息主體是序列化后的 JSON 字符串。由於 JSON 規范的流行,除了低版本 IE 之外的各大瀏覽器都原生支持 JSON.stringify,服務端語言也都有處理 JSON 的函數,使用 JSON 不會遇上什么麻煩。

POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}

 

text/xml
它是一種使用 HTTP 作為傳輸協議,XML 作為編碼方式的遠程調用規范。
XML-RPC 協議簡單、功能夠用,各種語言的實現都有。它的使用也很廣泛,如 WordPress 的 XML-RPC Api,搜索引擎的 ping 服務等等。JavaScript 中,也有現成的庫支持以這種方式進行數據交互,能很好的支持已有的 XML-RPC 服務。不過,我個人覺得 XML 結構還是過於臃腫,一般場景用 JSON 會更靈活方便。

-----------------------------------華麗的分割線------------------------------------------

通過上面對POST提交的簡單介紹,對Post提交Form表單已經有了認識:

1、Content-Type 被指定為 application/x-www-form-urlencoded;

2、提交的數據按照 key1=val1&key2=val2 的方式進行編碼,key 和 val 都進行了 URL 轉碼

 

Feign代碼實現樣例1

@FeignClient(name = "authentication", url = "${feign.url.authentication}")
public interface AuthenticationService {
    @RequestMapping(value = "/authentication/idcard_internal_apply",
            method = RequestMethod.POST,
            consumes = "application/x-www-form-urlencoded;charset=UTF-8")
    RealNameResponse verify(String entityBody);
}

測試調用:

@RunWith(SpringJUnit4ClassRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持
@SpringBootTest(classes = MasApplication.class)
public class NewMyTest {
    @Autowired
    private AuthenticationService authenticationService;

    @Test
    public void testRealName() throws Exception {
        Map<String, String> reqParms =  new HashMap<>();
        reqParms.put("merchantNo", "245888");
        reqParms.put("idNo", "310123198901233456");
        reqParms.put("realName", "李大庄");
        reqParms.put("merchantOrderNo", DateUtil.format(new Date(), DateUtil.longFormat));
        reqParms.put("reqSource", "ZF952_API");
        //===========拼裝數據為:k=v&k=v 格式===========
        StringBuffer v=new StringBuffer();
        for(Map.Entry<String, String> entries: reqParms.entrySet()){
            v.append(entries.getKey()).append("=").append(entries.getValue()).append("&");
        }
        System.out.println("---------------"+v.substring(0,v.length()-1));
        RealNameResponse response = authenticationService.verify(v.substring(0,v.length()-1));
        System.out.println("RESPONSE--------------->"+response);
    }
}

 

很明顯代碼有點挫,先構建了Map對象,然后遍歷拼裝k=v&k=v格式數據,是否可以直接支持Map數據的傳參呢,當然可以

Feign代碼實現樣例2

/**
 * Description: Form表單提交
 *
 * @author: lishaohua
 * @date: 2018/5/28  9:50
 */
@ConfigurationProperties("feign")
public class FeignFormConfiguration {
    private Integer connectionTimeout = 5000;
    private Integer readTimeout = 10000;
    private Integer retry = 0;
    private Long period = 100L;
    private Long maxPeriod = 1000L;

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    @Bean
    @Scope("prototype")
    public Encoder feignFormEncoder() {
        Encoder encoder = new FormEncoder(new SpringEncoder(this.messageConverters));
        return encoder;
    }


    public Request.Options feignOptions() {
        return new Request.Options(connectionTimeout, readTimeout);
    }

    public Retryer feignRetryer() {
        if (retry > 0) {
            // the first call is also one attempt try
            return new Retryer.Default(period, maxPeriod, retry + 1);
        } else {
            return Retryer.NEVER_RETRY;
        }
    }

    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    public Integer getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(Integer connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public Integer getReadTimeout() {
        return readTimeout;
    }

    public void setReadTimeout(Integer readTimeout) {
        this.readTimeout = readTimeout;
    }

    public Integer getRetry() {
        return retry;
    }

    public void setRetry(Integer retry) {
        this.retry = retry;
    }

    public Long getPeriod() {
        return period;
    }

    public void setPeriod(Long period) {
        this.period = period;
    }

    public Long getMaxPeriod() {
        return maxPeriod;
    }

    public void setMaxPeriod(Long maxPeriod) {
        this.maxPeriod = maxPeriod;
    }
}
@FeignClient(name = "authentication", url = "${feign.url.authentication}",
        configuration = FeignFormConfiguration.class)
public interface AuthenticationService {
    @RequestMapping(value = "/authentication/idcard_internal_apply",
            method = RequestMethod.POST,
            consumes = "application/x-www-form-urlencoded;charset=UTF-8")
    RealNameResponse verify(Map<String,?> entityBody);
}

測試

@RunWith(SpringJUnit4ClassRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持
@SpringBootTest(classes = MasApplication.class)
public class NewMyTest {
    @Autowired
    private AuthenticationService authenticationService;

    @Test
    public void testRealName() throws Exception {
        Map<String, String> reqParms =  new HashMap<>();
        reqParms.put("merchantNo", "245888");
        reqParms.put("idNo", "310123198901233456");
        reqParms.put("realName", "李大庄");
        reqParms.put("merchantOrderNo", DateUtil.format(new Date(), DateUtil.longFormat));
        reqParms.put("reqSource", "ZF952_API");
       
        RealNameResponse response = authenticationService.verify(reqParms);
        System.out.println("RESPONSE--------------->"+response);
    }
}

 

為了更好的理解POST提交后各個參數的含義,建議使用大白鯊抓包。。。。

參考文章:

四種常見的POST提交方式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM