web-dev-qa-db-fra.com

Analyse des chaînes de requête sur Android

Java EE a ServletRequest.getParameterValues ​​() .

Sur les plates-formes non EE, RL.getQuery () renvoie simplement une chaîne.

Quelle est la méthode normale pour analyser correctement la chaîne de requête dans une URL lorsque pas sur Java EE?


< fanfaron >

Il est courant dans les réponses d'essayer de créer votre propre analyseur. C'est un projet de micro-codage très intéressant et passionnant, mais Je ne peux pas dire que c'est une bonne idée :(

Les extraits de code ci-dessous sont généralement défectueux ou cassés. Les casser est un exercice intéressant pour le lecteur. Et aux pirates informatiques attaquant les sites Web qui les utilisent .

L'analyse des chaînes de requête est un problème bien défini, mais la lecture de la spécification et la compréhension des nuances ne sont pas triviales. Il est de loin préférable de laisser un codeur de bibliothèque de plate-forme faire le travail dur et de réparer, pour vous!

</rant >

266
Will

Depuis Android M les choses se sont compliquées. La réponse de Android.net.URI . GetQueryParameter () a un bogue qui casse les espaces avant JellyBean. Apache URLEncodedUtils.parse () a fonctionné, mais était obsolète en L , et supprimé en M .

La meilleure réponse est donc maintenant rlQuerySanitizer . Cela existe depuis le niveau 1 de l’API et existe toujours. Cela vous fait également penser à des questions délicates telles que la gestion des caractères spéciaux ou des valeurs répétées.

Le code le plus simple est

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
60
Nick Fortescue

Sur Android:

import Android.net.Uri;

[...]

Uri uri=Uri.parse(url_string);
uri.getQueryParameter("para1");
202
diyism
64
Will

Voici réponse de BalusC , mais il compile et renvoie les résultats:

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

Si vous avez des bibliothèques jetty (serveur ou client) sur votre chemin de classe, vous pouvez utiliser les classes jetty util (voir javadoc ), par exemple:

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");
22
moritz

Si vous utilisez Spring 3.1 ou une version ultérieure (oui, espérait que le support remonterait plus loin), vous pouvez utiliser les options UriComponents et UriComponentsBuilder:

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

components.getQueryParams() renvoie un MultiValueMap<String, String>

Voici encore de la documentation .

13
Nick Spacek

Pour un servlet ou une page JSP, vous pouvez obtenir des paires clé/valeur de chaîne de requête à l'aide de request.getParameter ("paramname").

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

Il y a d'autres façons de le faire, mais c'est comme cela que je le fais dans toutes les servlets et les pages jsp que je crée.

5
ChadNC

J'ai des méthodes pour y parvenir:

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) et le moyen le plus simple de le faire en utilisant ri classe:

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

et voici un exemple d'utilisation de l'une des deux méthodes suivantes:

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

la valeur de tagValue est 800

4
Jorgesys

L'analyse de la chaîne de requête est un peu plus compliquée qu'il n'y parait, selon votre volonté de pardonner.

Premièrement, la chaîne de requête est en octets ascii. Vous lisez ces octets un à la fois et les convertissez en caractères. Si le personnage est? ou & puis il signale le début d'un nom de paramètre. Si le caractère est =, il signale le début d'une valeur de paramètre. Si le caractère est%, il signale le début d'un octet codé. Voici où cela devient difficile.

Lorsque vous lisez un caractère%, vous devez lire les deux octets suivants et les interpréter comme des chiffres hexadécimaux. Cela signifie que les deux prochains octets seront 0-9, a-f ou A-F. Collez ces deux chiffres hexadécimaux ensemble pour obtenir la valeur de votre octet. Mais rappelez-vous, les octets ne sont pas des caractères. Vous devez savoir quel encodage a été utilisé pour encoder les caractères. Le caractère é ne code pas la même chose dans UTF-8 que dans ISO-8859-1. En général, il est impossible de savoir quel encodage a été utilisé pour un jeu de caractères donné. J'utilise toujours UTF-8 car mon site Web est configuré pour tout servir en UTF-8, mais en pratique, vous ne pouvez en être sûr. Certains agents utilisateurs vous indiqueront le codage des caractères dans la demande. vous pouvez essayer de lire cela si vous avez une requête HTTP complète. Si vous avez juste une URL isolée, bonne chance.

