2009-08-17 13 views
16

Jak uzyskać dostęp do treści żądania HTTP POST w usłudze REST WCF?Ciało żądania dostępu w WCF Usługa RESTful

Oto definicja usługi:

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

Oto realizacja:

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

I choć stosując poniższy kod, żeby uzyskać dostęp do żądania HTTP:

IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest; 

Ale IncomingWebRequestContext daje dostęp tylko do nagłówków, a nie do treści.

Dzięki.

Odpowiedz

0

Przepraszam za poprzednią odpowiedź, Głupio założyłem, że właśnie rzuciłem WebOperationContext, aby uzyskać w OperationContext, niestety prawdziwa odpowiedź jest o wiele bardziej brzydka.

Pozwolę sobie z tym opowiedzieć, musi być lepszy sposób!

Najpierw utworzyłem własny obiekt kontekstu, który można dołączyć do istniejącego obiektu OperationContext.

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; 
       } 
      } 
     } 
} 

Aby móc uzyskać dostęp do tego nowego obiektu kontekstowego, należy dodać go jako rozszerzenie do bieżącego obiektu. Zrobiłem to, tworząc klasę inspektorów wiadomości.

public class TMMessageInspector : IDispatchMessageInspector { 

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

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

Aby inspektor komunikatów zadziałał, musisz utworzyć nowe "zachowanie". Zrobiłem to za pomocą następującego kodu.

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()); 
       } 
      } 

     } 
} 

Zachowanie powinny być w stanie dodać w pliku konfiguracyjnym, choć zrobiłem to poprzez stworzenie nowego gospodarza i dodanie obiektu zachowanie ręcznie w metodzie OnOpening. Skończyło się na użyciu tych klas o wiele więcej niż tylko dostęp do obiektu OperationContext. Użyłem ich do rejestrowania i przesłonięcia obsługi błędów i dostępu do obiektu żądania http, itp. A więc nie jest to aż tak absurdalne rozwiązanie, jak się wydaje. Prawie, ale nie do końca!

Naprawdę nie pamiętam, dlaczego nie mogłem uzyskać dostępu bezpośrednio do OperationContext.Current. Mam mgliste wspomnienie, że zawsze było puste i ten paskudny proces był jedynym sposobem, w jaki mogłem uzyskać instancję, która faktycznie zawiera prawidłowe dane.

+0

Witam Darrel, Wypróbowałem twoją sugestię i wpadłem na kilka problemów. Kiedy użyłem twojego dokładnego kodu, dostałem ten błąd (podczas kompilacji): Nie można przekonwertować typu "System.ServiceModel.Web.WebOperationContext" na "System.ServiceModel.OperationContext" I kiedy zmieniłem go na ten kod: ciąg znaków = OperationContext.Current.RequestContext.RequestMessage.ToString(); Ciało było pustym ciągiem podczas uruchamiania. Jakieś pomysły? Dzięki, Uri – urini

10

Najlepszy sposób myślę, że nie wiąże się WebOperationContext

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

Domyślnie 'BodyStyle' ma wartość" WebMessageBodyStyle.Bare ". –

+0

Hmm to nie zadziała, jeśli URITemplate zawiera '{parameters}' – LamonteCristo

+0

tę pracę, nawet jeśli parametr urltemplate ma parametry. Dla mnie działa to dla żądania xml post surowego, ale rozwiązanie z OperationContext.Current.RequestContext.RequestMessage.ToString() - nie działa (wynik "... stream ...") – SalientBrain

1

Wydaje się, że z powodu WCF jest przeznaczony do transportu protokół-agnostyk, sposób usługa nie zapewnia dostęp do określonych informacji HTTP domyślnie. Jednak właśnie natknąłem się na ładny artykuł opisujący "Tryb zgodności ASP.Net", który zasadniczo pozwala ci określić, że twoja usługa rzeczywiście ma być odsłonięta przez HTTP.

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

Dodanie konfiguracji aspNetCompatibilityEnabled do Web.config połączeniu z atrybutem pożądanych operacji usług AspNetCompatibilityRequirements, powinno wystarczyć. Zaraz spróbuję tego.

Haw-Bin

+0

Prawda, ale odbiera selfhosting możliwości usługi. –

10

Zastosowanie

OperationContext.Current.RequestContext.RequestMessage

+5

To da ci xml wiadomości, a nie POST body –

1

Powyższe odpowiedzi pomógł mi wymyślić tego rozwiązania. Otrzymuję json z parami nazwa/wartość. { "P1": 7514, "P2": 3412, "P3": "Jan Kowalski" ...}

[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

dla żądania xml post daje mi "... stream ...", Encoding.UTF8.GetString (OperationContext.Current.RequestContext.RequestMessage.GetBody ()) doesn też nie działa. Dla mnie działa rozwiązanie [Kasthor] lub następujące: var inputStream = OperationContext.Current.RequestContext.RequestMessage.GetBody (); \t \t \t var sr = nowy StreamReader (inputStream, Encoding.UTF8); \t \t \t var str = sr.ReadToEnd(); – SalientBrain

+0

Zamiast iterować po węzłach, można utworzyć XmlNodeReader z dokumentu, a następnie wprowadzić go do obiektu DataContractJsonSerializer.ReadObject(), który akceptuje XmlReader. W ten sposób wartości, które nie są łańcuchami, takie jak liczby i wartości logiczne, są poprawnego typu! –

6

Przepraszam za opóźnienie w odpowiedzi, ale myślałem, że chciałbym dodać, co działa z parametrami UriTemplate dostać organ wnioskujący.

[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 przypisany jest ciąg z surowych bajtów treści wiadomości.

Powiązane problemy