WCF + REST: ¿Dónde están los datos de solicitud?

Actualmente estoy desarrollando un servicio WCF RESTful. Dentro de la validación de los datos POST, estoy lanzando excepciones si el XML de la solicitud no se ajusta a nuestras reglas comerciales.

El objective es enviar un correo electrónico al personal apropiado si se presenta una solicitud que se considera inválida. Pero, junto con los encabezados de las solicitudes entrantes, el método y el URI, me gustaría enviar también el XML que se publicó.

No he podido encontrar una forma de acceder a esta información. ¿WCF realmente está destruyendo el cuerpo / datos de la solicitud antes de que tenga la oportunidad de acceder o me falta algo?

Su ayuda es apreciada ya que estoy confundido sobre por qué no puedo acceder a los datos de solicitud.

Desafortunadamente, esto no es compatible, teníamos una necesidad similar, y lo hicimos al llamar a los miembros internos con reflexión. Simplemente lo usamos en un controlador de errores (para que podamos volcar la solicitud sin procesar), pero funciona bien. Sin embargo, no lo recomendaría para un sistema que no es de su propiedad y para el que opere (por ejemplo, no envíe este código a un cliente), ya que puede cambiar en cualquier momento con un paquete de servicio o lo que sea.

public static string GetRequestBody() { OperationContext oc = OperationContext.Current; if (oc == null) throw new Exception("No ambient OperationContext."); MessageEncoder encoder = oc.IncomingMessageProperties.Encoder; string contentType = encoder.ContentType; Match match = re.Match(contentType); if (!match.Success) throw new Exception("Failed to extract character set from request content type: " + contentType); string characterSet = match.Groups[1].Value; object bufferedMessage = operationContextType.InvokeMember("request", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField, null, oc, null); //TypeUtility.AssertType(bufferedMessageType, bufferedMessage); object messageData = bufferedMessageType.InvokeMember("MessageData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty, null, bufferedMessage, null); //TypeUtility.AssertType(jsonBufferedMessageDataType, messageData); object buffer = jsonBufferedMessageDataType.InvokeMember("Buffer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty, null, messageData, null); ArraySegment arrayBuffer = (ArraySegment)buffer; Encoding encoding = Encoding.GetEncoding(characterSet); string requestMessage = encoding.GetString(arrayBuffer.Array, arrayBuffer.Offset, arrayBuffer.Count); return requestMessage; } 

Entonces, si declara su contrato algo así como:

 [WebInvoke(Method = "POST", UriTemplate = "create", ResponseFormat=WebMessageFormat.Json)] int CreateItem(Stream streamOfData); 

(puede usar XML en su lugar) El streamOfData debe ser el cuerpo de un HTTP POST. Puedes deserializarlo usando algo como:

  StreamReader reader = new StreamReader(streamId); String res = reader.ReadToEnd(); NameValueCollection coll = HttpUtility.ParseQueryString(res); 

Funciona así para nosotros, al menos. Es posible que desee utilizar un enfoque diferente para obtener la cadena en un XMLDocument o algo así. Esto funciona para nuestras publicaciones JSON. Puede que no sea la solución más elegante, pero está funcionando.

Espero que esto ayude.

Glenn

Prueba esto,

 OperationContext.Current.RequestContext.RequestMessage 

Así es como lo haces sin reflexión:

 using (var reader = OperationContext.Current.RequestContext.RequestMessage.GetReaderAtBodyContents ()) { if (reader.Read ()) return new string (Encoding.ASCII.GetChars (reader.ReadContentAsBase64 ())); return result; } } 

Si el lector es un HttpStreamXmlDictionaryReader (como lo fue en mi caso), la implementación de la clase del método ReadContentAsBase64(byte[] buffer, int index, int count) simplemente pasa estos parámetros al método Stream.Rea d.

Una vez que tengo el byte[] , convierto los bytes a una cadena mediante encoding ASCII. Para una implementación adecuada, puede usar el tipo de contenido y la encoding de los encabezados del mensaje para cada especificación HTTP.

Puede detener HttpApplication.Request.InputStrea m en un HttpModule personalizado del WCF Service, leer la secuencia y volver a establecer su posición en 0 en el controlador de eventos personalizado de HttpModule. Luego, almacénelo en sesión y acceda a él más adelante en el Contrato de OperationContract real.

Por ejemplo:

 public class CustomModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication context) { context.AcquireRequestState +=context_AcquireRequestState; } void context_AcquireRequestState(object sender, EventArgs e) { HttpApplication application = sender as HttpApplication; Stream str = application.Request.InputStream; StreamReader sr = new StreamReader(str); string req = sr.ReadToEnd(); str.Position = 0; application.Session["CurrentRequest"] = req; } }