All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.core.net.ParamUtil Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.3.0
Show newest version
/*
 * 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.
     * 
     * 

说明:

*
*
    *
  1. 如果原来的uriString没有指定的参数paramName,那么追加新的参数paramName和值paramValue.
  2. *
  3. 如果原来的uriString有指定的参数paramName,那么会被新的值替换paramValue.
  4. *
  5. 如果原来的uriString有指定的参数paramName,并且是多值类型(参数数组),那么多值参数中第一个会被新的值替换paramValue,其他的被丢弃.
  6. *
  7. 如果原来的uriString有参数,不管是拼接还是替换都会保持参数的原始顺序.
  8. *
  9. 如果uriString带有? 和参数,会先被截取,最后再拼接.
  10. *
  11. 如果uriString不带?,则自动增加?
  12. *
*
* *

示例:

*
* *
     * 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. * *

说明:

*
*
    *
  1. 如果原来的uriString没有指定的参数paramName,那么循环arrayValueMap,追加新的参数paramName和值 * paramValue.
  2. *
  3. 如果原来的uriString有指定的参数paramName,那么会被新的值替换paramValue.
  4. *
  5. 如果原来的uriString有指定的参数paramName,并且是多值类型(参数数组),那么多值参数中第一个会被新的值替换paramValue,其他的被丢弃.
  6. *
  7. 如果原来的uriString有参数,不管是拼接还是替换都会保持参数的原始顺序.
  8. *
  9. 如果uriString带有? 和参数,会先被截取,最后再拼接.
  10. *
  11. 如果uriString不带?,则自动增加?
  12. *
*
* *

示例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包含指定的参数名字,那么会被新的值替换. * *

说明:

*
*
    *
  1. 如果原来的uriString没有指定的参数paramName,那么循环arrayValueMap,追加新的参数paramName和值 * paramValue.
  2. *
  3. 如果原来的uriString有指定的参数paramName,那么会被新的值替换paramValue.
  4. *
  5. 如果原来的uriString有指定的参数paramName,并且是多值类型(参数数组),那么多值参数中第一个会被新的值替换paramValue,其他的被丢弃.
  6. *
  7. 如果原来的uriString有参数,不管是拼接还是替换都会保持参数的原始顺序.
  8. *
  9. 如果uriString带有? 和参数,会先被截取,最后再拼接.
  10. *
  11. 如果uriString不带?,则自动增加?
  12. *
*
* *

示例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. * *

说明:

*
*
    *
  1. 如果参数里面有相同名字的参数,那么转换的时候取第一个值
  2. *
  3. 内部使用 {@link LinkedHashMap},map顺序依照 queryString 分隔的顺序
  4. *
*
* *

示例:

*
* *
     * 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). * *

说明:

*
*
    *
  1. 内部使用 {@link LinkedHashMap},map顺序依照 queryString 逗号分隔的顺序
  2. *
  3. 解析方式:参数和参数之间是以 {@code &} 分隔, 参数的key和value 是以 = 号分隔
  4. *
*
* *

示例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}
     * 
* *
* *

规则:

* *
* *
    *
  1. 首先将singleValueMap 使用 {@link SortUtil#sortMapByKeyAsc(Map)} 进行排序,
  2. *
  3. 然后将map的key和value 直接连接
  4. *
* *
* *

说明:

*
*
    * *
  1. 常用于和第三方对接数据(比如网易易盾验证码,生成 待签名的字符串)
  2. *
  3. 该方法不会执行encode操作,使用原生值进行拼接
  4. * *
  5. *

    对于 null key或者null value的处理:

    * *
    *

    * 如果 singleValueMap 中,
    * 如果有 keynull,那么会使用 {@link StringUtils#EMPTY} 进行拼接;
    * 如果有 valuenull,那么会使用 {@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}
         * 
    * *
    *
  6. * *
*
* * @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}
     * 
* *
* *

规则:

* *
* *
    *
  1. 首先将singleValueMap 使用 {@link SortUtil#sortMapByKeyAsc(Map)} 进行排序,
  2. *
  3. 然后将map的value 直接连接
  4. *
* *
* *

说明:

*
*
    * *
  1. 常用于和第三方对接数据(比如银联大华捷通,生成 待签名的字符串)
  2. *
  3. 该方法不会执行encode操作,使用原生值进行拼接
  4. * *
  5. *

    对于 null key或者null value的处理:

    * *
    *

    * 如果 singleValueMap 中,
    * 如果有 valuenull,那么会使用 {@link StringUtils#EMPTY} 进行拼接 *

    * *

    示例:

    * *
         * Map{@code } map = newHashMap();
         * map.put("totalActual", null);
         * map.put("province", "江苏省");
         * 
         * LOGGER.debug(ParamUtil.toNaturalOrderingKeyJoinValue(map));
         * 
    * * 返回: * *
         * {@code 江苏省}
         * 
    * *
    *
  6. * *
*
* * @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}
     * 
* *
* *

规则:

* *
* *
    *
  1. 首先将singleValueMap 使用 {@link SortUtil#sortMapByKeyAsc(Map)} 进行排序,
  2. *
  3. 然后将map的key和value 使用= 符号 连接,不同的entry之间再使用{@code &} 符号进行连接,最终格式类似于 url 的queryString
  4. *
* *
* *

说明:

*
*
    * *
  1. 常用于和第三方对接数据(比如支付宝,生成 待签名的字符串)
  2. *
  3. 该方法不会执行encode操作,使用原生值进行拼接
  4. * *
  5. *

    对于 null key或者null value的处理:

    * *
    *

    * 如果 singleValueMap 中,
    * 如果有 keynull,那么会使用 {@link StringUtils#EMPTY} 进行拼接;
    * 如果有 valuenull,那么会使用 {@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=}
         * 
    * *
    *
  6. * *
*
* * @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. * *

说明:

*
*
    *
  1. * 只是简单的将map的key value 按照 singleValueMap的顺序 连接起来,最终格式类似于 url 的queryString,
    * 比如,参数名字param Name=name,param Value=zhangfei,那么返回值是 name=zhangfei *
  2. *
  3. 如果singleValueMap有key 是null,将使用 {@link StringUtils#EMPTY} 进行拼接
  4. *
  5. 如果singleValueMap有value 是null,将使用 {@link StringUtils#EMPTY} 进行拼接
  6. *
*
* *

示例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 连接起来. * *

说明:

*
*
    *
  1. 比如,参数名字 {@code paramName=name},{@code paramValues 为 zhangfei,guanyu},那么返回值是{@code name=zhangfei&name=guanyu}
  2. *
  3. paramName和每个值都会调用 {@link StringUtils#defaultString(String)}转换后才进行拼接
  4. *
*
* * @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,连接起来(不使用任何连接符). * *

说明:

*
*
    *
  1. 拼接的顺序按照 includeKeys 的顺序,目前适用于个别银行(比如汇付天下)需要将值拼接起来加密
  2. *
  3. 如果singleValueMap中的value是null,那么会以{@link StringUtils#EMPTY}替代,进行拼接
  4. *
*
* *

示例:

*
* *

* 场景: 拼接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()参数差别; * *

说明:

*
*
    *
  1. 主要是为了处理浏览器兼容问题,参见 * * java-request-getquerystring-value-different-between-chrome-and-ie-browser,chrome会将query进行 encoded再发送请求;而ie原封不动的发送.
  2. *
  3. 由于暂时不能辨别是否encoded过,所以先强制decode再encode;
  4. *
  5. 此处不能先转decode(query,charsetType),参数就是想传 =是转义符
  6. *
*
* * @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(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy