在Android上解析查询字符串

279

Java EE具有ServletRequest.getParameterValues()

在非EE平台上,URL.getQuery()仅返回一个字符串。

在非Java EE环境下,正确解析URL中的查询字符串的常规方法是什么?


回答中流行的方式是尝试制作自己的解析器。这是一个很有趣和激动人心的微编码项目,但我不能说这是一个好主意

下面的代码片段通常存在缺陷或错误。破解它们对读者来说是一个有趣的练习。对于攻击使用它们的网站的黑客也一样

解析查询字符串是一个已经定义明确的问题,但阅读规范并理解其细节并不容易。最好让一些平台库的开发人员为您完成艰苦的工作和修复!


1
你想从一个servlet还是JSP页面来完成这个操作?在回答之前,我需要一些澄清。 - ChadNC
我正在尝试在Android上实现这个,但所有平台上的答案都将是有用的指针(也适用于其他可能遇到这个问题的人),所以请不要犹豫! - Will
1
你还需要解析POST参数吗? - Thilo
2
即使您正在使用J2EE(或通过OSGi添加了选定的EE包,如我所做),这个问题也可能是有意义的。在我的情况下,查询字符串/ URL编码的POST正文由系统的一部分处理,该部分故意忽略诸如ServletRequest之类的东西。 - Hanno Fietz
@Will 我不知道你是否已经解决了你的问题,但是这个库 http://cxf.apache.org/docs/jax-rs-advanced-features.html 对我很有帮助!看一下,还支持FIQL。 - rafa.ferreira
显示剩余2条评论
25个回答

214

在安卓上:

import android.net.Uri;

[...]

Uri uri=Uri.parse(url_string);
uri.getQueryParameter("para1");

20
请注意,这里使用的是 Uri 类而不是 URI 类(Uri 属于 android.net,而 URI 属于 java.net)。 - Marius
5
请注意,在冰激凌三明治版本之前,此功能无法将值中的 + 字符解析为空格字符。 - rpetrich
@rpetrich,实际上文档表明该错误优先于果冻豆,包括冰淇淋三明治。参考 - Big McLargeHuge

71

自从Android M以来,情况变得更加复杂了。由android.net.URI.getQueryParameter()返回的答案有一个错误,会破坏掉在JellyBean之前的空格。

Apache URLEncodedUtils.parse()可以工作,但在L中已被弃用,并在M中被移除

因此,现在最好的答案是UrlQuerySanitizer。它自API级别1以来一直存在,现在仍然存在。它还让您考虑到棘手的问题,例如如何处理特殊字符或重复值。

最简单的代码是:

UrlQuerySanitizer.ValueSanitizer sanitizer = UrlQuerySanitizer.getAllButNullLegal();
// remember to decide if you want the first or last parameter with the same name
// If you want the first call setPreferFirstRepeatedParameter(true);
sanitizer.parseUrl(url);
String value = sanitizer.getValue("paramName");

如果您对默认的解析行为感到满意,可以执行以下操作:

new UrlQuerySanitizer(url).getValue("paramName")

但是你应该确保了解默认的解析行为,因为它可能不是你想要的。


5
无效。sdk-23中的UrlQuerySanitizer只有一个名为sanitize()的方法。 - Ninja
这将把特殊字符和表情符号解码为“_”。我只能采用 https://dev59.com/GXI-5IYBdhLWcg3wy76n#35638979。 - Irshu
有没有类似于Spring框架的库? - iamjoshua

64

9
这个功能不仅在Android上可用,也可以在Apache Http Client库中找到。顺便提一下,Apache的链接已更改,最新链接为: http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/utils/URLEncodedUtils.html - Cristian Vrabie
9
URLEncodedUtils.parse() 很让人恼火,它返回一个列表,你必须通过循环来查找特定键的值。如果它像 BalusC 的回答那样返回一个Map,会更好一些。 - Asaph
1
@Hanno Fietz你的意思是你相信这些替代方案?我知道它们存在漏洞。我知道指出我看到的缺陷只会鼓励人们采用“修复”版本,而不是自己寻找我忽略的缺陷。 - Will
8
我想像解析函数会返回一个列表,这样它就可以维护位置顺序并更容易允许重复的条目。 - dhaag23
1
这实际上是一种不推荐的方法,因为它们在API级别22中弃用了apache http类,并将在23(Android M)中删除。 - Kazuki
显示剩余5条评论

26
public static Map<String, List<String>> getUrlParameters(String url)
        throws UnsupportedEncodingException {
    Map<String, List<String>> params = new HashMap<String, List<String>>();
    String[] urlParts = url.split("\\?");
    if (urlParts.length > 1) {
        String query = urlParts[1];
        for (String param : query.split("&")) {
            String pair[] = param.split("=", 2);
            String key = URLDecoder.decode(pair[0], "UTF-8");
            String value = "";
            if (pair.length > 1) {
                value = URLDecoder.decode(pair[1], "UTF-8");
            }
            List<String> values = params.get(key);
            if (values == null) {
                values = new ArrayList<String>();
                params.put(key, values);
            }
            values.add(value);
        }
    }
    return params;
}

