欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java調(diào)用UniHttp接口請求失敗的三種解決策略

 更新時間:2025年09月15日 09:19:05   作者:夜郎king  
在當(dāng)今數(shù)字化時代,不管是內(nèi)部系統(tǒng)之間還是跟外部系統(tǒng)的對接,接口調(diào)用已成為軟件開發(fā)中不可或缺的一部分,本文主要介紹了Java調(diào)用UniHttp接口請求失敗的三種解決策略,希望對大家有所幫助

前言

在當(dāng)今數(shù)字化時代,不管是內(nèi)部系統(tǒng)之間還是跟外部系統(tǒng)的對接,接口調(diào)用已成為軟件開發(fā)中不可或缺的一部分。Java作為一種廣泛使用的編程語言,在接口調(diào)用方面有著豐富的應(yīng)用。在現(xiàn)代軟件架構(gòu)中,分布式系統(tǒng)和微服務(wù)架構(gòu)的廣泛應(yīng)用使得不同模塊之間的通信主要依賴于接口調(diào)用。在之前的博文中,我們講解了許多跟第三方接口對接的詳細(xì)案例,比如如何調(diào)用實(shí)時航班數(shù)據(jù)、接入百度地圖、高德地圖、天地圖、實(shí)時景區(qū)等多源數(shù)據(jù),也講解了一些數(shù)據(jù)接入方式,比如使用抓取器,也詳細(xì)敘述過UniHttp框架來進(jìn)行接口調(diào)用。如果是一些對請求參數(shù)順序沒有限制的接口中,一般來講是可以正常進(jìn)行集成的,而在一些需要二次驗(yàn)簽的接口,比如一些在線地圖的開放平臺中就可以支持二次驗(yàn)簽,以百度地圖為例,需要二次驗(yàn)簽的接口接入流程如下圖所示:

通過上圖可以直觀的看到,二次驗(yàn)簽可以很好的對請求進(jìn)行保護(hù),用戶可以隨時修改二次驗(yàn)簽的密鑰,這樣即使是不小心泄露了AK,也可以很好的避免AK的濫用,客戶端需要使用sk進(jìn)行參數(shù)驗(yàn)簽,然后生成驗(yàn)簽參數(shù),服務(wù)端會使用同樣的sk來進(jìn)行同樣的參數(shù)驗(yàn)證,如果匹配則放行,否則會拒絕訪問,返回驗(yàn)證失敗。在實(shí)際開發(fā)過程中,開發(fā)者們常常會遇到各種問題,其中請求參數(shù)亂序?qū)е陆涌谡{(diào)用失敗便是較為常見的一種。百度SN簽名認(rèn)證是一種常見的接口認(rèn)證方式,它通過在請求中添加特定的簽名參數(shù)來驗(yàn)證請求的合法性。在進(jìn)行接口調(diào)用時,請求參數(shù)的順序?qū)τ诤灻纳芍陵P(guān)重要。如果參數(shù)順序發(fā)生混亂,生成的簽名將與服務(wù)器端的預(yù)期不一致,從而導(dǎo)致接口請求被拒絕。請求參數(shù)亂序問題雖然看似微不足道,但實(shí)際上可能會給開發(fā)工作帶來巨大的困擾。首先,它會導(dǎo)致接口調(diào)用失敗,使得開發(fā)進(jìn)度受阻。開發(fā)者需要花費(fèi)大量時間排查問題,尋找導(dǎo)致亂序的原因。其次,這個問題可能會引發(fā)安全風(fēng)險(xiǎn)。如果簽名認(rèn)證機(jī)制被破壞,可能會導(dǎo)致未經(jīng)授權(quán)的訪問,從而威脅到系統(tǒng)的安全性和穩(wěn)定性。因此,解決請求參數(shù)亂序問題對于確保接口調(diào)用的成功和系統(tǒng)的安全性至關(guān)重要。

本文將以百度SN簽名認(rèn)證為例,深入探討在Java調(diào)用UniHttp接口時,如何解決請求參數(shù)亂序問題,以確保接口請求的成功。在解決問題的同時,也跟開源作者進(jìn)行了咨詢,得到了作者的指導(dǎo),對于問題的解決幫助非常大,因此在這里也簡單講講與開源作者的交流過程及啟發(fā)。

一、場景重現(xiàn)

