我得到了这样的URI:
https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback
我需要一个包含已解析元素的集合:
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
确切地说,我需要一个与c# /等价的Java。净HttpUtility。ParseQueryString方法。
我有一个Kotlin版本,看看这是如何在谷歌的顶部结果。
@Throws(UnsupportedEncodingException::class)
fun splitQuery(url: URL): Map<String, List<String>> {
val queryPairs = LinkedHashMap<String, ArrayList<String>>()
url.query.split("&".toRegex())
.dropLastWhile { it.isEmpty() }
.map { it.split('=') }
.map { it.getOrEmpty(0).decodeToUTF8() to it.getOrEmpty(1).decodeToUTF8() }
.forEach { (key, value) ->
if (!queryPairs.containsKey(key)) {
queryPairs[key] = arrayListOf(value)
} else {
if(!queryPairs[key]!!.contains(value)) {
queryPairs[key]!!.add(value)
}
}
}
return queryPairs
}
还有扩展方法
fun List<String>.getOrEmpty(index: Int) : String {
return getOrElse(index) {""}
}
fun String.decodeToUTF8(): String {
URLDecoder.decode(this, "UTF-8")
}
Kotlin的答案,最初参考https://stackoverflow.com/a/51024552/3286489,但通过整理代码和提供2个版本的改进版本,并使用不可变的集合操作
使用java.net.URI提取查询。然后使用下面提供的扩展函数
假设你只想要查询的最后一个值,即page2&page3将得到{page=3},使用下面的扩展函数
fun URI.getQueryMap(): Map<String, String> {
if (query == null) return emptyMap()
return query.split("&")
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.associateBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
假设你想要查询所有值的列表,即page2&page3将得到{page=[2,3]}
fun URI.getQueryMapList(): Map<String, List<String>> {
if (query == null) return emptyMap()
return query.split("&")
.distinct()
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.groupBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
使用方法如下
val uri = URI("schema://host/path/?page=&page=2&page=2&page=3")
println(uri.getQueryMapList()) // Result is {page=[2, 3]}
println(uri.getQueryMap()) // Result is {page=3}
使用上面提到的注释和解决方案,我存储所有的查询参数使用映射<字符串,对象>对象可以是字符串或集<字符串>。解决方案如下。建议使用某种类型的url验证器先验证url,然后调用convertQueryStringToMap方法。
private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
public static Map<String, Object> convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException {
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME);
Map<String, Object> queryStringMap = new HashMap<>();
for(NameValuePair param : params){
queryStringMap.put(param.getName(), handleMultiValuedQueryParam(queryStringMap, param.getName(), param.getValue()));
}
return queryStringMap;
}
private static Object handleMultiValuedQueryParam(Map responseMap, String key, String value) {
if (!responseMap.containsKey(key)) {
return value.contains(",") ? new HashSet<String>(Arrays.asList(value.split(","))) : value;
} else {
Set<String> queryValueSet = responseMap.get(key) instanceof Set ? (Set<String>) responseMap.get(key) : new HashSet<String>();
if (value.contains(",")) {
queryValueSet.addAll(Arrays.asList(value.split(",")));
} else {
queryValueSet.add(value);
}
return queryValueSet;
}
}
有一个新版本的Apache HTTP客户端- org.apache.httpcomponents。client5 -其中URLEncodedUtils现在已弃用。应该改为使用URIBuilder:
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.net.URIBuilder;
private static Map<String, String> getQueryParameters(final String url) throws URISyntaxException {
return new URIBuilder(new URI(url), StandardCharsets.UTF_8).getQueryParams()
.stream()
.collect(Collectors.toMap(NameValuePair::getName,
nameValuePair -> URLDecoder.decode(nameValuePair.getValue(), StandardCharsets.UTF_8)));
}
一个kotlin版本
由马提亚提供的答案
fun decomposeQueryString(query: String, charset: Charset): Map<String, String?> {
return if (query.split("?").size <= 1)
emptyMap()
else {
query.split("?")[1]
.split("&")
.map { it.split(Pattern.compile("="), 2) }
.associate {
Pair(
URLDecoder.decode(it[0], charset.name()),
if (it.size > 1) URLDecoder.decode(it[1], charset.name()) else null
)
}
}
}
这需要问号'?’。
org.keycloak.common.util.UriUtils
我不得不在Keycloak扩展中解析uri和查询参数,并发现这个实用工具类非常有用:
org.keycloak.common.util.UriUtils:
static MultivaluedHashMap<String,String> decodeQueryString(String queryString)
还有一个有用的方法可以删除一个查询参数:
static String stripQueryParam(String url, String name)
解析URL有
org.keycloak.common.util.KeycloakUriBuilder:
KeycloakUriBuilder uri(String uriTemplate)
String getQuery()
还有很多其他的好东西。