Qu'est-ce qui est valide et qu'est-ce qui ne l'est pas dans une requête URI?

100

Contexte (question plus bas)

J'ai fait des recherches sur Google en lisant des RFC et des questions SO pour essayer de résoudre ce problème, mais je n'ai toujours pas de prise.

Donc je suppose que nous votons simplement pour la "meilleure" réponse et c'est tout, ou?

Fondamentalement, cela se résume à ceci.

3.4. Composant de requête

Le composant de requête est une chaîne d'informations à interpréter par la ressource.

query = *uric

Dans un composant de requête, les caractères ";", "/", "?", ":", "@", "&", "=", "+", "," Et "$" sont réservés.

La première chose qui me déroute est que * uric est défini comme ceci

uric = reserved | unreserved | escaped

reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

Ceci est cependant quelque peu clarifié par des paragraphes tels que

La classe de syntaxe «réservée» ci-dessus fait référence aux caractères qui sont autorisés dans un URI, mais qui peuvent ne pas être autorisés dans un composant particulier de la syntaxe URI générique; ils sont utilisés comme délimiteurs des composants décrits dans la section 3.

Les caractères de l'ensemble "réservé" ne sont pas réservés dans tous les contextes. L'ensemble des caractères réellement réservés dans un composant URI donné est défini par ce composant. En général, un caractère est réservé si la sémantique de l'URI change si le caractère est remplacé par son encodage US-ASCII échappé.

Ce dernier extrait semble quelque peu en arrière, mais il indique clairement que le jeu de caractères réservé dépend du contexte. Pourtant, 3.4 déclare que tous les caractères réservés sont réservés dans un composant de requête, cependant, la seule chose qui changerait la sémantique ici est d'échapper au point d'interrogation (?) Car les URI ne définissent pas le concept de chaîne de requête.

À ce stade, j'ai complètement abandonné les RFC, mais j'ai trouvé la RFC 1738 particulièrement intéressante.

Une URL HTTP prend la forme:

http://<host>:<port>/<path>?<searchpart>

Dans les composants <chemin> et <searchpart>, "/", ";", "?" sont réservés. Le caractère "/" peut être utilisé dans HTTP pour désigner une structure hiérarchique.

J'interprète cela au moins en ce qui concerne les URL HTTP que la RFC 1738 remplace la RFC 2396. Parce que la requête URI n'a aucune notion de chaîne de requête, l'interprétation de réservé ne me permet pas vraiment de définir des chaînes de requête comme je suis habitué à faire maintenant.

Question

Tout cela a commencé lorsque j'ai voulu transmettre une liste de nombres avec la demande d'une autre ressource. Je n'y ai pas beaucoup réfléchi et je l'ai juste passé sous forme de valeurs séparées par des virgules. À ma grande surprise, la virgule s'est échappée. La requête page.html?q=1,2,3encodée en page.html?q=1%2C2%2C3elle fonctionne, mais elle est moche et ne s'y attendait pas. C'est là que j'ai commencé à passer par les RFC.

Ma première question est simplement: est-ce que l'encodage des virgules est vraiment nécessaire?

Ma réponse, selon RFC 2396: oui, selon RFC 1738: non

Plus tard, j'ai trouvé des articles liés au passage de listes entre les demandes. Où l'approche CSV était sur le point d'être mauvaise. Cela est apparu à la place, (je n'ai jamais vu cela auparavant).

page.html?q=1;q=2;q=3

Ma deuxième question, est-ce une URL valide?

Ma réponse, selon RFC 2396: non, selon RFC 1738: non (; est réservé)

Je n'ai aucun problème avec le passage de csv tant qu'il s'agit de nombres, mais oui, vous courez le risque d'avoir à encoder et décoder des valeurs d'avant en arrière si la virgule est soudainement nécessaire pour autre chose. Quoi qu'il en soit, j'ai essayé la chaîne de requête point-virgule avec ASP.NET et le résultat n'était pas ce à quoi je m'attendais.

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

Je ne vois pas en quoi cela diffère grandement d'une approche csv, car lorsque je demande «a», j'obtiens une chaîne avec des virgules. ASP.NET n'est certainement pas une implémentation de référence, mais cela ne m'a pas encore laissé tomber.

Mais le plus important - ma troisième question - où est la spécification pour cela? et que feriez-vous ou d'ailleurs ne pas faire?

John Leidegren
la source
Comment la RFC 1738 peut-elle remplacer la RFC 2396, lorsque la RFC 2396 a été publiée près de 4 ans plus tard?
Matthew Flaschen
1
En ce qui concerne les URL et ce qui a pratiquement du sens, c'est mon interprétation que c'est le cas. (remplacer n'est probablement pas le bon mot cependant, car il a été utilisé dans la terminologie RFC pour les anciennes RFC obsolètes, la RFC 1738 ne se sent pas tout à fait obsolète quand c'est la seule spécification si elle est trouvée qui vous permet de mettre une chaîne de requête dans la partie de recherche de l'URL)
John Leidegren

Réponses:

69

Le fait qu'un caractère soit réservé dans un composant URL générique ne signifie pas qu'il doit être échappé lorsqu'il apparaît dans le composant ou dans les données du composant. Le caractère doit également être défini comme un délimiteur dans la syntaxe générique ou spécifique au schéma et l'apparence du caractère doit être dans les données.

La norme actuelle pour les URI génériques est la RFC 3986 , qui a ceci à dire:

2.2. Caractères réservés

Les URI incluent des composants et des sous-composants délimités par des caractères dans l'ensemble «réservé». Ces caractères sont appelés «réservés» car ils peuvent (ou non) être définis comme des délimiteurs par la syntaxe générique, par chaque syntaxe spécifique au schéma, ou par la syntaxe spécifique à l'implémentation de l'algorithme de déréférencement d'un URI. Si les données d'un composant URI entrent en conflit avec le but d'un caractère réservé comme délimiteur [emphase ajoutée], alors les données en conflit doivent être codées en pourcentage avant que l'URI ne soit formé.

   reserved = gen-delims / sub-delims

   gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"

   sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" / "="

3.3. Composant de chemin

[...]
pchar = non réservé / encodé pct / sous-délimiteurs / ":" / "@"
[...]

3.4 Composant de requête

[...]
      requête = * (pchar / "/" / "?")

Ainsi, les virgules sont explicitement autorisées dans les chaînes de requête et ne doivent être échappées dans les données que si des schémas spécifiques le définissent comme un délimiteur. Le schéma HTTP n'utilise pas la virgule ou le point-virgule comme délimiteur dans les chaînes de requête, il n'est donc pas nécessaire de les échapper. Que les navigateurs respectent cette norme est une autre affaire.

L'utilisation de CSV devrait fonctionner correctement pour les données de type chaîne, il vous suffit de suivre les conventions CSV standard et de citer les données ou d'échapper les virgules avec des barres obliques inverses.

Quant à la RFC 2396, elle autorise également les virgules sans échappement dans les chaînes de requête HTTP:

2.2. Caractères réservés

De nombreux URI incluent des composants constitués ou délimités par certains caractères spéciaux. Ces caractères sont appelés «réservés», car leur utilisation dans le composant URI est limitée à leur fonction réservée. Si les données d'un composant URI sont en conflit avec l'objectif réservé, les données en conflit doivent être échappées avant de former l'URI.

Étant donné que les virgules n'ont pas de but réservé dans le schéma HTTP, elles ne doivent pas être échappées dans les données. La note du § 2.3 sur les caractères réservés étant ceux qui changent de sémantique lorsqu'ils sont codés en pourcentage ne s'applique qu'en général; les caractères peuvent être codés en pourcentage sans changer la sémantique pour des schémas spécifiques et pourtant être réservés.

outis
la source
23

Pour répondre à ce qui est valide dans une chaîne de requête, j'ai vérifié quels caractères spéciaux sont remplacés par chrome lors d'une demande:

Space -> %20
! -> !
" -> %22
# -> removed, marks the end of the query string
% -> %
& -> &
' -> %27
( -> (
) -> )
* -> *
+ -> + (this usually means blank when received at the server, so encode if necessary)
, -> ,
- -> -
. -> .
/ -> /
: -> :
; -> ;
< -> %3C
= -> =
> -> %3E
? -> ?
@ -> @
[ -> [
\ -> \
] -> ]
^ -> ^
_ -> _
` -> `
{ -> {
| -> |
} -> }
~ -> ~

Extended ASCII (like °) -> Every character from this set is encoded

Remarque: cela ne signifie probablement pas que vous ne devriez pas échapper les caractères qui n'ont pas été remplacés lorsque vous générez des URI pour les liens. Par exemple, il est souvent recommandé de ne pas utiliser ~dans les URI en raison de problèmes de compatibilité, mais c'est toujours un caractère valide.

Un autre exemple serait le signe plus qui est valide mais généralement traité comme un blanc encodé lorsqu'un serveur le reçoit dans le cadre d'une requête. Ainsi, il doit être encodé même s'il est valide lorsque son but est de représenter un plus et non un espace.

Donc, pour répondre à ce qui doit être encodé: Caractères invalides et caractères que vous souhaitez traiter littéralement mais qui ont une signification particulière ou peuvent causer des problèmes au niveau du serveur.

user764754
la source
Est-ce /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b#1;c#2qu'un paramètre de requête valide?
Sumit Jain le
@SumitJain Non, car #il ne peut pas apparaître dans la partie requête d'un URI tel quel . Vous devrez l'encoder en tant que %23, de sorte que l'URI devrait être /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b%231;c%232.
Dai
10

Juste utiliser ?q=1+2+3

Je réponds ici à une quatrième question :) qui n'a pas demandé mais tout a commencé par: comment transmettre une liste de nombres séparés par des virgules? Il me semble que la meilleure approche est simplement de les passer séparés par des espaces, où les espaces seront encodés sous forme d'URL +. Fonctionne très bien, tant que vous savez que les valeurs de la liste ne contiennent pas d'espaces (ce que les nombres ont tendance à ne pas faire).