本節(jié)將詳細(xì)介紹使用UniHttp模式下,接入第三方接口是,遇到SN的驗(yàn)簽方式時會遇到驗(yàn)證失敗的問題。通過重現(xiàn)接口調(diào)用現(xiàn)場,讓大家對發(fā)生問題的背景有一個基本的認(rèn)識,也為后面解決問題留下思考的空間。本節(jié)將從以下三個方面去講解,首先我們按照普通的模式來進(jìn)行UniHttp接口的接口定義,然后詳細(xì)介紹SN模式的接口正式調(diào)用,最后詳細(xì)說明可能遇到的問題。如果大家在使用UniHttp時沒有遇到認(rèn)證問題,那真的恭喜你,可以劃走了。如果您也身處迷茫之中,不妨來這里看看。

1、UniHttp模式下SN接口定義

我們首先按照官方文檔的文檔說明,在UniHttp中進(jìn)行百度搜索接口的調(diào)用。按照UniHttp的處理請求規(guī)范,定義第一版的請求接口,接口定義如下:

package com.yelang.project.thridinterface;
import com.burukeyou.uniapi.http.annotation.HttpApi;
import com.burukeyou.uniapi.http.annotation.param.QueryPar;
import com.burukeyou.uniapi.http.annotation.request.GetHttpInterface;
import com.burukeyou.uniapi.http.core.response.HttpResponse;
@HttpApi
public interface BaiduGeoSearchWithSnService {
	/**
	 * - 百度行政區(qū)劃區(qū)域檢索接口
	 * @param query         檢索關(guān)鍵字。行政區(qū)劃區(qū)域檢索不支持多關(guān)鍵字檢索。如果需要按POI分類進(jìn)行檢索,請將分類通過query參數(shù)進(jìn)行設(shè)置,如query=美食
	 * @param region        檢索行政區(qū)劃區(qū)域(增加區(qū)域內(nèi)數(shù)據(jù)召回權(quán)重,如需嚴(yán)格限制召回?cái)?shù)據(jù)在區(qū)域內(nèi),請搭配使用city_limit參數(shù)),可輸入行政區(qū)劃名或?qū)?yīng)
	 * @param output        輸出格式為json或者xml
	 * @param scope         檢索結(jié)果詳細(xì)程度。取值為1 或空,則返回基本信息;取值為2,返回檢索POI詳細(xì)信息
	 * @param ret_coordtype 返回的坐標(biāo)類型,可選參數(shù),添加后POI返回國測局經(jīng)緯度坐標(biāo)
	 * @param pageSize      單次召回POI數(shù)量,默認(rèn)為10條記錄,最大返回20條
	 * @param pageNum       分頁頁碼,默認(rèn)為0,0代表第一頁
	 * @param ak            開發(fā)者的訪問密鑰,必填項(xiàng)
	 * @param sn            開發(fā)者的權(quán)限簽名
	 * @return
	 */
	@GetHttpInterface(url="https://api.map.baidu.com/place/v2/search")
	public HttpResponse<String> getSearch(@QueryPar("query") String query, @QueryPar("region") String region,
			@QueryPar("output") String output, @QueryPar("scope") String scope,
			@QueryPar("ret_coordtype") String ret_coordtype, @QueryPar("page_size") int pageSize,
			@QueryPar("page_num") int pageNum, @QueryPar("ak") String ak, @QueryPar("sn") String sn);
}

與普通的接口請求一樣,這里按照官方文檔要求,這里只增加一個sn的簽名參數(shù)。在調(diào)用的時候動態(tài)傳入sn參數(shù)實(shí)現(xiàn)接口的調(diào)用。

2、第一次正式調(diào)用

接下來還是常規(guī)操作,我們使用Junit來進(jìn)行接口的測試,使用UniHttp來代理服務(wù)訪問之后,接口的調(diào)用就比較簡單了。直接在測試類中注入service實(shí)例后就可以進(jìn)行服務(wù)的調(diào)用,關(guān)鍵代碼如下:

