Разбор строк запроса на Android

Java EE имеет ServletRequest.getParameterValues ​​().

На платформах, отличных от EE, URL.getQuery() просто возвращает строку.

Каков нормальный способ правильного синтаксического анализа строки запроса в URL-адресе, если не на Java EE?


< напыщенная >

Это популярно в ответах, чтобы попытаться сделать свой собственный парсер. Это очень интересный и захватывающий проект микрокодирования, но Я не могу сказать, что это хорошая идея: (

Нижеприведенные фрагменты кода, как правило, ошибочны или повреждены, кстати. Разбить их - интересное упражнение для читателя. И хакерам, атакующим сайты, которые их используют.

Разбор строк запроса - это хорошо определенная проблема, но чтение спецификации и понимание нюансов нетривиально. Намного лучше позволить некоторому кодеру библиотеки платформ выполнить тяжелую работу и сделать исправление для вас!

< /напыщенная >

Ответ 1

Так как вещи 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"); // get your value

Ответ 2

На Android:

import android.net.Uri;

[...]

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

Ответ 4

Вот ответ BalusC, но он компилирует и возвращает результаты:

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

Если у вас есть лифты (сервер или клиент) на вашем пути к классу, вы можете использовать классы использования причала (см. 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

Если вы используете Spring 3.1 или выше (yikes, надеялся, что поддержка вернется дальше), вы можете использовать UriComponents и UriComponentsBuilder:

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

components.getQueryParams() возвращает a MultiValueMap<String, String>

Вот еще несколько документов.

Ответ 7

Для сервлета или страницы JSP вы можете получить пары ключей/значений querystring с помощью request.getParameter( "paramname" )

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

Есть и другие способы сделать это, но так, как я это делаю во всех сервлетах и ​​страницах jsp, которые я создаю.

Ответ 8

Анализ строки запроса немного сложнее, чем кажется, в зависимости от того, насколько вы прощаете.

Сначала строка запроса представляет собой ascii байты. Вы читаете эти байты по одному и конвертируете их в символы. Если персонаж? или затем он сигнализирует начало имени параметра. Если символ =, то он сигнализирует начало значения параметра. Если символ%, то он сигнализирует начало закодированного байта. Вот где это становится сложно.

Когда вы читаете в% char, вы должны прочитать следующие два байта и интерпретировать их как шестнадцатеричные цифры. Это означает, что следующие два байта будут 0-9, a-f или A-F. Слейте эти две шестнадцатеричные цифры вместе, чтобы получить значение байта. Но помните, байты не являются символами. Вы должны знать, какая кодировка использовалась для кодирования символов. Символ é не кодирует то же самое в UTF-8, как в ISO-8859-1. В общем, невозможно знать, какая кодировка использовалась для заданного набора символов. Я всегда использую UTF-8, потому что мой веб-сайт настроен так, чтобы всегда обслуживать все, используя UTF-8, но на практике вы не можете быть уверены. Некоторые пользовательские агенты скажут вам кодировку символов в запросе; вы можете попробовать прочитать это, если у вас есть полный HTTP-запрос. Если у вас просто есть URL-адрес в изоляции, удачи.

В любом случае, если вы используете UTF-8 или некоторую другую многобайтовую кодировку символов, теперь, когда вы декодировали один закодированный байт, вам нужно отложить его до тех пор, пока вы не заберете следующий байт. Вам нужны все закодированные байты, которые вместе, потому что вы не можете правильно декодировать один байт за один раз. Отложите все байты, которые вместе, а затем полностью декодируют их для восстановления вашего персонажа.

Плюс это становится веселее, если вы хотите быть снисходительным и учитывать пользовательские агенты, которые мешают URL-адресам. Например, некоторые клиенты электронной почты дважды кодируют вещи. Или удвойте символы & = chars (например: http://yoursite.com/blah??p1==v1&&p2==v2). Если вы хотите попытаться изящно справиться с этим, вам нужно добавить логику к вашему парсеру.

Ответ 9

На Android я попытался использовать ответ @diyism, но я столкнулся с проблемой пространственного символа, вызванной @rpetrich, например: Я заполняю форму, где username = "us+us" и password = "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");

Ответ 10

У меня есть методы для достижения этой цели:

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

Ответ 11

На Android вы можете использовать статический метод Uri.parse класса android.net.Uri для тяжелого подъема. Если вы делаете что-либо с URI и намерениями, вы все равно захотите его использовать.

Ответ 12

Просто для справки, это то, чем я закончил (на основе URLEncodedUtils и возврата Карты).

Особенности:

  • он принимает часть строки запроса в URL-адресе (вы можете использовать request.getQueryString())
  • пустая строка запроса создаст пустой Map
  • параметр без значения (? test) будет сопоставлен с пустым 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;
}

Вспомогательный помощник (значения хранятся в массиве String так же, как в 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;
}

Ответ 13

Это работает для меня.. Я не уверен, почему каждый из них был после карты, спискa > Все, что мне нужно, это простая карта имени.

Чтобы все было просто, я использовал сборку в 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

В Android это просто, как код ниже:

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

Также, если вы не хотите регистрировать каждый ожидаемый ключ запроса, используйте:

sanitzer.setAllowUnregisteredParamaters(true)

Перед вызовом:

sanitzer.parseUrl(yourUrl)

Ответ 15

Guava Multimap лучше подходит для этого. Вот короткая чистая версия:

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);
        }
    }

