2010-02-08 5 views
12

Ho rintracciato un bug su un'applicazione Url Rewriting. Il bug si presentava come un problema di codifica su alcuni caratteri diacritici nella querystring.Qual è la differenza tra Request.Url.Query e Request.QueryString?

In sostanza, il problema era che una richiesta che era fondamentalmente /search.aspx?search=heřmánek si stava riscritto con una querystring di "search = egli% C5% 99m% c3% a1nek"

Il valore corretto (usando un codice diverso, funzionante) era una riscrittura della querystring come "search = he% u0159m% u00e1nek"

Notare la differenza tra le due stringhe. Tuttavia, se pubblichi entrambi, vedrai che la codifica Url riproduce la stessa stringa. Non è fino a quando non si utilizza il contesto. Funzione di riscrittura che interrompe la codifica. La stringa interrotta restituisce "heÅmánek" (utilizzando Request.QueryString ["Cerca"] e la stringa di lavoro restituisce "heřmánek" .Questo cambiamento avviene dopo la chiamata alla funzione di riscrittura

Ho tracciato questo verso il basso su un set di codice utilizzando Request.QueryString (funzionante) e l'altro utilizzando Request.Url.Query (request.Url restituisce un'istanza Uri)

Mentre ho risolto il bug, c'è un buco nella mia comprensione qui, quindi se Qualcuno sa la differenza, sono pronto per la lezione

risposta

2

La tua domanda ha davvero suscitato il mio interesse, quindi ho letto qualche lettura per l'ultima ora o così Non sono assolutamente sicuro di aver trovato la risposta ma lo butto fuori t qui per vedere cosa ne pensi.

Da quanto ho letto finora, Request.QueryString è in realtà "una versione analizzata della variabile QUERY_STRING nella raccolta ServerVariables" [reference], dove come Request.Url è (come dichiarato) l'URL raw incapsulato nel Oggetto Uri. Secondo this article, il "costruttore" della classe Uri ... analizza la [stringa url], la inserisce in formato canonico e fa tutte le codifiche di escape richieste. "

Pertanto, sembra che Request.QueryString utilizzi una funzione diversa per analizzare la variabile "QUERY_STRING" dal costruttore di ServerVariables. Questo spiegherebbe perché vedi la differenza tra i due. Ora, perché i metodi di codifica personalizzati vengono utilizzati dalla funzione di analisi personalizzata e la funzione di analisi dell'oggetto Uri è completamente al di fuori di me. Forse qualcuno un po 'più esperto sulla DLL di aspnet_isapi potrebbe fornire alcune risposte con quella domanda.

In ogni caso, si spera che il mio post abbia un senso. Una nota a margine, vorrei aggiungere un altro riferimento che fornisse anche una lettura molto approfondita e interessante: http://download.microsoft.com/download/6/c/a/6ca715c5-2095-4eec-a56f-a5ee904a1387/Ch-12_HTTP_Request_Context.pdf

+0

Entrambe le proprietà restituiscono la stessa stringa codificata per la maggior parte del tempo - i costruttori e l'analisi sono irrilevanti in questo caso. È solo dopo la chiamata di riscrittura che la codifica di Uri cambia. – zombat

+0

Forse la tua risposta e la risposta di womps sotto combinate è la verità. Forse è il caso che si conserva la codifica Url usando un codice Latin-1 e l'altro usa UTF-8. –

3

Quello che hai indicato come stringa "spezzata" è in realtà la codifica corretta in base agli standard. Quella che hai indicato come codifica "corretta" sta utilizzando un'estensione non standard alle specifiche per consentire un formato di %uXXXX (credo che indichi la codifica UTF-16).

In ogni caso, la stringa codificata "interrotta" è ok. È possibile utilizzare il seguente codice per verificare quello:

Uri uri = new Uri("http://www.example.com/test.aspx?search=heřmánek"); 
Console.WriteLine(uri.Query); 
Console.WriteLine(HttpUtility.UrlDecode(uri.Query)); 

Funziona correttamente. Comunque ... su una sensazione istintiva, ho provato con un UrlDecode-1 Latin codepage specificato, invece del default UTF-8:

Console.WriteLine(HttpUtility.UrlDecode(uri.Query, 
      Encoding.GetEncoding("iso-8859-1"))); 

... e ho avuto il valore di cattivo specificato, 'heÅmánek '.In altre parole, sembra che la chiamata a HttpContext.RewritePath() in qualche modo modifichi l'urlencoding/decoding per utilizzare la codepage Latin-1, anziché UTF-8, che è la codifica predefinita utilizzata dai metodi UrlEncode/Decode.

Questo mi sembra un insetto se me lo chiedi. Puoi guardare il codice RewritePath() nel riflettore e vedere che sta sicuramente giocando con la querystring - passandogli intorno a tutti i tipi di funzioni del percorso virtuale, e verso un codice IIS non gestito.

