API請求編碼問題
用戶端請求API Gateway時,需要對參數值進行utf-8的urlEncode,這樣能避免特殊參數或者中文出現亂碼。 注意:query、header、body位置編碼需要在簽名計算後,簽名時用原始值。
以下代碼可參考:https://github.com/aliyun/api-gateway-demo-sign-java。
1、header值進行編碼
header的編碼和value編碼不太一樣,header的值需要用ISO-8859-1編碼。
HttpPost post = new HttpPost(initUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
//header的傳值,value要編碼後傳,具體實現請看下面
post.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
}/**
* UTF-8編碼轉換為ISO-8859-1 *
* @param str
* @return
*/
public static String utf8ToIso88591(String str) {
if (str == null) {
return str;
}
try {
return new String(str.getBytes("UTF-8"), "ISO-8859-1"); }
catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage(), e);
}
}2、query值進行編碼
如果query參數值含中文,特殊符號,如“+”號等,都需要對query的值進行utf-8編碼。可參考下面URL的構建方式。
private static String initUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(host);
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, String> query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), "UTF-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}3、body參數進行編碼
1)form形式body的編碼:
UrlEncodedFormEntity formEntity = buildFormEntity(bodys);
if (formEntity != null) {
post.setEntity(formEntity);
} /**
* 構建FormEntity
*
* @param formParam
* @return
* @throws UnsupportedEncodingException
*/
private static UrlEncodedFormEntity buildFormEntity(Map<String, String> formParam)
throws UnsupportedEncodingException {
if (formParam != null) {
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
for (String key : formParam.keySet()) {
nameValuePairList.add(new BasicNameValuePair(key, formParam.get(key)));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "UTF-8");
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
return formEntity;
}
return null;
}2)非form形式的body
String 形式的body:
if (StringUtils.isNotBlank(body)) {
post.setEntity(new StringEntity(body, "UTF-8"));
}byte[]形式的body:
if (bodys != null) {
post.setEntity(new ByteArrayEntity(bodys));
}4、path參數編碼
當API的path中有參數,而且參數含特殊字元。需要先將參數urlencode後加入path中。不然簽名會有問題。此時後端收到的也是urlEncode後的值,如果要擷取原值,需要自己在後端進行urlDecode。
@Test
public void testPath() throws Exception{
//請求path
String host=“你的網域名稱”;
//請求path,先將參數處理後再放入path中。
String type=“中文 123”;
String pathParam= URLEncoder.encode(type,"UTF-8");
String path = "/"+pathParam;
Map<String, String> headers = new HashMap<String, String>();
//(必填)根據期望的Response內容類型設定
headers.put(HttpHeader.HTTP_HEADER_ACCEPT, "application/json");
CUSTOM_HEADERS_TO_SIGN_PREFIX.clear();
Request request = new Request(Method.GET, host, path, AppKey, AppSecret, Constants.DEFAULT_TIMEOUT);
request.setHeaders(headers);
request.setSignHeaderPrefixList(CUSTOM_HEADERS_TO_SIGN_PREFIX);
//調用服務端
Response response = Client.execute(request);
System.out.println(JSON.toJSONString(response));
}5、參數中包含emoji表情
當參數中包含emoji表情,需要先對參數值進行urlencode後再簽名,不然簽名不過,因為API Gateway的系統可能會識別不了該符號,會導致簽名失敗,所以需要先進行urlencode處理後再簽名。
此時後端收到的為urlencode後的字串,如果要擷取原文,後端需要自己做urldecode。