Nas Banov
la source
Bien que cela devrait être un commentaire (car il ne répond pas à la question), merci. +est encore plus logique dans le cas précis où je cherchais à utiliser une virgule.
Gajus
6

page.html? q = 1; q = 2; q = 3

est-ce une URL valide?

Oui. Le ;est réservé, mais pas par un RFC. Le contexte qui définit ce composant est la définition du application/x-www-form-urlencodedtype de média, qui fait partie du standard HTML (section 17.13.4.1 ). En particulier la note sournoise cachée dans la section B.2.2 :

Nous recommandons que les implémenteurs de serveur HTTP, et en particulier les implémenteurs CGI, prennent en charge l'utilisation de ";" à la place de "&" pour éviter aux auteurs d'avoir à échapper aux caractères "&" de cette manière.

Malheureusement, de nombreux frameworks de script côté serveur populaires, y compris ASP.NET, ne prennent pas en charge cette utilisation.

bobince
la source
Ainsi, bien que la ?q=1;q=2;q=3requête soit valide, elle est ambiguë: certains frameworks côté serveur la liront comme signifiant { q: '1;q=2;q=3' }, d'autres peuvent le faire comme { q: {'1', '2', '3'}}.
Nas Banov
1
Oui. Et ce qui est pire, HTML5 n'inclut plus le langage about ;, ce qui signifie que HTML4 et HTML5 sont incohérents. Ugh, les dangers du langage non normatif dans un document de spécification ...
bobince
@NasBanov Et encore d'autres (par exemple PHP) l'interpréteront comme{ q: 3 }
Nicholas Shanks
1
@NicholasShanks - là où PHP est impliqué, tous les paris sont ouverts! :)
Nas Banov
1

Je voudrais noter qu'il page.html?q=1&q=2&q=3s'agit également d'une URL valide. C'est une manière tout à fait légitime d'exprimer un tableau dans une chaîne de requête. La technologie de votre serveur déterminera comment exactement cela est présenté.

Dans ASP classique, vous vérifiez Response.QueryString("q").Count, puis utilisez Response.QueryString("q")(0)(et (1) et (2)).

Notez que vous avez également vu cela dans votre ASP.NET (je pense que ce n'était pas prévu, mais regardez):

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

Notez que le point-virgule est ignoré, donc vous avez adéfini deux fois et vous avez obtenu sa valeur deux fois, séparés par une virgule. L'utilisation de toutes les esperluettes Default.aspx?a=1&a=2&b=1&a=3donnera a"1,2,3". Mais je suis sûr qu'il existe une méthode pour obtenir chaque élément individuel, au cas où les éléments eux-mêmes contiennent des virgules. C'est simplement la propriété par défaut de la QueryString non indexée qui concatène les sous-valeurs avec des séparateurs par virgule.

ErikE
la source
1

J'ai eu le même problème. L'URL contenant le lien hypertexte était une URL tierce et attendait une liste de paramètres au format page.html?q=1,2,3UNIQUEMENT et l'URL page.html?q=1%2C2%2C3ne fonctionnait pas. J'ai pu le faire fonctionner en utilisant javascript. Ce n'est peut-être pas la meilleure approche, mais vous pouvez consulter la solution ici si cela aide quelqu'un.

sabrer
la source
-3

Si vous envoyez les caractères ENCODED dans un fichier FLASH / SWF , vous devez ENCODER le caractère deux fois !! (à cause de l'analyseur Flash)

T.Todua
la source