2009-08-17 18 views
16

¿Cómo accedo al cuerpo de solicitud HTTP POST en un servicio WCF REST?Cuerpo de solicitud de acceso en un WCF RESTful Service

Aquí es la definición del servicio:

[ServiceContract] 
public interface ITestService 
{ 
    [OperationContract] 
    [WebInvoke(Method = "POST", UriTemplate = "EntryPoint")] 
    MyData GetData(); 
} 

Aquí está la aplicación:

public MyData GetData() 
{ 
    return new MyData(); 
} 

pensé en usar el siguiente código para acceder a la petición HTTP:

IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest; 

Pero el IncomingWebRequestContext solo da acceso a los encabezados, no al cuerpo.

Gracias.

Respuesta

0

Mis disculpas por la respuesta anterior, estúpidamente asumí que acababa de lanzar WebOperationContext para llegar al OperationContext, desafortunadamente la respuesta real es mucho más fea.

Déjame prefacio con esto, ¡debe haber una manera mejor!

Primero creé mi propio objeto de contexto, que podría adjuntarse al objeto OperationContext existente.

public class TMRequestContext : IExtension<OperationContext> { 

    private OperationContext _Owner; 

     public void Attach(OperationContext owner) { 
      _Owner = owner; 
     } 

    public void Detach(OperationContext owner) { 
      _Owner = null; 
     } 

    public static TMRequestContext Current { 
      get { 
       if (OperationContext.Current != null) { 
        return OperationContext.Current.Extensions.Find<TMRequestContext>(); 
       } else { 
        return null; 
       } 
      } 
     } 
} 

Con el fin de poder acceder a este nuevo objeto de contexto, es necesario agregarlo como una extensión de la actual. Lo hice creando una clase de inspector de mensajes.

public class TMMessageInspector : IDispatchMessageInspector { 

     public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { 

      OperationContext.Current.Extensions.Add(new TMRequestContext()); 
      return null; 
     } 
} 

Para que el inspector de mensajes funcione, debe crear un nuevo "comportamiento". Lo hice usando el siguiente código.

public class TMServerBehavior : IServiceBehavior { 

     public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { 
      //Do nothing 
     } 

     public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) { 

      foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) { 

       foreach (EndpointDispatcher epDisp in chDisp.Endpoints) { 
        epDisp.DispatchRuntime.MessageInspectors.Add(new TMMessageInspector()); 
       } 
      } 

     } 
} 

El comportamiento debe ser capaz de añadir en el archivo de configuración, aunque lo hice mediante la creación de un nuevo huésped y añadiendo el objeto manualmente el comportamiento en el método OnOpening. Terminé usando estas clases para mucho más que simplemente acceder al objeto OperationContext. Los usé para registrar y anular el manejo de errores y el acceso al objeto de solicitud http, etc. Por lo tanto, no es una solución tan ridícula como parece. Casi, pero no del todo!

Realmente no recuerdo por qué no podía acceder directamente a OperationContext.Current. Tengo un leve recuerdo de que siempre estaba vacío y este desagradable proceso fue la única forma en que pude obtener una instancia que en realidad contenía datos válidos.

+0

Hola Darrel, He intentado su sugerencia y encontré algunos problemas. Cuando utilicé su código exacto, obtuve este error (en tiempo de compilación): No se puede convertir el tipo 'System.ServiceModel.Web.WebOperationContext' en 'System.ServiceModel.OperationContext' Y cuando lo cambié a este código: string body = OperationContext.Current.RequestContext.RequestMessage.ToString(); El cuerpo era una cadena vacía en tiempo de ejecución. ¿Alguna idea? Gracias, Uri – urini

10

mejor manera que creo que no implique WebOperationContext

[OperationContract] 
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)] 
MyData GetData(System.IO.Stream pStream); 
+0

El 'BodyStyle' por defecto es' WebMessageBodyStyle.Bare' de todos modos. –

+0

Hmm esto no funcionará si URITemplate incluye '{parameters}' – LamonteCristo

+0

este trabajo incluso si urltemplate tiene parámetros. Para mí, esto funciona para la solicitud de publicación xml sin procesar, pero la solución con OperationContext.Current.RequestContext.RequestMessage.ToString() - no funciona (resultado "... stream ...") – SalientBrain

1

Parece que debido a WCF está diseñado para ser el transporte de protocolo agnóstico, un método de servicio no proporciona acceso a la información específica de HTTP por defecto. Sin embargo, me encontré con un buen artículo que describe el "Modo de compatibilidad ASP.Net" que esencialmente le permite especificar que su servicio está destinado a estar expuesto a través de HTTP.

http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx

Adición de la configuración aspNetCompatibilityEnabled-Web.config, combinado con el atributo AspNetCompatibilityRequirements a las operaciones de servicio deseados, debe hacer el truco. Estoy a punto de probar esto yo mismo.

Haw-Bin

+0

Es cierto pero le quita el alojamiento capacidades del servicio. –

10

Uso

OperationContext.Current.RequestContext.RequestMessage

+5

Te dará el mensaje xml y no el cuerpo POST –

1

Las respuestas anteriores me ayudó a llegado con esta solución. Estoy recibiendo json con pares de nombre/valor. { "P1": 7514, "P2": 3412, "P3": "Joe Smith" ...}

[OperationBehavior(Impersonation = ImpersonationOption.Allowed)] 
    [WebInvoke(Method = "POST", 
     BodyStyle = WebMessageBodyStyle.Bare, 
     RequestFormat = WebMessageFormat.Json 
     )] 

public Stream getJsonRequest() 
    { 

     // Get the raw json POST content. .Net has this in XML string.. 
     string JSONstring = OperationContext.Current.RequestContext.RequestMessage.ToString(); 

     // Parse the XML string into a XML document 
     XmlDocument doc = new XmlDocument(); 
     doc.LoadXml(JSONstring); 

     foreach (XmlNode node in doc.DocumentElement.ChildNodes) 
     { 
       node.Name // has key 
       node.InnerText; // has value 
+0

para la solicitud de publicación xml me da "... transmisión ...", Encoding.UTF8.GetString (OperationContext.Current.RequestContext.RequestMessage.GetBody ()) doesn tampoco funciona Para mí, la solución [Kasthor] funciona o lo siguiente: var inputStream = OperationContext.Current.RequestContext.RequestMessage.GetBody (); \t \t \t var sr = new StreamReader (inputStream, Encoding.UTF8); \t \t \t var str = sr.ReadToEnd(); – SalientBrain

+0

En lugar de iterar sobre los nodos, puede crear un XmlNodeReader desde el documento, luego alimentarlo en DataContractJsonSerializer.ReadObject(), que acepta un XmlReader. ¡De esta manera, los valores que no son cadenas, como números y booleanos, son del tipo correcto! –

6

Lo siento por la tarde respuesta, pero pensé que iba a añadir lo que funciona con los parámetros para obtener UriTemplate el cuerpo de la solicitud.

[ServiceContract] 
public class Service 
{   
    [OperationContract] 
    [WebInvoke(UriTemplate = "{param0}/{param1}", Method = "POST")] 
    public Stream TestPost(string param0, string param1) 
    { 

     string body = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>()); 

     return ...; 
    } 
} 

body se le asigna una cadena a partir de los bytes sin formato del cuerpo del mensaje.