Ответ 16

Apache AXIS2 имеет автономную реализацию QueryStringParser.java. Если вы не используете Axis2, просто скачайте исходный код и тестовый пример отсюда -

http://svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/transport/http/util/QueryStringParser.java

http://svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/kernel/test/org/apache/axis2/transport/http/util/QueryStringParserTest.java

Ответ 17

Я не думаю, что в JRE есть один. Подобные функции можно найти в других пакетах, таких как Apache HttpClient. Если вы не используете другие пакеты, вам просто нужно написать свои собственные. Это не так сложно. Вот что я использую,

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

На основании ответа от BalusC я написал несколько примеров: Java-Code:

    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

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;
}

Ответ 21

с помощью Guava:

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

Отвечаю здесь, потому что это популярная тема. Это чистое решение в Kotlin, которое использует рекомендуемый API UrlQuerySanitizer. Смотрите официальную документацию. Я добавил конструктор строк для объединения и отображения параметров.

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

На Android есть класс Uri в пакете android.net. Обратите внимание, что Uri является частью android.net, а URI - частью java.net.

У класса Uri есть много функций для извлечения пар ключ-значение запроса. enter image description here

Следующая функция возвращает пары ключ-значение в виде HashMap.

В Java:

Map<String, String> getQueryKeyValueMap(Uri uri){
    HashMap<String, String> keyValueMap = new HashMap();
    String key;
    String value;

    Set<String> keyNamesList = uri.getQueryParameterNames();
    Iterator iterator = keyNamesList.iterator();

    while (iterator.hasNext()){
        key = (String) iterator.next();
        value = uri.getQueryParameter(key);
        keyValueMap.put(key, value);
    }
    return keyValueMap;
}

В Котлине:

fun getQueryKeyValueMap(uri: Uri): HashMap<String, String> {
        val keyValueMap = HashMap<String, String>()
        var key: String
        var value: String

        val keyNamesList = uri.queryParameterNames
        val iterator = keyNamesList.iterator()

        while (iterator.hasNext()) {
            key = iterator.next() as String
            value = uri.getQueryParameter(key) as String
            keyValueMap.put(key, value)
        }
        return keyValueMap
    }

Ответ 24

этот метод принимает 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;
}

Ответ 25

Вы говорите "Java", но "не Java EE". Вы имеете в виду, что используете JSP и/или сервлеты, но не полный стек Java EE? В этом случае вам все равно нужно иметь request.getParameter().

Если вы имеете в виду, что пишете Java, но вы не пишете JSP или сервлеты, или просто используете 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, вы можете просто использовать request.getParameters.)