com.feilong.core.net.ParamUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
/*
* Copyright (C) 2008 feilong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.feilong.core.net;
import static com.feilong.core.URIComponents.AMPERSAND;
import static com.feilong.core.URIComponents.QUESTIONMARK;
import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.bean.ConvertUtil.toArray;
import static com.feilong.core.bean.ConvertUtil.toMap;
import static com.feilong.core.lang.ArrayUtil.EMPTY_STRING_ARRAY;
import static com.feilong.core.lang.ObjectUtil.defaultEmptyMapIfNull;
import static com.feilong.core.lang.StringUtil.EMPTY;
import static com.feilong.core.net.URIUtil.decode;
import static com.feilong.core.net.URIUtil.encode;
import static com.feilong.core.util.CollectionsUtil.newArrayList;
import static com.feilong.core.util.MapUtil.newLinkedHashMap;
import static com.feilong.core.util.SortUtil.sortMapByKeyAsc;
import static com.feilong.lib.lang3.StringUtils.defaultString;
import static java.util.Collections.emptyMap;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.feilong.core.CharsetType;
import com.feilong.core.Validate;
import com.feilong.core.lang.StringUtil;
import com.feilong.core.util.MapUtil;
import com.feilong.core.util.SortUtil;
import com.feilong.lib.lang3.ArrayUtils;
import com.feilong.lib.lang3.StringUtils;
/**
* 处理参数相关.
*
* @author feilong
* @see "org.springframework.web.util.UriComponentsBuilder"
* @see "org.apache.http.client.utils.URIBuilder"
* @since 1.0.0
*/
public final class ParamUtil{
/** The Constant log. */
private static final Logger LOGGER = LoggerFactory.getLogger(ParamUtil.class);
/** Don't let anyone instantiate this class. */
private ParamUtil(){
//AssertionError不是必须的. 但它可以避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化.
//see 《Effective Java》 2nd
throw new AssertionError("No " + getClass().getName() + " instances for you!");
}
// -------------------------------addParameter-------------------------------
/**
* 给指定的uriString
添加指定的参数 paramName
和值 paramValue
.
*
* 说明:
*
*
* - 如果原来的
uriString
没有指定的参数paramName
,那么追加新的参数paramName
和值paramValue
.
* - 如果原来的
uriString
有指定的参数paramName
,那么会被新的值替换paramValue
.
* - 如果原来的
uriString
有指定的参数paramName
,并且是多值类型(参数数组),那么多值参数中第一个会被新的值替换paramValue
,其他的被丢弃.
* - 如果原来的
uriString
有参数,不管是拼接还是替换都会保持参数的原始顺序.
* - 如果
uriString
带有? 和参数,会先被截取,最后再拼接.
* - 如果
uriString
不带?,则自动增加?
*
*
*
* 示例:
*
*
*
* String uri = "http://www.feilong.com:8888/esprit-frontend/search.htm?{@code keyword=%E6%81%A4&page=}";
* String pageParamName = "label";
* String prePageNo = "2-5-8-12";
* LOGGER.info(ParamUtil.addParameter(uri, pageParamName, prePageNo, UTF8));
*
*
* 返回:
*
*
* {@code http://www.feilong.com:8888/esprit-frontend/search.htm?keyword=%E6%81%A4&page=&label=2-5-8-12}
*
*
*
*
* @param uriString
* 如果带有? 和参数,会先被截取,最后再拼接,
* 如果不带?,则自动增加?
* @param paramName
* 添加的参数名称
* @param paramValue
* 添加的参数值
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 uriString
是null或者empty,返回 {@link StringUtils#EMPTY}
* 如果 paramName
是null,以 {@link StringUtils#EMPTY}替代
* 如果 paramValue
是null,以 {@link StringUtils#EMPTY}替代
* @see #addParameterSingleValueMap(String, Map, String)
* @since 1.9.0 change paramValue type from Object to String
*/
public static String addParameter(String uriString,String paramName,String paramValue,String charsetType){
return addParameterSingleValueMap(uriString, toMap(paramName, paramValue), charsetType);
}
/**
* 给指定的uriString
添加指定的参数 singleValueMap
.
*
* 说明:
*
*
* - 如果原来的
uriString
没有指定的参数paramName
,那么循环arrayValueMap
,追加新的参数paramName
和值
* paramValue
.
* - 如果原来的
uriString
有指定的参数paramName
,那么会被新的值替换paramValue
.
* - 如果原来的
uriString
有指定的参数paramName
,并且是多值类型(参数数组),那么多值参数中第一个会被新的值替换paramValue
,其他的被丢弃.
* - 如果原来的
uriString
有参数,不管是拼接还是替换都会保持参数的原始顺序.
* - 如果
uriString
带有? 和参数,会先被截取,最后再拼接.
* - 如果
uriString
不带?,则自动增加?
*
*
*
* 示例1:
*
*
*
* String beforeUrl = "www.baidu.com";
* Map{@code } keyAndArrayMap = new LinkedHashMap{@code <>}();
*
* keyAndArrayMap.put("province", "江苏省");
* keyAndArrayMap.put("city", "南通市");
*
* LOGGER.info(ParamUtil.addParameterSingleValueMap(beforeUrl, keyAndArrayMap, UTF8));
*
*
* 返回:
*
*
* {@code www.baidu.com?province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
*
*
*
* 示例2:
*
*
*
* String beforeUrl = "www.baidu.com?a=b";
* Map{@code } keyAndArrayMap = new LinkedHashMap{@code <>}();
*
* keyAndArrayMap.put("province", "江苏省");
* keyAndArrayMap.put("city", "南通市");
*
* LOGGER.info(ParamUtil.addParameterSingleValueMap(beforeUrl, keyAndArrayMap, UTF8));
*
*
* 返回:
*
*
* {@code www.baidu.com?a=b&province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
*
*
*
*
* @param uriString
* 如果带有? 和参数,会先被截取,最后再拼接,
* 如果不带?,则自动增加?
* @param singleValueMap
* singleValueMap param name 和value 的键值对
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 uriString
是null或者empty,返回 {@link StringUtils#EMPTY}
* 如果 singleValueMap
是null,当作empty map处理
* @see #addParameterArrayValueMap(String, Map, String)
*/
public static String addParameterSingleValueMap(String uriString,Map singleValueMap,String charsetType){
return addParameterArrayValueMap(uriString, MapUtil.toArrayValueMap(singleValueMap), charsetType);
}
/**
* 给指定的 uriString
添加参数 arrayValueMap
,如果uri包含指定的参数名字,那么会被新的值替换.
*
* 说明:
*
*
* - 如果原来的
uriString
没有指定的参数paramName
,那么循环arrayValueMap
,追加新的参数paramName
和值
* paramValue
.
* - 如果原来的
uriString
有指定的参数paramName
,那么会被新的值替换paramValue
.
* - 如果原来的
uriString
有指定的参数paramName
,并且是多值类型(参数数组),那么多值参数中第一个会被新的值替换paramValue
,其他的被丢弃.
* - 如果原来的
uriString
有参数,不管是拼接还是替换都会保持参数的原始顺序.
* - 如果
uriString
带有? 和参数,会先被截取,最后再拼接.
* - 如果
uriString
不带?,则自动增加?
*
*
*
* 示例1:
*
*
*
* String beforeUrl = "www.baidu.com";
* Map{@code } keyAndArrayMap = new LinkedHashMap{@code <>}();
*
* keyAndArrayMap.put("receiver", new String[] { "鑫哥", "feilong" });
* keyAndArrayMap.put("province", new String[] { "江苏省" });
* keyAndArrayMap.put("city", new String[] { "南通市" });
* LOGGER.info(ParamUtil.addParameterArrayValueMap(beforeUrl, keyAndArrayMap, UTF8));
*
*
* 返回:
*
*
* {@code www.baidu.com?receiver=%E9%91%AB%E5%93%A5&receiver=feilong&province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
*
*
*
*
*
* 示例2:
*
*
*
* String beforeUrl = "www.baidu.com?a=b";
* Map{@code } keyAndArrayMap = new LinkedHashMap{@code <>}();
* keyAndArrayMap.put("province", new String[] { "江苏省" });
* keyAndArrayMap.put("city", new String[] { "南通市" });
* LOGGER.info(ParamUtil.addParameterArrayValueMap(beforeUrl, keyAndArrayMap, UTF8));
*
*
* 返回:
*
*
* {@code www.baidu.com?a=b&province=%E6%B1%9F%E8%8B%8F%E7%9C%81&city=%E5%8D%97%E9%80%9A%E5%B8%82}
*
*
*
*
* @param uriString
* 如果带有? 和参数,会先被截取,最后再拼接,
* 如果不带?,则自动增加?
* @param arrayValueMap
* the name and array value map
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 uriString
是null或者empty,返回 {@link StringUtils#EMPTY}
* 如果 arrayValueMap
是null,当作empty map处理
* 如果 charsetType
是null或者empty,那么参数部分原样拼接处理,自行处理兼容性问题
* @see #addParameterArrayValueMap(String, String, Map, String)
* @since 1.4.0
*/
public static String addParameterArrayValueMap(String uriString,Map arrayValueMap,String charsetType){
return addParameterArrayValueMap(uriString, URIUtil.getQueryString(uriString), arrayValueMap, charsetType);
}
//---------------------------------------------------------------
/**
* 将{@code a=1&b=2}这样格式的 queryString
数据转换成map.
*
* 说明:
*
*
* - 如果参数里面有相同名字的参数,那么转换的时候取第一个值
* - 内部使用 {@link LinkedHashMap},map顺序依照
queryString
分隔的顺序
*
*
*
* 示例:
*
*
*
* String queryString = "{@code sec_id=MD5&format=xml&sign=cc945983476d615ca66cee41a883f6c1&v=2.0&req_data=%3Cauth_and_execute_req%3E%3Crequest_token%3E201511191eb5762bd0150ab33ed73976f7639893%3C%2Frequest_token%3E%3C%2Fauth_and_execute_req%3E&service=alipay.wap.auth.authAndExecute&partner=2088011438559510}";
* LOGGER.info(JsonUtil.format(ParamUtil.toSingleValueMap(queryString, UTF8)));
*
*
* 返回:
*
*
* {
* "sec_id": "MD5",
* "format": "xml",
* "sign": "cc945983476d615ca66cee41a883f6c1",
* "v": "2.0",
* "req_data":"%3Cauth_and_execute_req%3E%3Crequest_token%3E201511191eb5762bd0150ab33ed73976f7639893%3C%2Frequest_token%3E%3C%2Fauth_and_execute_req%3E",
* "service": "alipay.wap.auth.authAndExecute",
* "partner": "2088011438559510"
* }
*
*
*
*
* @param queryString
* the query string
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 queryString
是null或者empty,返回 {@link Collections#emptyMap()}
* @see #toSafeArrayValueMap(String, String)
* @since 1.4.0
*/
public static Map toSingleValueMap(String queryString,String charsetType){
return MapUtil.toSingleValueMap(toSafeArrayValueMap(queryString, charsetType));
}
/**
* 将{@code a=1&b=2}这样格式的数据转换成map (如果charsetType
不是null或者empty 返回安全的 key和value).
*
* 说明:
*
*
* - 内部使用 {@link LinkedHashMap},map顺序依照
queryString
逗号分隔的顺序
* - 解析方式:参数和参数之间是以 {@code &} 分隔, 参数的key和value 是以 = 号分隔
*
*
*
* 示例1:
*
*
*
* LOGGER.info(JsonUtil.format(ParamUtil.toSafeArrayValueMap("{@code a=1&b=2&a=5}", UTF8)));
*
*
* 返回:
*
*
{"a": [
"1",
"5"
],
"b": ["2"]
}
*
*
*
*
*
* LOGGER.info(JsonUtil.format(ParamUtil.toSafeArrayValueMap("{@code a=&b=2&a}", UTF8)));
*
*
* 返回:
*
*
{"a": [
"",
""
],
"b": ["2"]
}
*
*
*
*
* @param queryString
* {@code a=1&b=2}类型的数据,支持{@code a=1&a=1}的形式, 返回map的值是数组
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 queryString
是null或者empty,返回 {@link Collections#emptyMap()}
* @see com.feilong.lib.lang3.ArrayUtils#add(Object[], Object)
* @see com.feilong.core.lang.StringUtil#split(String, String)
* @since 1.4.0
*/
static Map toSafeArrayValueMap(String queryString,String charsetType){
if (isNullOrEmpty(queryString)){
return emptyMap();
}
String[] nameAndValueArray = StringUtil.split(queryString, AMPERSAND);
int length = nameAndValueArray.length;
Map safeArrayValueMap = newLinkedHashMap(length);//使用 LinkedHashMap 保证元素的顺序
for (int i = 0; i < length; ++i){
String[] tempArray = nameAndValueArray[i].split("=", 2);
String key = decodeAndEncode(tempArray[0], charsetType);
String value = tempArray.length == 2 ? tempArray[1] : EMPTY;//有可能参数中,只有名字没有值或者值是空,处理的时候不能遗失掉
value = decodeAndEncode(value, charsetType);
safeArrayValueMap.put(key, ArrayUtils.add(safeArrayValueMap.get(key), value));
}
return safeArrayValueMap;
}
//---------------------------------------------------------------------------------------------
/**
* 将 singleValueMap
转成自然排序
, 然后将key和value直接拼接成字符串(不使用 {@code =} 和{@code &} 分隔).
*
*
* 已知,适用于 网易易盾验证码
*
*
* 示例:
*
*
*
* Map{@code } map = newHashMap();
* map.put("service", "create_salesorder");
* map.put("_input_charset", "gbk");
* map.put("totalActual", "210.00");
* map.put("address", "江苏南通市通州区888组888号");
* LOGGER.debug(ParamUtil.toNaturalOrderingKeyJoinValue(map));
*
*
* 返回:
*
*
* {@code _input_charsetgbkaddress江苏南通市通州区888组888号servicecreate_salesordertotalActual210.00}
*
*
*
*
* 规则:
*
*
*
*
* - 首先将
singleValueMap
使用 {@link SortUtil#sortMapByKeyAsc(Map)} 进行排序,
* - 然后将map的key和value 直接连接
*
*
*
*
* 说明:
*
*
*
* - 常用于和第三方对接数据(比如网易易盾验证码,生成 待签名的字符串)
* - 该方法不会执行encode操作,使用原生值进行拼接
*
* -
*
对于 null key或者null value的处理:
*
*
*
* 如果 singleValueMap
中,
* 如果有 key
是null
,那么会使用 {@link StringUtils#EMPTY} 进行拼接;
* 如果有 value
是 null
,那么会使用 {@link StringUtils#EMPTY} 进行拼接
*
*
* 示例:
*
*
* Map{@code } map = newHashMap();
* map.put("totalActual", null);
* map.put(null, "create_salesorder");
* map.put("province", "江苏省");
*
* LOGGER.debug(ParamUtil.toNaturalOrderingKeyJoinValue(map));
*
*
* 返回:
*
*
* {@code create_salesorderprovince江苏省totalActual}
*
*
*
*
*
*
*
*
* @param singleValueMap
* 用于拼接签名的参数
* @return 如果 singleValueMap
是null或者empty,返回 {@link StringUtils#EMPTY}
* 否则将singleValueMap
排序之后,循环直接拼接key和value
* @since 1.10.6
*/
public static String toNaturalOrderingKeyJoinValue(Map singleValueMap){
if (isNullOrEmpty(singleValueMap)){
return EMPTY;
}
//---------------------------------------------------------------
Map sortMapByKeyAsc = sortMapByKeyAsc(singleValueMap);
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : sortMapByKeyAsc.entrySet()){//有顺序的参数
String key = entry.getKey();
String value = entry.getValue();
//注意:如果 value是null,StringBuilder将拼接 "null" 字符串, 详见 java.lang.AbstractStringBuilder#append(String)
sb.append(defaultString(key));
sb.append(defaultString(value));
}
return sb.toString();
}
/**
* 将 singleValueMap
转成自然排序
, 然后仅将value直接拼接成字符串(不使用 {@code =} 和{@code &} 分隔).
*
*
* 已知,适用于 银联大华捷通
*
*
* 示例:
*
*
*
* Map{@code } map = newHashMap();
* map.put("service", "create_salesorder");
* map.put("_input_charset", "gbk");
* map.put("totalActual", "210.00");
* map.put("address", "江苏南通市通州区888组888号");
* LOGGER.debug(ParamUtil.toNaturalOrderingJoinValue(map));
*
*
* 返回:
*
*
* {@code gbk江苏南通市通州区888组888号create_salesorder210.00}
*
*
*
*
* 规则:
*
*
*
*
* - 首先将
singleValueMap
使用 {@link SortUtil#sortMapByKeyAsc(Map)} 进行排序,
* - 然后将map的value 直接连接
*
*
*
*
* 说明:
*
*
*
* - 常用于和第三方对接数据(比如银联大华捷通,生成 待签名的字符串)
* - 该方法不会执行encode操作,使用原生值进行拼接
*
* -
*
对于 null key或者null value的处理:
*
*
*
* 如果 singleValueMap
中,
* 如果有 value
是 null
,那么会使用 {@link StringUtils#EMPTY} 进行拼接
*
*
* 示例:
*
*
* Map{@code } map = newHashMap();
* map.put("totalActual", null);
* map.put("province", "江苏省");
*
* LOGGER.debug(ParamUtil.toNaturalOrderingKeyJoinValue(map));
*
*
* 返回:
*
*
* {@code 江苏省}
*
*
*
*
*
*
*
*
* @param singleValueMap
* 用于拼接签名的参数
* @return 如果 singleValueMap
是null或者empty,返回 {@link StringUtils#EMPTY}
* 否则将singleValueMap
排序之后,循环直接拼接value
* @since 2.0.1
*/
public static String toNaturalOrderingJoinValue(Map singleValueMap){
if (isNullOrEmpty(singleValueMap)){
return EMPTY;
}
//---------------------------------------------------------------
Map sortMapByKeyAsc = sortMapByKeyAsc(singleValueMap);
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : sortMapByKeyAsc.entrySet()){//有顺序的参数
String value = entry.getValue();
//注意:如果 value是null,StringBuilder将拼接 "null" 字符串, 详见 java.lang.AbstractStringBuilder#append(String)
sb.append(defaultString(value));
}
return sb.toString();
}
/**
* 将 singleValueMap
转成自然排序
的 queryString
字符串.
*
* 示例:
*
*
*
* Map{@code } map = newHashMap();
* map.put("service", "create_salesorder");
* map.put("_input_charset", "gbk");
* map.put("totalActual", "210.00");
* map.put("address", "江苏南通市通州区888组888号");
* LOGGER.debug(ParamUtil.toNaturalOrderingQueryString(map));
*
*
* 返回:
*
*
* {@code _input_charset=gbk&address=江苏南通市通州区888组888号&service=create_salesorder&totalActual=210.00}
*
*
*
*
* 规则:
*
*
*
*
* - 首先将
singleValueMap
使用 {@link SortUtil#sortMapByKeyAsc(Map)} 进行排序,
* - 然后将map的key和value 使用= 符号 连接,不同的entry之间再使用{@code &} 符号进行连接,最终格式类似于 url 的queryString
*
*
*
*
* 说明:
*
*
*
* - 常用于和第三方对接数据(比如支付宝,生成 待签名的字符串)
* - 该方法不会执行encode操作,使用原生值进行拼接
*
* -
*
对于 null key或者null value的处理:
*
*
*
* 如果 singleValueMap
中,
* 如果有 key
是null
,那么会使用 {@link StringUtils#EMPTY} 进行拼接;
* 如果有 value
是 null
,那么会使用 {@link StringUtils#EMPTY} 进行拼接
*
*
* 示例:
*
*
* Map{@code } map = newHashMap();
* map.put("totalActual", null);
* map.put(null, "create_salesorder");
* map.put("province", "江苏省");
*
* LOGGER.debug(ParamUtil.toNaturalOrderingQueryString(map));
*
*
* 返回:
*
*
* {@code =create_salesorder&province=江苏省&totalActual=}
*
*
*
*
*
*
*
*
* @param singleValueMap
* 用于拼接签名的参数
* @return 如果 singleValueMap
是null或者empty,返回 {@link StringUtils#EMPTY}
* 否则将singleValueMap
排序之后,调用 {@link #toQueryStringUseSingleValueMap(Map)}
* @since 1.4.0
*/
public static String toNaturalOrderingQueryString(Map singleValueMap){
return isNullOrEmpty(singleValueMap) ? EMPTY : toQueryStringUseSingleValueMap(sortMapByKeyAsc(singleValueMap));
}
/**
* 将singleValueMap
转成 queryString.
*
* 说明:
*
*
* -
* 只是简单的将map的key value 按照
singleValueMap
的顺序 连接起来,最终格式类似于 url 的queryString,
* 比如,参数名字param Name=name
,param Value=zhangfei
,那么返回值是 name=zhangfei
*
* - 如果
singleValueMap
有key 是null,将使用 {@link StringUtils#EMPTY} 进行拼接
* - 如果
singleValueMap
有value 是null,将使用 {@link StringUtils#EMPTY} 进行拼接
*
*
*
* 示例1:
*
*
*
* Map{@code } singleValueMap = new LinkedHashMap{@code <>}();
*
* singleValueMap.put("province", "江苏省");
* singleValueMap.put("city", "南通市");
*
* LOGGER.info(ParamUtil.joinSingleValueMap(singleValueMap));
*
*
* 返回:
*
*
* {@code province=江苏省&city=南通市}
*
*
*
*
* 示例2:
*
*
*
* Map{@code } map = new LinkedHashMap{@code <>}();
* map.put(null, null);
* map.put("a", "");
* map.put("b", null);
* map.put("c", "jim");
*
* LOGGER.info(ParamUtil.toQueryStringUseSingleValueMap(map));
*
*
* 返回:
*
*
* {@code =&a=&b=&c=jim}
*
*
*
*
* @param singleValueMap
* the params map
* @return 如果singleValueMap
是 null或者empty,返回 {@link StringUtils#EMPTY}
* 否则将 singleValueMap
转成 {@link MapUtil#toArrayValueMap(Map)},再调用 {@link #toQueryStringUseArrayValueMap(Map)}
* @see MapUtil#toArrayValueMap(Map)
* @see #toQueryStringUseArrayValueMap(Map)
* @see build-convert-map-to-query-string
* @since 1.5.5
*/
public static String toQueryStringUseSingleValueMap(Map singleValueMap){
return toQueryStringUseArrayValueMap(MapUtil.toArrayValueMap(singleValueMap));
}
/**
* 只是简单的将map的key value 连接起来,最终格式类似于 url 的queryString.
*
* 注意:
*
*
*
* - 该方法不会执行encode操作,使用原生值进行拼接
* - 按照传入的map key顺序进行排序,不会自行自动排序转换;如有有业务需求,先行排序完传入进来
* - 如果
arrayValueMap
有key 是null,将使用 {@link StringUtils#EMPTY} 进行拼接
* - 如果
arrayValueMap
有value的元素是null,将使用 {@link StringUtils#EMPTY} 进行拼接
*
*
*
* 示例1:
*
*
*
* Map{@code } keyAndArrayMap = new LinkedHashMap{@code <>}();
*
* keyAndArrayMap.put("province", new String[] { "江苏省", "浙江省" });
* keyAndArrayMap.put("city", new String[] { "南通市" });
* LOGGER.info(ParamUtil.joinArrayValueMap(keyAndArrayMap));
*
*
* 返回:
*
*
* {@code province=江苏省&province=浙江省&city=南通市}
*
*
*
*
* 示例2:
*
*
*
* Map{@code } keyAndArrayMap = new LinkedHashMap{@code <>}();
*
* keyAndArrayMap.put("province", new String[] { "江苏省", null });
* keyAndArrayMap.put("city", new String[] { "南通市" });
* LOGGER.info(ParamUtil.joinArrayValueMap(keyAndArrayMap));
*
*
* 返回:
*
*
* {@code province=江苏省&province=&city=南通市}
*
*
*
*
* @param arrayValueMap
* the array value map
* @return 如果 arrayValueMap
是 null或者Empty,返回 {@link StringUtils#EMPTY}
* 否则循环 arrayValueMap
拼接成QueryString
* @see build-convert-map-to-query-string
* @since 1.5.5
*/
public static String toQueryStringUseArrayValueMap(Map arrayValueMap){
if (isNullOrEmpty(arrayValueMap)){
return EMPTY;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : arrayValueMap.entrySet()){
sb.append(joinParamNameAndValues(entry.getKey(), entry.getValue()));
sb.append(AMPERSAND); //放心大胆的拼接 &, 不判断是否是最后一个,最后会截取
}
return StringUtil.substringWithoutLast(sb, AMPERSAND);
}
/**
* 将参数名称paramName
和多值 paramValues
连接起来.
*
* 说明:
*
*
* - 比如,参数名字 {@code paramName=name},{@code paramValues 为 zhangfei,guanyu},那么返回值是{@code name=zhangfei&name=guanyu}
* - paramName和每个值都会调用 {@link StringUtils#defaultString(String)}转换后才进行拼接
*
*
*
* @param paramName
* 参数名字
* @param paramValues
* 参数多值
* @return 如果paramValues
是null,那么直接返回 paramName=
* @see java.lang.AbstractStringBuilder#append(String)
* @see com.feilong.lib.lang3.StringUtils#defaultString(String)
* @see "org.springframework.web.servlet.view.RedirectView#appendQueryProperties(StringBuilder,Map, String)"
* @since 1.4.0
*/
private static String joinParamNameAndValues(String paramName,String[] paramValues){
//see https://github.com/venusdrogon/feilong-core/issues/372
if (null == paramValues){
return defaultString(paramName) + "=";
}
StringBuilder sb = new StringBuilder();
for (String paramValue : paramValues){
//注意:如果 value 是null ,StringBuilder将拼接 "null" 字符串, 详见 java.lang.AbstractStringBuilder#append(String)
sb.append(defaultString(paramName)).append("=").append(defaultString(paramValue));
sb.append(AMPERSAND);//放心大胆的拼接 &, 不判断是否是最后一个,最后会截取
}
return StringUtil.substringWithoutLast(sb, AMPERSAND);
}
/**
* 在singleValueMap
中取到指定includeKeys
key的value,连接起来(不使用任何连接符).
*
* 说明:
*
*
* - 拼接的顺序按照
includeKeys
的顺序,目前适用于个别银行(比如汇付天下)需要将值拼接起来加密
* - 如果
singleValueMap
中的value是null,那么会以{@link StringUtils#EMPTY}替代,进行拼接
*
*
*
* 示例:
*
*
*
* 场景: 拼接map中 key是 "service"以及 "paymentType"的 value
*
*
*
* Map{@code } map = newHashMap();
* map.put("service", "create_salesorder");
* map.put("paymentType", "unionpay_mobile");
*
* ParamUtil.joinValuesOrderByIncludeKeys(map, "service", "paymentType") = create_salesorderunionpay_mobile
*
*
*
*
* @param
* the key type
* @param singleValueMap
* the map
* @param includeKeys
* 包含的key
* @return 如果 singleValueMap
是null,抛出 {@link NullPointerException}
* 如果 includeKeys
是null或者empty,返回 {@link StringUtils#EMPTY}
* 否则循环 includeKeys
,依次从 singleValueMap
中取到值,连接起来;
* @see com.feilong.lib.lang3.StringUtils#defaultString(String)
* @since 1.5.5
*/
@SafeVarargs
public static String joinValuesOrderByIncludeKeys(Map singleValueMap,K...includeKeys){
Validate.notNull(singleValueMap, "singleValueMap can't be null!");
if (isNullOrEmpty(includeKeys)){
return EMPTY;
}
StringBuilder sb = new StringBuilder();
for (K key : includeKeys){//有顺序的参数
//注意:如果 value是null,StringBuilder将拼接 "null" 字符串, 详见 java.lang.AbstractStringBuilder#append(String)
sb.append(defaultString(singleValueMap.get(key)));
}
return sb.toString();
}
//---------------------------------------------------------------------------------------------
/**
* 添加 parameter array value map.
*
*
* 如果 queryString
参数不为空,那么会解析成map,此后再拼接 arrayValueMap
;
* 内部使用 {@link LinkedHashMap},保持map元素顺序
*
*
* @param uriString
* the uri string
* @param queryString
* the query
* @param arrayValueMap
* the name and array value map
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return the string
* @since 1.4.0
*/
static String addParameterArrayValueMap(String uriString,String queryString,Map arrayValueMap,String charsetType){
Map safeArrayValueMap = defaultEmptyMapIfNull(arrayValueMap);
Map arrayParamValuesMap = newLinkedHashMap(safeArrayValueMap.size());
//先提取queryString map
if (isNotNullOrEmpty(queryString)){
arrayParamValuesMap.putAll(toSafeArrayValueMap(queryString, null));
}
arrayParamValuesMap.putAll(safeArrayValueMap);
return combineUrl(URIUtil.getFullPathWithoutQueryString(uriString), arrayParamValuesMap, charsetType);
}
/**
* To safe array value map.
*
*
* 内部使用 {@link LinkedHashMap},保持map元素顺序
*
*
* @param arrayValueMap
* the array value map
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 arrayValueMap
是null或者empty,返回 {@link Collections#emptyMap()}
*/
private static Map toSafeArrayValueMap(Map arrayValueMap,String charsetType){
if (isNullOrEmpty(arrayValueMap)){
return emptyMap();
}
Map safeArrayValueMap = newLinkedHashMap(arrayValueMap.size()); //使用 LinkedHashMap,保持map元素顺序
for (Map.Entry entry : arrayValueMap.entrySet()){
String key = entry.getKey();
String[] paramValues = entry.getValue();
if (isNullOrEmpty(paramValues)){
LOGGER.warn("the param key:[{}] value is null", key);
paramValues = EMPTY_STRING_ARRAY;//赋予 empty数组,为了下面的转换
}
safeArrayValueMap.put(decodeAndEncode(key, charsetType), toSafeValueArray(paramValues, charsetType));
}
return safeArrayValueMap;
}
/**
* To safe value array.
*
* @param paramValues
* the param values
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* @return the string[]
* @since 1.6.2
*/
private static String[] toSafeValueArray(String[] paramValues,String charsetType){
if (isNullOrEmpty(charsetType)){
return paramValues;
}
List paramValueList = newArrayList();
for (String value : paramValues){
paramValueList.add(decodeAndEncode(value, charsetType));
}
return toArray(paramValueList, String.class);
}
/**
* 浏览器传递queryString()参数差别;
*
* 说明:
*
*
* - 主要是为了处理浏览器兼容问题,参见
*
* java-request-getquerystring-value-different-between-chrome-and-ie-browser,chrome会将query进行 encoded再发送请求;而ie原封不动的发送.
* - 由于暂时不能辨别是否encoded过,所以先强制decode再encode;
* - 此处不能先转decode(query,charsetType),参数就是想传 =是转义符
*
*
*
* @param value
* the value
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 value
是 null或者empty,返回 {@link StringUtils#EMPTY}
* 如果charsetType
是 null或者empty,直接返回 value
* 否则先 {@link URIUtil#decode(String, String)} 再 {@link URIUtil#encode(String, String)}值
* @see
* java-request-getquerystring-value-different-between-chrome-and-ie-browser
* @since 1.4.0
*/
private static String decodeAndEncode(String value,String charsetType){
if (isNullOrEmpty(value)){
return EMPTY;
}
return isNullOrEmpty(charsetType) ? value : encode(decode(value, charsetType), charsetType);
}
/**
* 拼接url.
*
* @param beforePathWithoutQueryString
* the before path without query string
* @param arrayValueMap
* the array value map
* @param charsetType
* 字符编码,建议使用 {@link CharsetType} 定义好的常量
* 如果是null或者 empty,那么参数部分原样返回,自行处理兼容性问题
* 否则会先解码,再加码,因为ie浏览器和chrome浏览器 url中访问路径 ,带有中文情况下不一致
* @return 如果 beforePathWithoutQueryString
是null或者empty,返回 {@link StringUtils#EMPTY}
* 如果arrayValueMap
是null或者empty,返回 beforePathWithoutQueryString
* @since 1.4.0
*/
static String combineUrl(String beforePathWithoutQueryString,Map arrayValueMap,String charsetType){
if (isNullOrEmpty(beforePathWithoutQueryString)){
return EMPTY;
}
if (isNullOrEmpty(arrayValueMap)){//没有参数 直接return
return beforePathWithoutQueryString;
}
StringBuilder sb = new StringBuilder();
sb.append(beforePathWithoutQueryString);
sb.append(QUESTIONMARK);
sb.append(toQueryStringUseArrayValueMap(toSafeArrayValueMap(arrayValueMap, charsetType)));
return sb.toString();
}
}