Pergunta

A classe URI padrão para RFC 2396. Para OpenID e OAuth, preciso de Uri escapar consistente com a RFC 3986.

De Documentação da classe System.uri:

Por padrão, quaisquer caracteres reservados no URI são escapados de acordo com a RFC 2396. Esse comportamento mudar se os identificadores internacionais de recursos ou o nome de domínio internacional forem ativados, nesse caso, os caracteres reservados no URI forem escapados de acordo com a RFC 3986 e RFC 3987.

A documentação também afirma que ativar esse modo IRI e, portanto, o comportamento da RFC 3986 significa adicionar um elemento de seção URI ao Machine.config e isso ao seu arquivo app/web.config:

<configuration>
  <uri>
  <idn enabled="All" />
  <iriParsing enabled="true" />
  </uri>
</configuration>

Mas se isso está presente no arquivo .config ou não, estou obtendo o mesmo comportamento de escape (3986) para um aplicativo .NET 3.5 SP1. O que mais eu preciso fazer para obter Uri.EscapeDataString Para usar as regras da RFC 3986? (Especificamente, para escapar dos caracteres reservados, conforme definido naquela RFC)

Foi útil?

Solução

Não tendo sido capaz de obter URI.ESCAPEDATASTRING para enfrentar o comportamento da RFC 3986, escrevi meu próprio método de fuga compatível com RFC 3986. Ele aproveita o Uri.escapedatastring e, em seguida, 'atualiza' a fuga para a conformidade da RFC 3986.

/// <summary>
/// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
/// </summary>
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };

/// <summary>
/// Escapes a string according to the URI data string rules given in RFC 3986.
/// </summary>
/// <param name="value">The value to escape.</param>
/// <returns>The escaped value.</returns>
/// <remarks>
/// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
/// RFC 3986 behavior if certain elements are present in a .config file.  Even if this
/// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
/// host actually having this configuration element present.
/// </remarks>
internal static string EscapeUriDataStringRfc3986(string value) {
    // Start with RFC 2396 escaping by calling the .NET method to do the work.
    // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
    // If it does, the escaping we do that follows it will be a no-op since the
    // characters we search for to replace can't possibly exist in the string.
    StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));

    // Upgrade the escaping to RFC 3986, if necessary.
    for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) {
        escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
    }

    // Return the fully-RFC3986-escaped string.
    return escaped.ToString();
}

Outras dicas

Na verdade, isso foi corrigido no .NET 4.5 para funcionar por padrão, consulte aqui.

Acabei de criar uma nova biblioteca chamada Purificar (depois de entrar nessa edição) que lidará com isso para que isso funcione para .NET pré 4.5 (trabalha para 3.5) e mono através de uma variação da abordagem neste publicar. O Purify não muda o EscapeDatatring, mas permite que você tenha URIs com chars reservados que não serão escapados.

Percebo que essa pergunta e respostas têm alguns anos, mas pensei em compartilhar minha descoberta quando tivesse problemas conformidade sob .NET 4.5.

Se o seu código estiver em execução no ASP.NET, basta definir o projeto para segmentar 4.5 e executar em uma máquina com 4.5 ou posterior, você ainda poderá obter um comportamento 4.0. Você precisa garantir <httpRuntime targetFramework="4.5" /> está definido no web.config.

A partir de Este artigo do blog no msdn,

Se não há <httpRuntime targetFramework> Atributo presente no web.config, assumimos que o aplicativo queria o comportamento 4.0 Quirks.

Qual versão da estrutura você está usando? Parece que muitas dessas mudanças foram feitas no (de msdn) ".NET Framework 3.5. 3.0 SP1 e 2.0 SP1" Timeframe.

Não consegui encontrar uma resposta melhor (100% da estrutura ou 100% de reimplementação), por isso criei essa abominação. Parece estar trabalhando com OAuth.

class al_RFC3986
{
    public static string Encode(string s)
    {
        StringBuilder sb = new StringBuilder(s.Length*2);//VERY rough estimate
        byte[] arr = Encoding.UTF8.GetBytes(s);

        for (int i = 0; i < arr.Length; i++)
        {
            byte c = arr[i];

            if(c >= 0x41 && c <=0x5A)//alpha
                sb.Append((char)c);
            else if(c >= 0x61 && c <=0x7A)//ALPHA
                sb.Append((char)c);
            else if(c >= 0x30 && c <=0x39)//123456789
                sb.Append((char)c);
            else if (c == '-' || c == '.' || c == '_' || c == '~')
                sb.Append((char)c);
            else
            {
                sb.Append('%');
                sb.Append(Convert.ToString(c, 16).ToUpper());
            }
        }
        return sb.ToString();
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top