No se pueden establecer algunos encabezados HTTP al usar System.Net.WebRequest

Cuando trato de agregar un par de clave / valor de encabezado HTTP en un objeto WebRequest , obtengo la siguiente excepción:

Este encabezado debe ser modificado usando la propiedad apropiada

Intenté agregar nuevos valores a la colección Headers utilizando el método Add (), pero sigo recibiendo la misma excepción.

 webRequest.Headers.Add(HttpRequestHeader.Referer, "http://stackoverflow.com"); 

Puedo evitar esto lanzando el objeto WebRequest a HttpWebRequest y estableciendo las propiedades como httpWebReq.Referer ="http://stackoverflow.com" , pero esto solo funciona para un puñado de encabezados que están expuestos a través de propiedades.

Me gustaría saber si hay una manera de obtener un control más detallado sobre la modificación de los encabezados con una solicitud de un recurso remoto.

Si necesita la respuesta breve y técnica, vaya directamente a la última sección de la respuesta.

Si quieres saber mejor, léelo todo, y espero que disfrutes …


Este problema también lo resolví hoy, y lo que descubrí hoy es que:

  1. las respuestas anteriores son verdaderas, como:

    1.1 le dice que el encabezado que está tratando de agregar ya existe y luego debe modificar su valor utilizando la propiedad adecuada (el indizador, por ejemplo), en lugar de intentar agregarlo nuevamente.

    1.2 Cada vez que cambie los encabezados de una HttpWebRequest , debe usar las propiedades adecuadas en el objeto en sí, si existen.

Gracias FOR y Jvenema por las pautas principales …

  1. Pero, lo que descubrí, y esa fue la pieza que falta en el rompecabezas es que:

    2.1 La clase WebHeaderCollection generalmente se accede a través de WebRequest .Heading o WebResponse .Headers. Algunos encabezados comunes se consideran restringidos y están expuestos directamente por la API (como Content-Type) o protegidos por el sistema y no se pueden modificar.

Los encabezados restringidos son:

  • Accept
  • Connection
  • Content-Length
  • Content-Type
  • Date
  • Expect
  • Host
  • If-Modified-Since
  • Range
  • Referer
  • Transfer-Encoding
  • User-Agent
  • Proxy-Connection

Entonces, la próxima vez que enfrente esta excepción y no sepa cómo resolver esto, recuerde que hay algunos encabezados restringidos, y la solución es modificar sus valores usando la propiedad apropiada explícitamente de la clase WebRequest / HttpWebRequest .


Editar: (útil, a partir de comentarios, comentario del usuario Kaido )

La solución es comprobar si el encabezado ya existe o está restringido ( WebHeaderCollection.IsRestricted(key) ) antes de llamar add

Me encontré con este problema con un cliente web personalizado. Creo que la gente puede confundirse debido a las múltiples formas de hacer esto. Al usar WebRequest.Create() puede convertir a HttpWebRequest y usar la propiedad para agregar o modificar un encabezado. Al usar un WebHeaderCollection , puede usar .Add("referer","my_url") .

Ex 1

 WebClient client = new WebClient(); client.Headers.Add("referer", "http://stackoverflow.com"); client.Headers.Add("user-agent", "Mozilla/5.0"); 

Ex 2

 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Referer = "http://stackoverflow.com"; request.UserAgent = "Mozilla/5.0"; response = (HttpWebResponse)request.GetResponse(); 

Todas las respuestas anteriores describen el problema sin proporcionar una solución. Aquí hay un método de extensión que resuelve el problema al permitirle configurar cualquier encabezado a través de su nombre de cadena.

Uso

 HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.SetRawHeader("content-type", "application/json"); 

Clase de extensión

 public static class HttpWebRequestExtensions { static string[] RestrictedHeaders = new string[] { "Accept", "Connection", "Content-Length", "Content-Type", "Date", "Expect", "Host", "If-Modified-Since", "Keep-Alive", "Proxy-Connection", "Range", "Referer", "Transfer-Encoding", "User-Agent" }; static Dictionary HeaderProperties = new Dictionary(StringComparer.OrdinalIgnoreCase); static HttpWebRequestExtensions() { Type type = typeof(HttpWebRequest); foreach (string header in RestrictedHeaders) { string propertyName = header.Replace("-", ""); PropertyInfo headerProperty = type.GetProperty(propertyName); HeaderProperties[header] = headerProperty; } } public static void SetRawHeader(this HttpWebRequest request, string name, string value) { if (HeaderProperties.ContainsKey(name)) { PropertyInfo property = HeaderProperties[name]; if (property.PropertyType == typeof(DateTime)) property.SetValue(request, DateTime.Parse(value), null); else if (property.PropertyType == typeof(bool)) property.SetValue(request, Boolean.Parse(value), null); else if (property.PropertyType == typeof(long)) property.SetValue(request, Int64.Parse(value), null); else property.SetValue(request, value, null); } else { request.Headers[name] = value; } } } 

