Marc Rubiño

ASP.NET, C#, AJAX.NET, JavaScript, etc.

May 2010 - Artículos

Publicar Servicio WCF en un servidor compartido

Siguiendo con las ayuditas a mis rivales en el concurso Es la hora de los Puños , voy a exponer un error común que nos podemos encontrar al intentar publicar el servicio WCF en un servidor compartido, una configuración muy habitual en nuestros hostings.

El error:

Esta colección ya contiene una dirección con el esquema http. Sólo puede haber una dirección por esquema en esta colección.

image

El Problema:

El servidor web IIS solo permite una sola dirección base por esquema (HTTP) y eso para un entorno compartido es un verdadero problema, esto ya está solucionado en IIS 7.0 y framework 4.0 con el atributo multipleSiteBindingsEnabled en el elemento serviceHostingEnvironment.

Posible solución para versiones anteriores:

Para poder hospedar un servicio WCF con un extremo web en un servidor IIS hay que especificar  la clase WebServiceHostFactory en el archivo .svc

<%@ ServiceHost Language="C#" 
Debug="true" 
Service="KillBits.Fight" 
CodeBehind="Fight.svc.cs"
Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>

O en el fichero de configuración estableciendo el binding webHttpBinding

<endpoint address="http://www.lonetcamp.com" 

   binding="webHttpBinding"  contract="KillBits.IFight">

Podemos crear nuestro propio HostFactory heredando de WebServiceHostFactory o WebScriptServiceFactory dependiendo si queremos facilitar el acceso desde script de cliente o solo ASP.NET.

public class CustomHostFactory : WebScriptServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, 
             Uri[] baseAddresses)
    {
        Uri[] newUri;
        if (baseAddresses.Length > 1)
            newUri = new Uri[] { 
             new Uri("http://www.lonetcamp.com/KillBits/Fight.svc")};
        else
        {
            newUri = new Uri[] { baseAddresses[0]};
        }
         return base.CreateServiceHost(serviceType, newUri);
    }
}

Este ejemplo detecta si existe más de una baseAddresss y si es así utiliza el creado especialmente para mi servicio y si solo hay uno utiliza ese “ejemplo mejorable”.

lo único que tenemos que hacer es especificar que el servicio utilice nuestro factoy en vez de el por defecto para que funcione perfectamente la publicación.

<%@ ServiceHost Language="C#" 
Debug="true" 
Service="KillBits.Fight" 
CodeBehind="Fight.svc.cs"
Factory="KillBits.CustomHostFactory" %>
¿ Servicios REST con diferente serialización Json ?

Resulta que estos días he estado jugando un poco con WCF y serialización JSON para participar en el nuevo concurso de MSDN http://www.lahoradelospunos.com

En resumen lo que hay que hacer es un Servicio REST accesible desde peticiones HTTP GET que devuelva la secuencia de movimientos conforme el siguiente esquema.

{ "Alias": "My_alias", "Moves": "A1, A2, B1, A1, ME, A1, A2, B1, B2, B1", "Enemy": "Enemy_name" }

Pero el problema viene cuando genero la página para acceder manualmente al servicio desde una página web propia y pretendo hacerlo compatible con las llamadas de cliente MS AJAX.

WCF accesible desde ASP.NET AJAX:

Para hacer accesible el servicio y poderlo consumir desde ASP.NET AJAX solo hay que especificar el parámetro Factory de la directiva @ServiceHost en el archivo .svc como WebScriptServiceHostFactory.

image

Desde ese momento la salida de mi objeto JSon tiene el siguiente formato:

{"d":{"__type":"Ataque:#KillBits","Alias":"Marckys ","Enemy":"Cliente","Moves":"A2,A2,B1,A2,A2,A2,ME,A1,B1,A2"}}

UPSSS!!! no tiene el mismo formato que me piden en el concurso.

Para hacer que el formato cuadre exactamente con las especificaciones del concurso solo tenemos que hacer una cosa. NO hacer compatible el servicio con MS AJAX, solamente con ASP.NET.

WCF accesible desde ASP.NET:

Hay que utilizar WebServiceHostFactory como Factory y no WebScriptServiceHostFactory

image

Formato correcto !!!

{"Alias":"Marckys ","Enemy":"Cliente descontento ","Moves":"A1,A1,ME,A1,A1,A1,A2,A2,A2,A2"}

¿Porqué esta diferencia?

Por cuestiones de seguridad desde ASP.NET 3.5 la serialización JSON que tiene que ser consumida desde el cliente “MS AJAX”, viene envuelta en el atributo d. De esta manera se soluciona una conocida vulnerabilidad “javascript hijacking” que permite la ejecución directa de Script en el constructor de arrays de objetos.

Evitando un ataque de este estilo:

["Marckys", alert("Ataque XSS")]

Cuidado al consumir los datos:

Con estas modificaciones en la serialización, tenéis que tener cuidado al obtener el objeto porque no es lo mismo obtener la información con una respuesta u otra.

No es lo mismo esperar el objeto d:

var alias = resultado.d.Alias;

Que utilizarlo directamente:

var alias = resultado.Alias;

Esto puede dar errores al actualizar aplicaciones a las nuevas versiones.

Espero que os haya podido ayudar esta información.