package com.yelang.project.unihttp;
import java.io.UnsupportedEncodingException;
import java.util.LinkedHashMap;
import java.util.Map;
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;
import com.burukeyou.uniapi.http.core.response.HttpResponse;
import com.yelang.project.thridinterface.BaiduGeoSearchSnProcessorService;
import com.yelang.project.thridinterface.BaiduGeoSearchWithSnService;
import com.yelang.project.thridinterface.signature.BaiduSignature;
/**
 * - 百度檢索2.0接口帶SN訪問服務(wù)示例
 * @author 夜郎king
 *
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class BaiduGeoSearchWithSnServiceCase {
	/**
	 * - 應(yīng)用申請的AK值
	 */
	private static final String AK_VALUE = "yourak";
	/**
	 * - 應(yīng)用申請的SK值
	 */
	private static final String SK_VALUE = "yoursk";
	@Autowired
	private BaiduGeoSearchWithSnService bdGeoSearchWithSnService;
	@Test
	public void searchBySn() throws UnsupportedEncodingException {
		/**
		 * -計(jì)算sn跟參數(shù)對出現(xiàn)順序有關(guān),get請求請使用LinkedHashMap保存<key,value>,該方法根據(jù)key的插入順序排序;
		 * -post請使用TreeMap保存<key,value>,該方法會自動將key按照字母a-z順序排序。
		 * -所以get請求可自定義參數(shù)順序(sn參數(shù)必須在最后)發(fā)送請求,但是post請求必須按照字母a-z順序填充body(sn參數(shù)必須在最后)。
		 * -以get請求為例:https://api.map.baidu.com/geocoder/v2/?address=百度大廈&output=json&ak=yourak,paramsMap中先放入address,
		 * -再放output,然后放ak,放入順序必須跟get請求中對應(yīng)參數(shù)的出現(xiàn)順序保持一致。
		 */
		Map<String, String> paramsMap = new LinkedHashMap<String, String>();
		String api_prefix = "/place/v2/search?";
		//按接口定義順序來創(chuàng)建參數(shù)
		String query = "湘菜";
		String region = "158";// 158表示長沙市
		String output = "json";
		String scope = "2";
		String ret_coordtype = "WGS84";
		int pageSize = 20;
		int pageNum = 0;
		paramsMap.put("query", query);
		paramsMap.put("region", region);
		paramsMap.put("output", output);
		paramsMap.put("scope", scope);
		paramsMap.put("ret_coordtype", ret_coordtype);
		paramsMap.put("page_size", String.valueOf(pageSize));
		paramsMap.put("page_num", String.valueOf(pageNum));
		paramsMap.put("ak", AK_VALUE);
		// 調(diào)用簽名工具生成SN
		BaiduSignature signature = new BaiduSignature(AK_VALUE, SK_VALUE,paramsMap,api_prefix);
		String sn = signature.getSnByMap();
		System.out.println("sn==>" + sn);
		HttpResponse<String> result = bdGeoSearchWithSnService.getSearch(query, region, output, scope,
				ret_coordtype, pageSize, pageNum ,AK_VALUE,sn);
		System.out.println("第一頁的數(shù)據(jù)如下:");
		System.out.println(result);
		System.out.println(result.getBodyResult());
		System.out.println("--------------------------------------------------------------");
	}
}

在這里請注意,這里我們進(jìn)行接口定義時,按照上層接口的定義時參數(shù)的有序性,我們在設(shè)置HashMap時使用的是LinkedHashMap來進(jìn)行保存,讓生成SN值的時候按照順序來生成。萬事俱備只欠東風(fēng),下面我們就可以直接運(yùn)行調(diào)用函數(shù),來測試一下接口是否可以聯(lián)通。

3、第一次遇到211 APP SN校驗(yàn)失敗

通常來說,不出意外的話一定是出了意外。我們本來是充滿了希望,認(rèn)為直接將參數(shù)傳入到UniHttp中進(jìn)行攜帶訪問即可,先來看下運(yùn)行之后,在控制臺中看到以下結(jié)果,如下圖:

首輪宣告失敗,這是我們第一次遇到:{"status":211,"message":"APP SN校驗(yàn)失敗"}。當(dāng)然這個錯誤我們在之前的博客中曾經(jīng)簡單介紹過,在官方文檔中也有所提及。如下表:

211APP SN校驗(yàn)失敗SERVER類型APP有兩種校驗(yàn)方式IP校驗(yàn)和SN校驗(yàn),當(dāng)用戶請求的SN和服務(wù)端計(jì)算出來的SN不相等的時候提示SN校驗(yàn)失敗

出現(xiàn)SN校驗(yàn)失敗的原因就是服務(wù)端的SN值和客戶端計(jì)算的值不一樣,導(dǎo)致不一樣的可能有很多種,比如參數(shù)值順序,還有就是ak和sk的值有變化,這些都是有可能的。那么使用UniHttp請求框架后,為什么會遇到這種問題呢?下面將針對這個問題來進(jìn)行問題定位。

二、問題查找及開源尋求解決方案

程序開發(fā)的過程就是解決問題的過程,遇到請求不通的情況確實(shí)比較容易讓人焦急,但是如果能解決問題,那么自己又能積累更多的實(shí)戰(zhàn)經(jīng)驗(yàn)。遇到問題,我們根據(jù)官網(wǎng)的提示來大膽的猜測問題,即SN的計(jì)算生成問題,究竟是哪里出了問題?上面也分析了一些原因,比如參數(shù)的順序等。既然是開源項(xiàng)目,我們就可以通過源碼的定位來分析問題,也可以通過debug調(diào)試代碼來進(jìn)行輔助。