Escenarios

Escribí un contenedor para HttpWebRequest y no quería exponer los 13 encabezados restringidos como propiedades en mi envoltorio. En su lugar, quería utilizar un Dictionary simple Dictionary .

Otro ejemplo es un proxy HTTP donde necesita tomar encabezados en una solicitud y reenviarlos al destinatario.

Hay muchos otros escenarios donde simplemente no es práctico o posible usar propiedades. Obligar al usuario a establecer el encabezado a través de una propiedad es un diseño muy inflexible por lo que se necesita reflexión. El lado positivo es que el reflection se abstrae, sigue siendo rápido (0,001 segundos en mis pruebas) y como método de extensión se siente natural.

Notas

Los nombres de encabezado son insensibles a las mayúsculas y minúsculas según el RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

Cada vez que cambie los encabezados de una HttpWebRequest , debe usar las propiedades adecuadas en el objeto en sí, si es que existen. Si tiene una WebRequest simple, asegúrese de convertirla a HttpWebRequest primero. A continuación, se puede acceder a Referrer en su caso a través de la ((HttpWebRequest)request).Referrer , por lo que no necesita modificar directamente el encabezado; simplemente establezca la propiedad en el valor correcto. ContentLength , ContentType , UserAgent , etc., todos deben configurarse de esta manera.

En mi humilde opinión, esta es una deficiencia en la parte de MS … configurar los encabezados a través de Headers.Add() debe llamar automáticamente a la propiedad adecuada detrás de las escenas, si eso es lo que quieren hacer.

Tuve la misma excepción cuando mi código intentó establecer el valor del encabezado “Aceptar” como este:

 WebRequest request = WebRequest.Create("http://someServer:6405/biprws/logon/long"); request.Headers.Add("Accept", "application/json"); 

La solución fue cambiarlo a esto:

 HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://someServer:6405/biprws/logon/long"); request.Accept = "application/json"; 

WebRequest es abstracto (y dado que cualquier clase heredante debe anular la propiedad Headers) … ¿qué WebRequest concreto está usando? En otras palabras, ¿cómo logras que ese objeto WebRequest se vincule?

ehr .. mnour respuesta me hizo darme cuenta de que el mensaje de error que estaba recibiendo es realmente acertado: le dice que el encabezado que está tratando de agregar ya existe y luego debe modificar su valor usando la propiedad apropiada (el indexador, por ejemplo ), en lugar de intentar agregarlo de nuevo. Eso es probablemente todo lo que estabas buscando.

Otras clases heredadas de WebRequest pueden tener incluso mejores propiedades que envuelven ciertos encabezados; Ver esta publicación, por ejemplo.

Básicamente, no. Es un encabezado http, por lo que es razonable HttpWebRequest a HttpWebRequest y configurar el .Referer (como lo indica en la pregunta):

 HttpWebRequest req = ... req.Referer = "your url"; 

Las respuestas anteriores están bien, pero la esencia del problema es que algunos encabezados se establecen de una manera y otros se configuran de otras maneras. Ver arriba para las listas de ‘encabezado restringido’. Para estos, simplemente los configura como una propiedad. Para otros, realmente agregas el encabezado. Mira aquí.

  request.ContentType = "application/x-www-form-urlencoded"; request.Accept = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + info.clientId + ":" + info.clientSecret); 

Estoy usando solo:

 request.ContentType = "application/json; charset=utf-8" 

Puede simplemente enviar la WebRequest a una HttpWebRequest que se muestra a continuación:

 var request = (HttpWebRequest)WebRequest.Create(myUri); 

y luego, en lugar de tratar de manipular la lista de encabezado, aplíquela directamente en la solicitud de propiedad request.Referer:

 request.Referer = "yourReferer"; 

Estas propiedades están disponibles en el objeto de solicitud.