1
JVM 注意:我已经使用 Java 集合在 Scala 中实现了一个等效的形式;这是 Github Gist 的链接:https://gist.github.com/3504765 - Jay Taylor
2
我建议将 String pair[] = param.split("="); 更改为 String pair[] = param.split("=", 2);,以便仅在第一次出现时拆分键值对。我认为允许在值中有未编码的等号。 - Dennie
谢谢 @Dennie,已添加。 - dfrankow

22

如果你的类路径上有jetty(服务器或客户端)库,那么你可以使用jetty util类(参见javadoc),例如:

import org.eclipse.jetty.util.*;
URL url = new URL("www.example.com/index.php?foo=bar&bla=blub");
MultiMap<String> params = new MultiMap<String>();
UrlEncoded.decodeTo(url.getQuery(), params, "UTF-8");

assert params.getString("foo").equals("bar");
assert params.getString("bla").equals("blub");

13

如果你正在使用Spring 3.1或更高版本(天哪,本来希望支持更低版本的),你可以使用UriComponentsUriComponentsBuilder

UriComponents components = UriComponentsBuilder.fromUri(uri).build();
List<String> myParam = components.getQueryParams().get("myParam");

components.getQueryParams() 返回一个 MultiValueMap<String, String>

这里有更多的文档


这正是我正在寻找的东西。我的问题是如何获取URI?我被困在一些代码的维护中,我不能太多地改变它,我们也没有使用HttpServlet。相反,只是使用注释和Spring(@Get,@Produces(mediaType)和@Path(“/dataAsJSON/datafield/{datafield}”))。只需要知道如何获取查询字符串,以便我可以像示例中所示那样解析它。 - Nelda.techspiress

6

I have methods to achieve this:

1):

public static String getQueryString(String url, String tag) {
    String[] params = url.split("&");
    Map<String, String> map = new HashMap<String, String>();
    for (String param : params) {
        String name = param.split("=")[0];
        String value = param.split("=")[1];
        map.put(name, value);
    }

    Set<String> keys = map.keySet();
    for (String key : keys) {
        if(key.equals(tag)){
         return map.get(key);
        }
        System.out.println("Name=" + key);
        System.out.println("Value=" + map.get(key));
    }
    return "";
}

2) 最简单的方法是使用 Uri 类:

public static String getQueryString(String url, String tag) {
    try {
        Uri uri=Uri.parse(url);
        return uri.getQueryParameter(tag);
    }catch(Exception e){
        Log.e(TAG,"getQueryString() " + e.getMessage());
    }
    return "";
}

这是如何使用两种方法之一的示例:

String url = "http://www.jorgesys.com/advertisements/publicidadmobile.htm?position=x46&site=reform&awidth=800&aheight=120";      
String tagValue = getQueryString(url,"awidth");

tagValue的值为800


1
第二个解决方案对我有效。 - Parth Patel

5

对于servlet或JSP页面,您可以使用request.getParameter("paramname")获取查询字符串键/值对。

String name = request.getParameter("name");

还有其他的方法可以实现,但这是我在创建所有servlet和jsp页面时所采用的方法。


3
HttpServletRequest是J2EE的一部分,但他没有这个。而使用getParamter()并不是真正的解析。 - Mr. Shiny and New 安宇
3
请花些时间阅读我在评论中请求澄清他问题的留言。这个回答是针对他回复那条评论而做出的回应,在那里他说:“我正在尝试在Android上实现这一点,但所有平台上的答案都可能是有用的答案,可能会提供指引(也适用于其他可能遇到这个问题的人),所以不要保留任何信息!”我基于那个评论回答了他的问题。如果你没有有用的补充,请不要添加任何内容。 - ChadNC
1
不要太沮丧。在我看来,“这并没有回答问题”是有用的补充。 - Mr. Shiny and New 安宇
1
无论是Android还是其他,问题在于如何解析包含URL的字符串并从中获取URL参数。你正在移植的是Servlet API的一部分,其中Servlet容器会为您解析传入的HTTP请求参数。这与问题无关,因为问题是关于解析包含URL的字符串,而不是HTTP请求,也不是在Servlet容器内部。 - mvmn

5
在Android上,我尝试使用@diyism的答案,但遇到了由@rpetrich提出的空格字符问题,例如: 我填写一个表单,其中用户名=“us+us”密码=“pw pw”,导致URL字符串看起来像:
http://somewhere?username=us%2Bus&password=pw+pw

然而,@diyism的代码返回"us+us"和"pw+pw",即它不能检测空格字符。如果URL被重写为"%20",则可以识别空格字符。
http://somewhere?username=us%2Bus&password=pw%20pw

这导致以下修复方案:
Uri uri = Uri.parse(url_string.replace("+", "%20"));
uri.getQueryParameter("para1");

replace(" ", "%20") 这感觉不太对。但对我来说起到了作用 :D - Mārtiņš Briedis
正确的语法应该是:"some string".replaceAll("[+]", "%20"); - RRTW
@RRTW 这并不必要;String#replace方法不接受正则表达式。 - Unmitigated

4
在 Android 上,代码如下:
UrlQuerySanitizer sanitzer = new UrlQuerySanitizer(url);
String value = sanitzer.getValue("your_get_parameter");

此外,如果您不想注册每个预期的查询键,请使用以下方法:
sanitzer.setAllowUnregisteredParamaters(true)

在调用之前:

sanitzer.parseUrl(yourUrl)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接