本節(jié)分享博主如何抽絲剝繭的進(jìn)行問題分析,最終找到了問題的所在。接著帶著問題,通過GitHub上找到了開源作者,并且做了一些交流,通過信息的分享,進(jìn)一步明確了問題的所在,從而解決了問題。這是一個特別有趣的過程,在與開源作者的溝通過程中,也能看到開源作者身上的韌勁和開源精神。

1、抽絲剝繭找到問題所在

既然是參數(shù)生成的問題,那么我們就首先使用Debug的方式來看看到底是發(fā)生了什么?根據(jù)之前的經(jīng)驗(yàn),我們懷疑是請求參數(shù)的順序的問題。因此我們可以在Eclipse中使用Debug的方式來看一下請求的參數(shù)順序到底有沒有問題,請求參數(shù)我們可以在HttpResponse對象中的值中進(jìn)行獲取,UniHttpResponse中可以看到:

在HttpUrl對象中可以看到請求的地址和請求的參數(shù),進(jìn)一步打開這個HttpUrl對象的值:

HttpUrl(
url=https://api.map.baidu.com/place/v2/search?, 
path=, 
anchor=null, 
queryParam={output=json, query=湘菜, scope=2, page_num=0, ak=yourak, s
n=d1abf338a49bd82fca6992f5d2b9f686, 
region=158, ret_coordtype=WGS84, 
page_size=20}, 
pathParam={}
)

請注意,上面參數(shù)中queryParam與我們在接口中定義的順序似乎不太一樣,為了方便對比,我們整理成以下圖片表格:

可以看到在請求發(fā)送過程中,經(jīng)過UniHttp的處理,參數(shù)的順序已經(jīng)發(fā)生了變化,因此可以得出結(jié)論,在請求過程中參數(shù)的順序發(fā)生了變化,由此發(fā)生的請求,服務(wù)端拿到請求參數(shù)后,根據(jù)這個參數(shù)去加密計(jì)算得到的SN,與我們自己計(jì)算的一定是不一樣的,所以就會發(fā)生SN驗(yàn)證失敗的問題。現(xiàn)在基本是可以定位是這個問題。帶著疑問我們就這個開源項(xiàng)目跟作者試著聊一聊。

2、與作者在Issues的交流

帶著問題,打開開源項(xiàng)目的開源地址,UniHttp開源地址。首先我們來看一下有關(guān)這個項(xiàng)目的Issues,或許在我之前已經(jīng)有朋友遇到類似的問題,來看下列表界面:

可以看到暫時還沒有其它的博主有類似的問題,第一條其實(shí)博主本人與開源作者的實(shí)際交流情況。點(diǎn)開交流詳情,是我們的詳細(xì)交流過程:

博主在Issue中描述了個人的使用問題和疑問,并咨詢了作者是否可以設(shè)置順序問題。也是非??焖俚牡玫搅俗髡叩幕貜?fù)

雖然在官方文檔中看到,也親自實(shí)踐過這個方法,但是還是得為作者的迅速點(diǎn)贊。因?yàn)檎娴臎]有多久就得到了作者本文的回復(fù)。

3、開源項(xiàng)目交流有感

UniHttp這個項(xiàng)目非常好用,在服務(wù)接口接入中提供了非常良好的便利性。也因此,使用的朋友非常多。在博主遇到相關(guān)的請求參數(shù)亂序的情況下,及時找到了開源地址,并且給予了留言說明了詳細(xì)情況。中所周知,開源項(xiàng)目一般都是自己的副業(yè)或者第二職業(yè),并不是隨時隨地的要看開源社區(qū)的消息和問題。但是這次的問題反饋和交流,卻是異常的迅速,同時開源作者也指出了解決方法和實(shí)現(xiàn)路徑。這是個人遇到的答復(fù)非常迅速及時的開源項(xiàng)目了,上一個如此迅速的項(xiàng)目是kkfileview。開源不易,希望大家都能找到解決辦法,也能貢獻(xiàn)更多的好項(xiàng)目,以后會更加深入的加入開源項(xiàng)目,為開源做貢獻(xiàn)。

三、朝著正確的方向解決問題

言歸正傳,在開源作者的指導(dǎo)下,我們又回到問題的本身,既然是參數(shù)的問題,那么我們?nèi)绾蝸斫鉀Q呢?找到了正確的方向,接下來就是朝著正確的方向去努力,去找到解決的辦法。這里我們按照從簡單到復(fù)雜,從固化到靈活的三種不同的解決辦法,如果還有更好的方法,也歡迎各位專家、博主在評論區(qū)指出。

1、加密順序按實(shí)際請求參數(shù)求解