Quoi qu'il en soit, en supposant que vous utilisez UTF-8 ou un autre encodage de caractères multi-octets, maintenant que vous avez décodé un octet encodé, vous devez le mettre de côté jusqu'à ce que vous capturiez l'octet suivant. Vous avez besoin de tous les octets codés qui sont ensemble car vous ne pouvez pas décoder correctement les URL, un octet à la fois. Mettez de côté tous les octets qui sont ensemble, puis décodez-les tous en même temps pour reconstruire votre personnage.

De plus, cela devient plus amusant si vous voulez être indulgent et prendre en compte les agents utilisateurs qui gèrent les URL. Par exemple, certains clients de messagerie Web codent les choses en double. Ou doublez les caractères? & = (Par exemple: http://yoursite.com/blah??p1==v1&&p2==v2). Si vous voulez essayer de régler cela avec élégance, vous devrez ajouter plus de logique à votre analyseur.

Sur Android, j'ai essayé d'utiliser @diyism answer mais j'ai rencontré le problème de caractère d'espacement soulevé par @rpetrich, par exemple: je remplis un formulaire dans lequel username = "us+us" et password = "pw pw" provoquant l'apparence d'une chaîne URL:

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

Cependant, le code @diyism renvoie "us+us" et "pw+pw", c’est-à-dire qu’il ne détecte pas le caractère espace. Si l'URL a été réécrite avec %20, le caractère d'espacement est identifié:

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

Cela conduit au correctif suivant:

Uri uri = Uri.parse(url_string.replace("+", "%20"));
uri.getQueryParameter("para1");
4
Stephen Quan

Sur Android sa aussi simple que le code ci-dessous:

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

De plus, si vous ne souhaitez pas enregistrer chaque utilisation attendue de la clé de requête:

sanitzer.setAllowUnregisteredParamaters(true)

Avant d'appeler:

sanitzer.parseUrl(yourUrl)
3
Kevin Crain

Cela fonctionne pour moi .. Je ne suis pas sûr de savoir pourquoi tout le monde cherchait une carte, Liste> Tout ce dont j'avais besoin était une simple carte de valeur de nom.

Pour que les choses restent simples, j'ai utilisé le build dans 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;
}
3
Dave

Sur Android, vous pouvez utiliser la méthode statique Uri.parse de la classe Android.net.Uri pour faire le gros du travail. Si vous faites quelque chose avec les URI et les intentions, vous voudrez quand même l'utiliser.

3
Patrick O'Leary

Juste pour référence, voici ce que j’ai obtenu (basé sur URLEncodedUtils et renvoyant une carte).

Fonctionnalités:

  • il accepte la partie chaîne de requête de l'URL (vous pouvez utiliser request.getQueryString())
  • une chaîne de requête vide produira un Map vide
  • un paramètre sans valeur (? test) sera mappé sur un List<String> vide

Code:

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

Un assistant de compatibilité (les valeurs sont stockées dans un tableau String exactement comme dans 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;
}
3
Dan

Multimap de Guava est mieux adapté pour cela. Voici une version courte propre:

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);
        }
    }
2
pathikrit
1
Sripathi Krishnan

Réponse d'origine ici

Sur Android, la classe Uri est incluse dans le package Android.net . Notez que Uri fait partie d'Android.net, tandis que URI fait partie de Java.net.

La classe Uri a de nombreuses fonctions pour extraire les paires clé-valeur de requête. enter image description here

La fonction suivante renvoie des paires clé-valeur sous la forme de HashMap.

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

À Kotlin:

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

Basé sur la réponse de BalusC, j’ai écrit un exemple de code 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
            }
        }
    }
0
Andreas

Répondre ici parce que c'est un fil populaire. Il s’agit d’une solution propre de Kotlin qui utilise l’application recommandée UrlQuerySanitizer. Voir la documentation officielle . J'ai ajouté un constructeur de chaînes pour concaténer et afficher les paramètres.

    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")
    }
0
jungledev
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;
}
0
wafna

en utilisant la goyave:

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

Utilisez Apache HttpComponents et connectez-le avec un code de collection pour accéder aux paramètres par valeur: http://www.joelgerard.com/2012/09/14/parsing-query-strings-in-Java-and-accessing -values-by-key /

0
Joel

Je ne pense pas qu'il y en a un dans JRE. Vous pouvez trouver des fonctions similaires dans d'autres paquets, comme Apache HttpClient. Si vous n'utilisez aucun autre paquet, il vous suffit d'écrire le vôtre. Ce n'est pas si dur. Voici ce que j'utilise,

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;
 } 
}
0
ZZ Coder