Cómo hacer que Uri.EscapeDataString cumpla con RFC 3986

La clase Uri tiene como valor predeterminado RFC 2396. Para OpenID y OAuth, necesito escapar de Uri de forma coherente con RFC 3986.

De la documentación de la clase System.Uri :

Por defecto, cualquier carácter reservado en el URI se escapa de acuerdo con RFC 2396. Este comportamiento cambia si se habilitan los identificadores de recursos internacionales o el análisis de nombres de dominio internacional, en cuyo caso se escapan los caracteres reservados en el URI de acuerdo con RFC 3986 y RFC 3987.

La documentación también establece que activar este modo IRI y, por lo tanto, el comportamiento RFC 3986 significa agregar un elemento de sección uri a machine.config y esto a su archivo app / web.config:

      

Pero ya sea que esté presente en el archivo .config o no, obtengo el mismo comportamiento de escape (no 3986) para una aplicación .NET 3.5 SP1. ¿Qué más debo hacer para que Uri.EscapeDataString use las reglas RFC 3986? (específicamente, para escapar de los caracteres reservados como se define en ese RFC)

Al no haber podido hacer que Uri.EscapeDataString asumiera el comportamiento de RFC 3986, escribí mi propio método de escape compatible con RFC 3986. Aprovecha Uri.EscapeDataString, y luego ‘actualiza’ el escape para cumplir con RFC 3986.

 ///  /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. ///  private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; ///  /// Escapes a string according to the URI data string rules given in RFC 3986. ///  /// The value to escape. /// The escaped value. ///  /// The  method is supposed 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 doesn't), we can't rely on every /// host actually having this configuration element present. ///  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(); } 

Esto se ha corregido en .NET 4.5 para que funcione de manera predeterminada, mira aquí .

Acabo de crear una nueva biblioteca llamada PUrify (después de encontrarme con este problema) que manejará que esto funcione para .NET pre 4.5 (funciona para 3.5) y Mono a través de una variación del enfoque en esta publicación . PUrify no cambia EscapeDataString pero le permite tener Uris con caracteres reservados que no se escapará.

¿Qué versión del marco estás usando? Parece que muchos de estos cambios se realizaron en el marco de tiempo de “.NET Framework 3.5. 3.0 SP1 y 2.0 SP1” ( de MSDN ).

No pude encontrar una mejor respuesta (ya sea 100% framework o 100% reimplementation), así que he creado esta abominación. Parece estar trabajando con 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(); } } 

Me doy cuenta de que esta pregunta y sus respuestas tienen algunos años, pero pensé que compartiría mi hallazgo cuando tuviera problemas para cumplir con .Net 4.5 .

Si su código se está ejecutando bajo asp.net, simplemente configurando el proyecto para apuntar a 4.5 y ejecutándose en una máquina con 4.5 o posterior, aún puede obtener el comportamiento de 4.0. asegurarse de que esté configurado en web.config.

De este artículo de blog sobre msdn ,

Si no existe ningún presente en Web.config, suponemos que la aplicación quería el comportamiento peculiar de 4.0.