首先分享第一種比較簡單的方法,已經(jīng)知道導(dǎo)致驗(yàn)證失敗的原因就是參數(shù)不一致的原因。在UniHttp中,使用以下方式定義的接口:

@GetHttpInterface(url="https://api.map.baidu.com/place/v2/search")
public HttpResponse<String> getSearch(@QueryPar("query") String query, @QueryPar("region") String region,
			@QueryPar("output") String output, @QueryPar("scope") String scope,
			@QueryPar("ret_coordtype") String ret_coordtype, @QueryPar("page_size") int pageSize,
			@QueryPar("page_num") int pageNum, @QueryPar("ak") String ak, @QueryPar("sn") String sn);

通常隨著參數(shù)的定義完成之后,其請求參數(shù)的順序基本就可以預(yù)估了,因此第一種方式實(shí)際上比較簡單,在客戶端加密的時候,把參數(shù)順序按照服務(wù)端的加密順序進(jìn)行處理,服務(wù)端的加密加密順序我們可以使用上一節(jié)中的方式進(jìn)行Debug時進(jìn)行跟蹤。根據(jù)請求的參數(shù)順序做如下調(diào)整:

@Test
public void searchBySn() throws UnsupportedEncodingException {
	Map<String, String> paramsMap = new LinkedHashMap<String, String>();
	String api_prefix = "/place/v2/search?";
	/*
	 * - 按請求順序來加密
	*/
	String output = "json";
	String query = "湘菜";
	String scope = "2";
	int pageNum = 0;
	String region = "158";// 158表示長沙市
	String ret_coordtype = "WGS84";
	int pageSize = 20;
	paramsMap.put("output", output);
	paramsMap.put("query", query);
	paramsMap.put("scope", scope);
	paramsMap.put("page_num", String.valueOf(pageNum));
	paramsMap.put("ak", AK_VALUE);
	paramsMap.put("region", region);
	paramsMap.put("ret_coordtype", ret_coordtype);
	paramsMap.put("page_size", String.valueOf(pageSize));
	// 調(diào)用簽名工具生成SN
	BaiduSignature signature = new BaiduSignature(AK_VALUE, SK_VALUE,paramsMap,api_prefix);
	String sn = signature.getSnByMap();
	System.out.println("sn==>" + sn);
	HttpResponse<String> result = bdGeoSearchWithSnService.getSearch(query, region, output, scope,
				ret_coordtype, pageSize, pageNum ,AK_VALUE,sn);
	System.out.println("第一頁的數(shù)據(jù)如下:");
	System.out.println(result);
	System.out.println(result.getBodyResult());
	System.out.println("--------------------------------------------------------------");
}

再次運(yùn)行程序發(fā)現(xiàn)成功獲取了數(shù)據(jù):

上面這種方式雖然可以解決問題,但是每次都需要進(jìn)行調(diào)整參數(shù),使用起來比較麻煩,下面我們根據(jù)作者的推薦使用請求前置處理器來進(jìn)行參數(shù)的重新設(shè)置。

2、一種兼容Get請求參數(shù)動態(tài)調(diào)整的方法

關(guān)于使用Processor的模式來進(jìn)行請求參數(shù)重置的方法雖然是第一次說,但是之前我們講過基于Processor的AK自動設(shè)置,其核心原理是差不多的。這里我們的改造重點(diǎn)是BaiduHttpApiProcessor中的postBeforeHttpRequest方法。這個方法的核心作用就是發(fā)送Http請求之前會回調(diào)該方法,可對Http請求體的內(nèi)容進(jìn)行二次處理。因此我們首先直接來拼接請求參數(shù)的方法。在這里我們需要完全重寫請求URL,以完成全新的請求。根據(jù)queryParams來重新拼接請求字符串,我們從queryParams中取出請求集合,然后根據(jù)Map再設(shè)置Url的值,核心方法如下:

/**
* 
* @param snMapParams
* @param queryParam
* @return
*/
protected Map<String, Object> reorderingQueryParamMap(String snMapParams,Map<String, Object> queryParam) {
	Map<String, Object> paramsMap = null;
	if(StringUtils.isNotEmpty(snMapParams)) {
		paramsMap = new LinkedHashMap<String, Object>();
		//將ak和sn分別放到paramsMap中
		paramsMap.put("region", queryParam.get("region"));
        //其它參數(shù)
		//將ak和sn分別放到paramsMap中
		paramsMap.put("ak", queryParam.get("ak"));
		paramsMap.put("sn", queryParam.get("sn"));
	}else {
		paramsMap = queryParam;
	}
	return paramsMap;
}

在請求之前手動修改URL地址,關(guān)鍵方法如下:

/** * -實(shí)現(xiàn)-postBeforeHttpMetadata: 發(fā)送Http請求之前會回調(diào)該方法,可對Http請求體的內(nèi)容進(jìn)行二次處理
*
* @param uniHttpRequest              原來的請求體
* @param methodInvocation          被代理的方法
* @return                          新的請求體
*/
@Override
public UniHttpRequest postBeforeHttpRequest(UniHttpRequest uniHttpRequest,
			HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) {
		/**
         * -在查詢參數(shù)中添加提供的appId字段
         */
        // 獲取BaiduHttpApi注解
		BaiduHttpApi apiAnnotation = methodInvocation.getProxyApiAnnotation();
        // 獲取所有查詢參數(shù)
        Map<String, Object> queryParam = uniHttpRequest.getHttpUrl().getQueryParam();
        System.out.println("未處理之前的請求參數(shù):" + queryParam);
        Map<String, Object> paramsMap = this.reorderingQueryParamMap(snMapParams, queryParam);
       System.out.println("處理之后的請求參數(shù):" + paramsMap);
        // 第二種方式,直接修改請求參數(shù)
        String queryString =  this.reorderingQueryParam(snMapParams,queryParam);
        uniHttpRequest.getHttpUrl().setQueryParam(null);
        String url = uniHttpRequest.getHttpUrl().getUrl() + uniHttpRequest.getHttpUrl().getPath() + "?" + queryString;
        uniHttpRequest.getHttpUrl().setUrl(url);
        uniHttpRequest.getHttpUrl().setPath("");
    }
    return uniHttpRequest;
}

請注意,使用這種方式,一般需要將QueryParam參數(shù)這是為空,并且完全使用url參數(shù),不要使用path,可以直接設(shè)置為null即可。還有使用方式的時候需要注意的點(diǎn),即我們的接口定義時,不要多加?號,否則會變成雙問號,從而導(dǎo)致請求報(bào)錯,比如:

@GetHttpInterface(path = "/place/v2/search?")
public HttpResponse<String> getSearch(@QueryPar("query") String query, @QueryPar("region") String region,
			@QueryPar("output") String output, @QueryPar("scope") String scope,
			@QueryPar("ret_coordtype") String ret_coordtype, @QueryPar("page_size") String pageSize,
			@QueryPar("page_num") String pageNum, @QueryPar("ak") String ak, @QueryPar("sn") String sn);

加完之后,再查看UniHttp發(fā)出的請求地址就會加了一個?號,如下圖:

將請求地址復(fù)制出來,

url=https://api.map.baidu.com/place/v2/search??query=%E6%B9%98%E8%8F%9C&region=158&output=json&scope=2&ret_coordtype=WGS84&page_size=20&page_num=0&ak=yourak&sn=d1abf338a49bd82fca6992f5d2b9f686

由此也會極易導(dǎo)致出現(xiàn)SN校驗(yàn)失敗的問題,請大家注意。

3、使用注解來設(shè)置重排序規(guī)則

沒有什么方法是可以自己來定義請求的請求順序呢?答案是一定的,我們完全可以將參數(shù)的順序定義到注解中,同時增加一個SN校驗(yàn)的模式開關(guān),這樣程序?qū)崿F(xiàn)起來就更加靈活。在注解類中作如下定義:

package com.yelang.project.thridinterface.apiprocessor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import com.burukeyou.uniapi.http.annotation.HttpApi;
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@HttpApi(processor = BaiduHttpApiProcessor.class)
public @interface BaiduHttpApi {
	/**
     * -渠道方域名地址
     */
    @AliasFor(annotation = HttpApi.class)
    String url() default "${unihttp.channel.baidu.url}";
    /**
     * -snMapParams 模式下的請求參數(shù)順序,用于按順序加密,在統(tǒng)一處理器中獲取后處理
     */
    String snMapParams() default "";
    /**
     * - 是否使用sn簽名模式
     * @return
     */
    boolean snMode() default false;
}

重點(diǎn)就是這兩個參數(shù),通過這兩個參數(shù)來設(shè)置SN模式和具體的參數(shù),定義了注解字后,我們看在Processor中如何使用。簡單來說就是先看SN模式,然后再解析參數(shù)設(shè)置的順序,再讀取參數(shù)放置到集合中或者拼接到字符串中。核心方法如下:

/**
* 
* @param snMapParams
* @param queryParam
* @return
*/
protected Map<String, Object> reorderingQueryParamMap(String snMapParams,Map<String, Object> queryParam) {
	Map<String, Object> paramsMap = null;
	if(StringUtils.isNotEmpty(snMapParams)) {
		paramsMap = new LinkedHashMap<String, Object>();
		//從注解中讀取配置的參數(shù)
		String [] snMap = snMapParams.split(",");
		//按照參數(shù)重新組合參數(shù),生成按順序的請求字符串
		for(String paramKey : snMap) {
			paramsMap.put(paramKey, queryParam.get(paramKey));
		}
		//將ak和sn分別放到paramsMap中
		paramsMap.put("ak", queryParam.get("ak"));
		paramsMap.put("sn", queryParam.get("sn"));
	}else {
		paramsMap = queryParam;
	}
	return paramsMap;
}