Mi chiedo se da qualche parte lungo la strada, l'Uri al centro dell'oggetto Request viene manipolato con la codepage errata? Ciò spiegherebbe perché il Request.Querystring (che è semplicemente i valori non elaborati dalle intestazioni HTTP) sarebbe corretto, mentre l'Uri che utilizza la codifica errata per i segni diacritici sarebbe errato.

+0

Sono felice che tu pensi che sia un bug. Non riesco a capire perché questo comportamento sarebbe in questo modo. –

0

Ho fatto un po 'di ricerche nel corso dell'ultimo giorno o giù di lì e penso di avere qualche informazione su questo.

Quando si utilizza Request.Querystring o HttpUtility.UrlDecode (o Encode), viene utilizzata la codifica specificata nell'elemento (in particolare l'attributo requestEncoding) di web.config (o la gerarchia .config se si haven ') t specificato) --- NOT Encoding.Default che è la codifica predefinita per il tuo server.

Quando la codifica è impostata su UTF-8, un singolo carattere unicode può essere codificato come valori esadecimale del 2% xx. Sarà anche decodificato in questo modo quando viene fornito l'intero valore.

Se si utilizza la codifica UrlDecoding con una codifica diversa da quella con cui è stato codificato l'url, si otterrà un risultato diverso.

Poiché HttpUtility.UrlEncode e UrlDecode possono prendere un parametro di codifica, è tentato di provare a codificare utilizzando una codepage ANSI, ma UTF-8 è il modo giusto per andare se si dispone del supporto del browser (apparentemente le vecchie versioni non lo fanno supporto UTF-8). Devi solo assicurarti che sia impostato correttamente e che entrambe le parti funzionino correttamente.

UTF-8 sembra essere la codifica di default: (dal riflettore .net System.Web.HttpRequest)

internal Encoding QueryStringEncoding 
{ 
    get 
    { 
     Encoding contentEncoding = this.ContentEncoding; 
     if (!contentEncoding.Equals(Encoding.Unicode)) 
     { 
      return contentEncoding; 
     } 
     return Encoding.UTF8; 
    } 
} 

Seguendo il percorso per scoprire il this.ContentEncoding vi porta a (anche in HttpRequest)

public Encoding ContentEncoding 
{ 
    get 
    { 
     if (!this._flags[0x20] || (this._encoding == null)) 
     { 
      this._encoding = this.GetEncodingFromHeaders(); 
      if (this._encoding == null) 
      { 
       GlobalizationSection globalization = RuntimeConfig.GetLKGConfig(this._context).Globalization; 
       this._encoding = globalization.RequestEncoding; 
      } 
      this._flags.Set(0x20); 
     } 
     return this._encoding; 
    } 
    set 
    { 
     this._encoding = value; 
     this._flags.Set(0x20); 
    } 
} 

Per rispondere alla tua domanda specifica sulla differenza betwen Request.Url.Quer e Request.QueryString ... ecco come HttpRequest costruisce la sua proprietà URL:

public Uri Url 
{ 
    get 
    { 
     if ((this._url == null) && (this._wr != null)) 
     { 
      string queryStringText = this.QueryStringText; 
      if (!string.IsNullOrEmpty(queryStringText)) 
      { 
       queryStringText = "?" + HttpEncoder.CollapsePercentUFromStringInternal(queryStringText, this.QueryStringEncoding); 
      } 
      if (AppSettings.UseHostHeaderForRequestUrl) 
      { 
       string knownRequestHeader = this._wr.GetKnownRequestHeader(0x1c); 
       try 
       { 
        if (!string.IsNullOrEmpty(knownRequestHeader)) 
        { 
         this._url = new Uri(this._wr.GetProtocol() + "://" + knownRequestHeader + this.Path + queryStringText); 
        } 
       } 
       catch (UriFormatException) 
       { 
       } 
      } 
      if (this._url == null) 
      { 
       string serverName = this._wr.GetServerName(); 
       if ((serverName.IndexOf(':') >= 0) && (serverName[0] != '[')) 
       { 
        serverName = "[" + serverName + "]"; 
       } 
       this._url = new Uri(this._wr.GetProtocol() + "://" + serverName + ":" + this._wr.GetLocalPortAsString() + this.Path + queryStringText); 
      } 
     } 
     return this._url; 
    } 
} 

Si può vedere che sta usando la classe HttpEncoder per eseguire la decodifica, ma utilizza lo stesso valore di QueryStringEncoding.

Poiché sto già postando un sacco di codice qui e chiunque può ottenere .NET Reflector, ho intenzione di snippet il resto. La proprietà QueryString proviene da HttpValueCollection che utilizza il metodo FillFromEncodedBytes per chiamare infine HttpUtility.UrlDecode (con il valore QueryStringEncoding impostato in precedenza), che alla fine chiama HttpEncoder per decodificarlo. Sembra che usino una metodologia diversa per decodificare i byte effettivi di querystring, ma la codifica che usano per farlo sembra essere la stessa.

È interessante per me che HttpEncoder abbia così tante funzioni che sembrano fare la stessa cosa, quindi è possibile che ci siano differenze in quei metodi che possono causare un problema.