代码之家  ›  专栏  ›  技术社区  ›  Will

分析Android上的查询字符串

  •  258
  • Will  · 技术社区  · 15 年前

    爪哇EE ServletRequest.getParameterValues() .

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

    在URL中正确解析查询字符串的正常方法是什么? 关于JavaEE?


    < 咆哮 gt;

    在答案中,尝试生成自己的解析器是很流行的。这是一个非常有趣和令人兴奋的微编码项目,但是 我不能说这是个好主意 :(

    下面的代码片段通常都有缺陷或损坏,顺便说一句,对读者来说,破坏它们是一个有趣的练习。 以及攻击使用它们的网站的黑客 .

    解析查询字符串是一个定义良好的问题,但是阅读规范并理解细微差别是非常重要的。最好让一些平台库编码人员为您做艰苦的工作,并进行修复!

    < /咆哮 gt;

    24 回复  |  直到 6 年前
        1
  •  58
  •   Nick Fortescue    8 年前

    自从安卓M以来,事情变得更复杂了。答案 android.net.URI .getQueryParameter()有一个bug,它在Jellbean之前中断了空格。 Apache URLEncodedUtils.parse() 工作,但 deprecated in L removed in 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"); // get your value
    
        2
  •  199
  •   Benno    9 年前

    安卓:

    import android.net.Uri;
    
    [...]
    
    Uri uri=Uri.parse(url_string);
    uri.getQueryParameter("para1");
    
        4
  •  25
  •   dfrankow    7 年前

    这里是 BalusC's answer ,但它编译并返回结果:

    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("=");
                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;
    }
    
        5
  •  21
  •   moritz    13 年前

    如果类路径上有jetty(服务器或客户机)libs,则可以使用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");
    
        6
  •  13
  •   Nick Spacek    11 年前

    如果您使用的是Spring3.1或更高版本(Yikes,希望支持能再进一步),您可以使用 UriComponents UriComponentsBuilder :

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

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

    Here's some more documentation .

        7
  •  5
  •   ChadNC    15 年前

    对于servlet或JSP页面,可以使用request.getParameter(“paramname”)获取queryString键/值对。

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

    还有其他的方法可以做到这一点,但我在创建的所有servlet和jsp页面中都是这样做的。

        8
  •  4
  •   Mr. Shiny and New 安宇    15 年前

    解析查询字符串要比看起来复杂一些,这取决于您希望得到的宽容程度。

    首先,查询字符串是ASCII字节。您一次读取一个字节,然后将其转换为字符。如果角色是?或者&然后它指示参数名称的开头。如果字符为=则表示参数值的开始。如果字符为%,则表示编码字节的开始。这就是问题的症结所在。

    当你读取一个%char时,你必须读取接下来的两个字节,并将它们解释为十六进制数字。这意味着接下来的两个字节将是0-9、a-f或a-f。将这两个十六进制数字粘合在一起以获得字节值。但请记住, 字节不是字符 . 你必须知道用什么编码来编码字符。字符_)在UTF-8中的编码与在ISO-8859-1中的编码不同。一般来说,不可能知道给定字符集使用了什么编码。我总是使用UTF-8,因为我的网站被配置为总是使用UTF-8为所有内容提供服务,但实际上你不能确定。一些用户代理会告诉您请求中的字符编码;如果您有完整的HTTP请求,您可以尝试读取它。如果你只有一个单独的网址,祝你好运。

    无论如何,假设您使用的是UTF-8或其他一些多字节字符编码,既然您已经解码了一个编码的字节,那么您必须将它放在一边,直到捕获下一个字节。您需要将所有编码的字节放在一起,因为您不能一次正确地对一个字节进行URL解码。将所有的字节放在一起,然后一次对它们进行解码,以重建您的角色。

    另外,如果你想宽容一点,并且考虑到用户代理会破坏URL的话,这会变得更有趣。例如,一些Webmail客户机对事物进行双重编码。还是加倍?&=字符(例如: http://yoursite.com/blah??p1==v1&&p2==v2 )如果您想要优雅地处理这个问题,您需要向您的解析器添加更多的逻辑。

        9
  •  4
  •   Dan    13 年前

    仅供参考,这是我最后得出的结论(基于urlencodedutils,并返回一张地图)。

    特征:

    • 它接受URL的查询字符串部分(您可以使用 request.getQueryString() )
    • 空查询字符串将生成空的 Map
    • 没有值(?测试)将映射到空 List<String>

    代码:

    public static Map<String, List<String>> getParameterMapOfLists(String queryString) {
        Map<String, List<String>> mapOfLists = new HashMap<String, List<String>>();
        if (queryString == null || queryString.length() == 0) {
            return mapOfLists;
        }
        List<NameValuePair> list = URLEncodedUtils.parse(URI.create("http://localhost/?" + queryString), "UTF-8");
        for (NameValuePair pair : list) {
            List<String> values = mapOfLists.get(pair.getName());
            if (values == null) {
                values = new ArrayList<String>();
                mapOfLists.put(pair.getName(), values);
            }
            if (pair.getValue() != null) {
                values.add(pair.getValue());
            }
        }
    
        return mapOfLists;
    }
    

    兼容性助手(值存储在字符串数组中,就像 ServletRequest.getParameterMap() ):

    public static Map<String, String[]> getParameterMap(String queryString) {
        Map<String, List<String>> mapOfLists = getParameterMapOfLists(queryString);
    
        Map<String, String[]> mapOfArrays = new HashMap<String, String[]>();
        for (String key : mapOfLists.keySet()) {
            mapOfArrays.put(key, mapOfLists.get(key).toArray(new String[] {}));
        }
    
        return mapOfArrays;
    }
    
        10
  •  4
  •   Stephen Quan    12 年前

    在Android上,我尝试使用@diyism answer,但遇到了@rpetrich提出的空格字符问题,例如: 我填了一张表格 username = "us+us" password = "pw pw" 使URL字符串看起来像:

    http://somewhere?username=us%2Bus&password=pw+pw
    

    但是,@diyism代码返回 "us+us" "pw+pw" 也就是说,它不检测空格字符。如果使用 %20 识别空格字符:

    http://somewhere?username=us%2Bus&password=pw%20pw
    

    这将导致以下修复:

    Uri uri = Uri.parse(url_string.replace("+", "%20"));
    uri.getQueryParameter("para1");
    
        11
  •  4
  •   Jorgesys    6 年前

    我有办法做到这一点:

    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

        12
  •  3
  •   Patrick O'Leary    14 年前

    在Android上,可以使用的uri.parse静态方法 android.net.Uri 上课做举重。如果你对uris和intent做了什么,你无论如何都要使用它。

        13
  •  3
  •   Dave    12 年前

    这对我有用…… 我不知道为什么每个人都在地图后,请列出> 我只需要一个简单的名称-值映射。

    为了简单起见,我使用了内置的uri.getquery();

    public static Map<String, String> getUrlParameters(URI uri)
        throws UnsupportedEncodingException {
        Map<String, String> params = new HashMap<String, String>();
        for (String param : uri.getQuery().split("&")) {
            String pair[] = param.split("=");
            String key = URLDecoder.decode(pair[0], "UTF-8");
            String value = "";
            if (pair.length > 1) {
                value = URLDecoder.decode(pair[1], "UTF-8");
            }
            params.put(new String(key), new String(value));
        }
        return params;
    }
    
        14
  •  3
  •   Kevin Crain    8 年前

    在Android上,其简单代码如下:

    UrlQuerySanitizer sanitzer = new UrlQuerySanitizer(url);
    String value = sanitzer.getValue("your_get_parameter");
    

    另外,如果不想注册每个预期的查询键,请使用:

    sanitzer.setAllowUnregisteredParamaters(true)
    

    打电话之前:

    sanitzer.parseUrl(yourUrl)
    
        15
  •  2
  •   pathikrit    12 年前

    番石榴的多重地图更适合这个。下面是一个简短的清晰版本:

    Multimap<String, String> getUrlParameters(String url) {
            try {
                Multimap<String, String> ret = ArrayListMultimap.create();
                for (NameValuePair param : URLEncodedUtils.parse(new URI(url), "UTF-8")) {
                    ret.put(param.getName(), param.getValue());
                }
                return ret;
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }
    
        17
  •  0
  •   ZZ Coder    15 年前

    我觉得JRE里没有。您可以在其他包中找到类似的函数,如ApacheHTTPClient。如果你不使用其他软件包,你只需要自己写。没那么难。这是我用的,

    public class QueryString {
    
     private Map<String, List<String>> parameters;
    
     public QueryString(String qs) {
      parameters = new TreeMap<String, List<String>>();
    
      // Parse query string
         String pairs[] = qs.split("&");
         for (String pair : pairs) {
                String name;
                String value;
                int pos = pair.indexOf('=');
                // for "n=", the value is "", for "n", the value is null
             if (pos == -1) {
              name = pair;
              value = null;
             } else {
           try {
            name = URLDecoder.decode(pair.substring(0, pos), "UTF-8");
                  value = URLDecoder.decode(pair.substring(pos+1, pair.length()), "UTF-8");            
           } catch (UnsupportedEncodingException e) {
            // Not really possible, throw unchecked
               throw new IllegalStateException("No UTF-8");
           }
             }
             List<String> list = parameters.get(name);
             if (list == null) {
              list = new ArrayList<String>();
              parameters.put(name, list);
             }
             list.add(value);
         }
     }
    
     public String getParameter(String name) {        
      List<String> values = parameters.get(name);
      if (values == null)
       return null;
    
      if (values.size() == 0)
       return "";
    
      return values.get(0);
     }
    
     public String[] getParameterValues(String name) {        
      List<String> values = parameters.get(name);
      if (values == null)
       return null;
    
      return (String[])values.toArray(new String[values.size()]);
     }
    
     public Enumeration<String> getParameterNames() {  
      return Collections.enumeration(parameters.keySet()); 
     }
    
     public Map<String, String[]> getParameterMap() {
      Map<String, String[]> map = new TreeMap<String, String[]>();
      for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
       List<String> list = entry.getValue();
       String[] values;
       if (list == null)
        values = null;
       else
        values = (String[]) list.toArray(new String[list.size()]);
       map.put(entry.getKey(), values);
      }
      return map;
     } 
    }
    
        18
  •  0
  •   Andreas    14 年前

    基于BalusC的答案,我编写了一些Java代码:

        if (queryString != null)
        {
            final String[] arrParameters = queryString.split("&");
            for (final String tempParameterString : arrParameters)
            {
                final String[] arrTempParameter = tempParameterString.split("=");
                if (arrTempParameter.length >= 2)
                {
                    final String parameterKey = arrTempParameter[0];
                    final String parameterValue = arrTempParameter[1];
                    //do something with the parameters
                }
            }
        }
    
        19
  •  0
  •   wafna    13 年前
    public static Map <String, String> parseQueryString (final URL url)
            throws UnsupportedEncodingException
    {
        final Map <String, String> qps = new TreeMap <String, String> ();
        final StringTokenizer pairs = new StringTokenizer (url.getQuery (), "&");
        while (pairs.hasMoreTokens ())
        {
            final String pair = pairs.nextToken ();
            final StringTokenizer parts = new StringTokenizer (pair, "=");
            final String name = URLDecoder.decode (parts.nextToken (), "ISO-8859-1");
            final String value = URLDecoder.decode (parts.nextToken (), "ISO-8859-1");
            qps.put (name, value);
        }
        return qps;
    }
    
        20
  •  0
  •   Joel    12 年前

    使用apache httpcomponents并将其与一些收集代码连接,以按值访问参数: http://www.joelgerard.com/2012/09/14/parsing-query-strings-in-java-and-accessing-values-by-key/

        21
  •  0
  •   Tyson    11 年前

    使用番石榴:

    Multimap<String,String> parseQueryString(String queryString, String encoding) {
        LinkedListMultimap<String, String> result = LinkedListMultimap.create();
    
        for(String entry : Splitter.on("&").omitEmptyStrings().split(queryString)) {
            String pair [] = entry.split("=", 2);
            try {
                result.put(URLDecoder.decode(pair[0], encoding), pair.length == 2 ? URLDecoder.decode(pair[1], encoding) : null);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
    
        return result;
    }
    
        22
  •  0
  •   jungledev    6 年前

    回答这里是因为这是一个流行的话题。这是Kotlin中使用推荐的 UrlQuerySanitizer 应用程序编程接口。 See the official documentation . 我添加了一个字符串生成器来连接和显示参数。

        var myURL: String? = null
        // if the url is sent from a different activity where you set it to a value
        if (intent.hasExtra("my_value")) {
            myURL = intent.extras.getString("my_value")
        } else {
            myURL = intent.dataString
        }
    
        val sanitizer = UrlQuerySanitizer(myURL)
        // We don't want to manually define every expected query *key*, so we set this to true
        sanitizer.allowUnregisteredParamaters = true
        val parameterNamesToValues: List<UrlQuerySanitizer.ParameterValuePair> = sanitizer.parameterList
        val parameterIterator: Iterator<UrlQuerySanitizer.ParameterValuePair> = parameterNamesToValues.iterator()
    
        // Helper simply so we can display all values on screen
        val stringBuilder = StringBuilder()
    
        while (parameterIterator.hasNext()) {
            val parameterValuePair: UrlQuerySanitizer.ParameterValuePair = parameterIterator.next()
            val parameterName: String = parameterValuePair.mParameter
            val parameterValue: String = parameterValuePair.mValue
    
            // Append string to display all key value pairs
            stringBuilder.append("Key: $parameterName\nValue: $parameterValue\n\n")
        }
    
        // Set a textView's text to display the string
        val paramListString = stringBuilder.toString()
        val textView: TextView = findViewById(R.id.activity_title) as TextView
        textView.text = "Paramlist is \n\n$paramListString"
    
        // to check if the url has specific keys
        if (sanitizer.hasParameter("type")) {
            val type = sanitizer.getValue("type")
            println("sanitizer has type param $type")
        }
    
        23
  •  -2
  •   HaveNoDisplayName    9 年前

    此方法获取par名称和par值的uri和返回映射

      public static Map<String, String> getQueryMap(String uri) {
    
        String queryParms[] = uri.split("\\?");
    
        Map<String, String> map = new HashMap<>();// 
    
        if (queryParms == null || queryParms.length == 0) return map;
    
        String[] params = queryParms[1].split("&");
        for (String param : params) {
            String name = param.split("=")[0];
            String value = param.split("=")[1];
            map.put(name, value);
        }
        return map;
    }
    
        24
  •  -3
  •   Arjan Tijms UML GURU    12 年前

    你说的是“Java”,而不是“Java EE”。您的意思是使用JSP和/或servlet,而不是完整的JavaEE堆栈?如果是这样,那么您仍然可以使用request.getParameter()。

    如果你的意思是你正在编写Java,但是你不是在写JSP和servlet,或者你只是用Java作为你的参考点,但是你在其他没有内置参数解析的平台上…哇,这听起来不太可能,但如果是这样,原则是:

    xparm=0
    word=""
    loop
      get next char
      if no char
        exit loop
      if char=='='
        param_name[xparm]=word
        word=""
      else if char=='&'
        param_value[xparm]=word
        word=""
        xparm=xparm+1
      else if char=='%'
        read next two chars
        word=word+interpret the chars as hex digits to make a byte
      else
        word=word+char
    

    (我可以编写Java代码,但这是毫无意义的,因为如果你有Java可用,你可以只使用请求。