2012-05-09 10 views
11

Tengo un servicio que estoy escribiendo que está destinado a ser utilizado por varios llamadores, incluidos los que son incapaces de recibir o analizar XML o JSON.¿Es posible en WCF REST 4 para devolver HTML como uno de los formatos de respuesta

Sé que es posible devolver el HTML de una respuesta de servicio utilizando la secuencia sin formato, pero lo que me gustaría hacer es devolver uno de XML, JSON o HTML, dependiendo del encabezado Aceptado-Tipo pasado por el cliente

Podría hacerlo con URL separadas, pero esto está reemplazando a un sistema que ya tiene una capa API bien definida.

¿Hay algún ejemplo de hacer esto disponible, o alguien sabe qué partes de la tubería necesitarían ser extendidas?

(Adición): Ya sé sobre AutomaticFormatSelection y lo tengo habilitado, pero me gustaría admitir los tres (o más) formatos (HTML, JSON, XML, etc.) desde un único punto final.

Respuesta

9

Sí, es posible. Pero necesitará crear un nuevo formateador de mensaje que sepa cómo convertir entre un tipo CLR (la respuesta de operación) y la página HTML. La forma más sencilla de hacerlo es envolviendo el formateador original en un formateador nuevo, de modo que cuando obtiene una respuesta para las solicitudes con Accept: text/html utiliza su lógica, pero para otras solicitudes utiliza el formateador original.

El formateador no tiene una forma de acceder a las solicitudes entrantes, por lo que podemos usar un inspector de mensajes para proporcionarlo también.

El siguiente código muestra una posible implementación de dicho par de formateador/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 awesome @carlosfigueira, gracias. Voy a echar un vistazo e implementar esto. – GrayWizardx

Cuestiones relacionadas