這樣通過結(jié)合注解方式來實(shí)現(xiàn)了參數(shù)順序的定義,在創(chuàng)建接口時注意注解的設(shè)置,實(shí)例如下,通過設(shè)置參數(shù)就設(shè)置了運(yùn)行模式:

@BaiduHttpApi(snMode = true, snMapParams = "query,region,output,scope,ret_coordtype,page_size,page_num")
public interface BaiduGeoSearchSnProcessorService {
}

4、重寫請求的QueryParam重排序方法

這里結(jié)合前面的注記方式來對請求參數(shù)的重排序進(jìn)行處理,這種方式適應(yīng)性更加強(qiáng)。與get方式不同的是,使用設(shè)置queryParam的方式,程序的通用性和方法的兼容性會更加好。重新設(shè)置QueryParam的方法核心代碼如下:

/** * -實(shí)現(xiàn)-postBeforeHttpMetadata: 發(fā)送Http請求之前會回調(diào)該方法,可對Http請求體的內(nèi)容進(jìn)行二次處理
 *
 * @param uniHttpRequest              原來的請求體
 * @param methodInvocation          被代理的方法
 * @return                          新的請求體
 */
@Override
public UniHttpRequest postBeforeHttpRequest(UniHttpRequest uniHttpRequest,
			HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) {
    // 獲取BaiduHttpApi注解
	BaiduHttpApi apiAnnotation = methodInvocation.getProxyApiAnnotation();
    String snMapParams = apiAnnotation.snMapParams();
    boolean snMode = apiAnnotation.snMode();
    //如果開啟sn簽名,則將請求參數(shù)進(jìn)行充排序后進(jìn)行簽名,反之不用處理
    if(snMode) {
        System.out.println("開啟SN簽名處理");
        // 獲取所有查詢參數(shù)
        Map<String, Object> queryParam = uniHttpRequest.getHttpUrl().getQueryParam();
        Map<String, Object> paramsMap = this.reorderingQueryParamMap(snMapParams, queryParam);
        System.out.println("處理之后的請求參數(shù):" + paramsMap);
        //第一種請求參數(shù)重排序
        uniHttpRequest.getHttpUrl().setQueryParam(paramsMap);
    }
    return uniHttpRequest;
}

經(jīng)過這樣的改造和設(shè)計(jì),我們就完美的解決了在UniHttp中請求參數(shù)順序不可控的問題。需要注意的是,參數(shù)的加密順序和請求順序一致即可。成功請求界面如下:

5、三種方法的使用場景及對比

以上這三種方法都能滿足在UniHttp中實(shí)現(xiàn)請求參數(shù)動態(tài)調(diào)整的需求,我們將從代碼通用性和改造復(fù)雜度來進(jìn)行對比:

序號處理方式通用性改造復(fù)雜度缺點(diǎn)
1客戶端兼容需要根據(jù)請求參數(shù)定制
2兼容Get調(diào)參只支持Get方法
3重寫QueryParam代碼稍微復(fù)雜一點(diǎn),維護(hù)難度高

以上三種思路都能很好的滿足業(yè)務(wù)需要,但是具體采用哪種方式,還需要大家根據(jù)自己的場景進(jìn)行選擇。沒有最好的架構(gòu),只有更好的架構(gòu),架構(gòu)是慢慢演化而來,而不是一蹴而就,良好的架構(gòu)值得好好學(xué)習(xí)。

四、總結(jié)

以上就是本文的主要內(nèi)容,為了解決Java調(diào)用UniHttp接口時請求參數(shù)亂序?qū)е掳俣萐N簽名認(rèn)證失敗的問題。本文將詳細(xì)探討這三種解決策略,并通過具體的代碼示例和實(shí)際操作步驟,幫助開發(fā)者理解和應(yīng)用這些方法。本文的研究意義在于為Java開發(fā)者提供一種系統(tǒng)性的解決方案,幫助他們解決在接口調(diào)用過程中遇到的請求參數(shù)亂序問題。通過深入分析問題的根源,并提出有效的解決策略,本文旨在提高接口調(diào)用的成功率,減少開發(fā)過程中的錯誤和風(fēng)險(xiǎn)。同時,本文的研究成果也可以為其他使用類似接口調(diào)用框架和簽名認(rèn)證機(jī)制的開發(fā)者提供參考和借鑒。博文由淺入深,按照問題重現(xiàn)、查找原因、問題突破的方式對相關(guān)內(nèi)容進(jìn)行講解,希望大家都能掌握著三種不同的處理方式。

