2012-05-09 10 views
11

Ho un servizio che sto scrivendo che è destinato ad essere utilizzato da più chiamanti, compresi quelli che non sono in grado di ricevere o analizzare XML o JSON.E 'possibile in WCF REST 4 restituire HTML come uno dei formati di risposta

So che è possibile restituire HTML da una risposta del servizio utilizzando lo stream raw, ma quello che vorrei poter fare è restituire uno dei due XML, JSON o HTML a seconda dell'intestazione Accetta-Tipo superata dal cliente.

Potrei farlo con URL separati, ma questo sta sostituendo un sistema che ha già un livello API ben definito.

Esistono esempi di attività disponibili o qualcuno sa quali parti della pipeline dovrebbero essere estese?

(Addendum): conosco già AutomaticFormatSelection e l'ho abilitato, ma vorrei supportare tutti e tre (o più) i formati (HTML, JSON, XML, ecc.) Da un singolo endpoint.

risposta

9

Sì, è possibile. Ma dovrai creare un nuovo formattatore di messaggi che sappia come convertire tra un tipo CLR (la risposta all'operazione) e la pagina HTML. Il modo più semplice per farlo è quello di avvolgere il formattatore originale in un nuovo formattatore, quindi quando si ottiene una risposta per le richieste con Accept: text/html si utilizza la logica, ma per altre richieste si utilizza il formattatore originale.

Il programma di formattazione non ha un modo per accedere alle richieste in arrivo, tuttavia, in modo che possiamo usare un ispettore di messaggi per fornire anche quello.

Il codice seguente mostra una possibile implementazione di tale coppia formatter/inspector.

public class StackOverflow_10519075 
{ 
    [DataContract(Name = "Person", Namespace = "")] 
    public class Person 
    { 
     [DataMember] 
     public string Name { get; set; } 
     [DataMember] 
     public int Age { get; set; } 
    } 
    [ServiceContract] 
    public interface ITest 
    { 
     [WebGet] 
     Person GetPerson(); 
    } 
    public class Service : ITest 
    { 
     public Person GetPerson() 
     { 
      return new Person { Name = "John Doe", Age = 33 }; 
     } 
    } 
    public class MyHtmlAwareFormatter : IDispatchMessageFormatter 
    { 
     IDispatchMessageFormatter original; 
     public MyHtmlAwareFormatter(IDispatchMessageFormatter original) 
     { 
      this.original = original; 
     } 

     public void DeserializeRequest(Message message, object[] parameters) 
     { 
      this.original.DeserializeRequest(message, parameters); 
     } 

     public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) 
     { 
      MyUseHtmlExtension useHtml = OperationContext.Current.Extensions.Find<MyUseHtmlExtension>(); 
      if (useHtml != null && useHtml.UseHtmlResponse) 
      { 
       StringBuilder sb = new StringBuilder(); 
       sb.AppendLine("<html><head><title>Result of " + useHtml.OperationName + "</title></head>"); 
       sb.AppendLine("<body><h1>Result of " + useHtml.OperationName + "</h1>"); 
       sb.AppendLine("<p><b>" + result.GetType().FullName + "</b></p>"); 
       sb.AppendLine("<ul>"); 
       foreach (var prop in result.GetType().GetProperties()) 
       { 
        string line = string.Format("{0}: {1}", prop.Name, prop.GetValue(result, null)); 
        sb.AppendLine("<li>" + line + "</li>"); 
       } 
       sb.AppendLine("</ul></body></html>"); 
       byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString()); 
       Message reply = Message.CreateMessage(messageVersion, null, new RawBodyWriter(bytes)); 
       reply.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw)); 
       HttpResponseMessageProperty httpResp = new HttpResponseMessageProperty(); 
       reply.Properties.Add(HttpResponseMessageProperty.Name, httpResp); 
       httpResp.Headers[HttpResponseHeader.ContentType] = "text/html"; 
       return reply; 
      } 
      else 
      { 
       return original.SerializeReply(messageVersion, parameters, result); 
      } 
     } 

     class RawBodyWriter : BodyWriter 
     { 
      private byte[] bytes; 

      public RawBodyWriter(byte[] bytes) 
       : base(true) 
      { 
       this.bytes = bytes; 
      } 

      protected override void OnWriteBodyContents(XmlDictionaryWriter writer) 
      { 
       writer.WriteStartElement("Binary"); 
       writer.WriteBase64(this.bytes, 0, this.bytes.Length); 
       writer.WriteEndElement(); 
      } 
     } 
    } 
    public class MyHtmlAwareInspector : IDispatchMessageInspector 
    { 
     public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) 
     { 
      HttpRequestMessageProperty httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; 
      string accept = httpRequest.Headers[HttpRequestHeader.Accept]; 
      string operationName = request.Properties[WebHttpDispatchOperationSelector.HttpOperationNamePropertyName] as string; 
      if (accept == "text/html") 
      { 
       OperationContext.Current.Extensions.Add(new MyUseHtmlExtension { UseHtmlResponse = true, OperationName = operationName }); 
      } 

      return null; 
     } 

     public void BeforeSendReply(ref Message reply, object correlationState) 
     { 
     } 
    } 
    class MyUseHtmlExtension : IExtension<OperationContext> 
    { 
     public void Attach(OperationContext owner) { } 
     public void Detach(OperationContext owner) { } 
     public bool UseHtmlResponse { get; set; } 
     public string OperationName { get; set; } 
    } 
    public class MyHtmlAwareEndpointBehavior : IEndpointBehavior 
    { 
     public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
     { 
     } 

     public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
     { 
     } 

     public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
     { 
      endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyHtmlAwareInspector()); 
      foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations) 
      { 
       operation.Formatter = new MyHtmlAwareFormatter(operation.Formatter); 
      } 
     } 

     public void Validate(ServiceEndpoint endpoint) 
     { 
     } 
    } 
    public static void Test() 
    { 
     string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
     ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); 
     var endpoint = host.AddServiceEndpoint(typeof(ITest), new WebHttpBinding(), ""); 
     endpoint.Behaviors.Add(new WebHttpBehavior { AutomaticFormatSelectionEnabled = true }); 
     endpoint.Behaviors.Add(new MyHtmlAwareEndpointBehavior()); 
     host.Open(); 
     Console.WriteLine("Host opened"); 

     WebClient c; 

     c = new WebClient(); 
     c.Headers[HttpRequestHeader.Accept] = "application/json"; 
     Console.WriteLine(c.DownloadString(baseAddress + "/GetPerson")); 
     Console.WriteLine(); 

     c = new WebClient(); 
     c.Headers[HttpRequestHeader.Accept] = "text/xml"; 
     Console.WriteLine(c.DownloadString(baseAddress + "/GetPerson")); 
     Console.WriteLine(); 

     c = new WebClient(); 
     c.Headers[HttpRequestHeader.Accept] = "text/html"; 
     Console.WriteLine(c.DownloadString(baseAddress + "/GetPerson")); 
     Console.WriteLine(); 

     Console.Write("Press ENTER to close the host"); 
     Console.ReadLine(); 
     host.Close(); 
    } 
} 
+0

Wow fantastico @carlosfigueira, grazie. Darò un'occhiata e attuare questo. – GrayWizardx

Problemi correlati