以上就是Java調(diào)用UniHttp接口請求失敗的三種解決策略的詳細(xì)內(nèi)容,更多關(guān)于Java調(diào)用接口請求的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java Applet查找素?cái)?shù)小程序代碼實(shí)例

    Java Applet查找素?cái)?shù)小程序代碼實(shí)例

    這篇文章主要介紹了Java Applet查找素?cái)?shù)小程序代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • SpringBoot實(shí)現(xiàn)過濾敏感詞的示例代碼

    SpringBoot實(shí)現(xiàn)過濾敏感詞的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用SpringBoot實(shí)現(xiàn)過濾敏感詞功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動手嘗試一下
    2022-08-08
  • Java利用Socket類實(shí)現(xiàn)TCP通信程序

    Java利用Socket類實(shí)現(xiàn)TCP通信程序

    TCP通信能實(shí)現(xiàn)兩臺計(jì)算機(jī)之間的數(shù)據(jù)交互,通信的兩端,要嚴(yán)格區(qū)分為客戶端與服務(wù)端,下面我們就來看看Java如何利用Socket類實(shí)現(xiàn)TCP通信程序吧
    2024-02-02
  • Java中消息隊(duì)列任務(wù)的平滑關(guān)閉詳解

    Java中消息隊(duì)列任務(wù)的平滑關(guān)閉詳解

    對于消息隊(duì)列的監(jiān)聽,我們一般使用Java寫一個獨(dú)立的程序,在Linux服務(wù)器上運(yùn)行。程序啟動后,通過消息隊(duì)列客戶端接收消息,放入一個線程池進(jìn)行異步處理,并發(fā)的快速處理。這篇文章主要給大家介紹了關(guān)于Java中消息隊(duì)列任務(wù)的平滑關(guān)閉的相關(guān)資料,需要的朋友可以參考下。
    2017-11-11
  • Java中ThreadLocal的用法和原理詳解

    Java中ThreadLocal的用法和原理詳解

    這篇文章主要為大家詳細(xì)介紹了Java中ThreadLocal的用法和原理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下
    2023-04-04
  • java實(shí)現(xiàn)音頻轉(zhuǎn)文本的實(shí)現(xiàn)步驟

    java實(shí)現(xiàn)音頻轉(zhuǎn)文本的實(shí)現(xiàn)步驟

    本文主要介紹了java實(shí)現(xiàn)音頻轉(zhuǎn)文本的實(shí)現(xiàn)步驟,可以通過使用一些現(xiàn)成的庫或者API來實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • springboot依賴沖突問題及解決過程

    springboot依賴沖突問題及解決過程

    新搭了一個springboot 2.3.7.RELASE的框架,在集成mysql,tkMapper,mybatis的過程中,啟動報(bào)錯,怎么解決這個問題呢,下面小編給大家?guī)砹藄pringboot依賴沖突問題及解決過程,一起看看吧
    2021-09-09
  • 排序算法圖解之Java快速排序的分步刨析

    排序算法圖解之Java快速排序的分步刨析

    快速排序是通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割為獨(dú)立的兩個部分,一部分的所有數(shù)據(jù)比另外一部分的所有數(shù)據(jù)要小,然后按照此方法對這兩部分分別進(jìn)行快速排序,整個過程可以遞歸進(jìn)行,以此達(dá)到整個數(shù)據(jù)變成有序序列。本文通過示例講解了快速排序的實(shí)現(xiàn),需要的可以參考一下
    2022-11-11
  • Java哈希表的概念及實(shí)現(xiàn)完整代碼

    Java哈希表的概念及實(shí)現(xiàn)完整代碼

    這篇文章主要介紹了Java哈希表的概念及實(shí)現(xiàn)的相關(guān)資料,哈希表是一種高效查找數(shù)據(jù)的結(jié)構(gòu),通過哈希函數(shù)將關(guān)鍵字映射到數(shù)組的索引位置,當(dāng)發(fā)生沖突時,可以通過閉散列或開散列(鏈地址法)來解決,需要的朋友可以參考下
    2024-11-11
  • Java中HttpServletResponse響應(yīng)中文出現(xiàn)亂碼問題

    Java中HttpServletResponse響應(yīng)中文出現(xiàn)亂碼問題

    這篇文章主要介紹了Java中HttpServletResponse響應(yīng)中文出現(xiàn)亂碼問題的相關(guān)資料,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06

最新評論