Archivo por meses: agosto 2011

MTOM cómo codificación de ficheros en WCF

MTOM (Mecanismo de optimización de transmisión del mensaje), como su nombre indica, es un mecanismo para transmitir datos binarios grandes con basicHttpBinding. De forma predeterminada, basicHttpBinding envía y recibe los mensajes en XML y este tipo de encoding del mensaje debe ser habilitado en la configuración de nuestro proyecto cómo veremos más adelante.

El objetivo de usar MTOM en vez de otro encoding de mensajes, no es otro que optimizar la transmisión de grandes cargas binarias. Al contrario, esta codificación cuándo se usa para pequeñas cargas añade una sobrecarga innecesaria en nuestro mensaje por lo que debemos saber cuándo debemos usarlo, aunque teniendo en cuenta los documentos que se usan hoy en día y la calidad de las fotografías seguramente lo uséis la gran mayoría de las veces.

Antes de empezar os recomiendo que os bajéis el código de ejemplo y lo vais siguiendo con lo que explicaré. Como veréis dentro de la solución que he creado, hay dos carpetas: Cliente y Servidor. La primera de ellas tendrá un proyecto WinForms que nos servirá para subir/bajar archivos y la segunda el servicio WCF. Lo primero que vamos a ver es un sencillo servicio en WCF que nos permitirá subir y bajar archivos de cualquier tipo. Es facil, la interface IService.cs tendrá dos métodos; uno para subir ficheros con el atributo IsOneWay = true para indicar que este método no va a devolver mensaje de respuesta y otro para obtener el fichero. Además dos clases, PeticionDescarga y Fichero. Esta última implementara la interfaz IDisposable para asegurarnos que al terminar la transmisión podremos vaciar el flujo de datos.

Cómo MTOM transmite el mensaje por SOAP debemos formatear las clases cómo tal, así que la clase Fichero contendrá las propiedades NombreFichero y Peso con el atributo [MessageHeader(MustUnderstand = true)] que nos indica que ambas propiedades pertenecerán a la cabecera SOAP (Header) del mensaje y nos permite especificar si el Header deberá ser analizado y comprendido por el destinatario o no. Sí esta a true, al enviar los datos se espera que el receptor comprenda la cabecera y los tenga en el DataContract. La propiedad FicheroBytes tiene el atributo [MessageBodyMember()] que nos indica que será el cuerpo del mensaje.

Ahora viene lo complicado, ¿Cómo hacemos que nuestro servicio transfiera sus datos a través de la codificación MTOM? Configurando el binding de nuestro servicio. Para ello nos situamos en el archivo Web.config y nos deberá quedar, además de las otras etiquetas, la configuración del servicio así:

<system.serviceModel>
<
bindings>
<
basicHttpBinding>
<
bindingname=MTOMBindingmaxReceivedMessageSize=1048576messageEncoding=MtomtransferMode=Streamed>
</
binding>
</
basicHttpBinding>
</
bindings>
<
services>
<
servicename=ServicebehaviorConfiguration=ServiceBehavior>
<
endpointaddress=“”binding=basicHttpBindingbindingConfiguration=MTOMBindingcontract=IService/>
</
service>
</
services>
<
behaviors>
<
serviceBehaviors>
<
behaviorname=ServiceBehavior>
<
serviceMetadatahttpGetEnabled=true/>
<
serviceDebugincludeExceptionDetailInFaults=true/>
</
behavior>
</
serviceBehaviors>
</
behaviors>
</
system.serviceModel>

Como veís, le indicamos que la codificación del mensaje será con MTOM y a través del transporte http. Las propiedades a destacar y de importacia són maxReceivedMessageSize que nos indica el tamaño máximo del mensaje total expresado en bytes y transferMode que lo estableceremos a Streamed indicando así que no será un transporte almacenado en un búfer si no que será transmitido, es decir, solo deberán ser almacenadas las cabeceras SOAP y el cuerpo del mensaje será transmitido cúando se haga la petición. Cómo le hemos indicado que será transmitido ahora maxBufferSize deberá ser menor o igual que maxReceivedMessageSize ya que a diferencia de este, maxBufferSizeunicamente limita el tamaño máximo de la cabecera SOAP. Para que lo podaís entender mejor, maxBufferSize configura el búfer de WCF que almacena la cabecera del mensaje mientras se procesa el mensaje en el extremo del cliente y no la totalidad del mismo mensaje. Así pues, nuestra cabecera SOAP contendrá el nombre del fichero y el peso tal y cómo hemos configurado la interficie IService.

El servicio no tiene más, lo que haremos ahora es la implementación de la interface y el código de los métodos que no explicaré ya que son muy básicos, aunque podéis preguntar si tenéis cualquier duda por supuesto y además lo más complejo lo he comentado. Algo simple, dentro de Service.cs tendremos implementados los métodos SubirFichero y DescargarFichero.

El método SubirFichero, recibirá una instancia de la clase Fichero con la información del archivo a subir: su nombre, tamaño y el contenido en un stream de bytes. Este método tendrá un FileStream que va a escribir a disco en una ruta concreta el stream de bytes recibido, o sea, el archivo. Y el método DescargarFichero hará todo lo contrario, recibirá una instancia de la clase PeticionDescarga con el nombre del archivo a obtener y va a crear una instancia de la clase Fichero,es decir, el nombre, su longitud y el contenido en un stream devolviendo esta instancia cómo resultado de la llamada al método.

Hasta aquí todo lo que se refiere al servicio, ya lo tenemos terminado y ahora nos toca hacer la parte de cliente. WCF siempre debe de tener almenos un EndPoint así que a esta aplicación cliente le añadiremos una referencia a un servicio, el que hemos hecho, del que deberemos obtener la URL para saber dónde está hospedado. La podremos ver en la barra de direcciones ejecutando el servicio independientemente. Aunque el servicio este configurado, el cliente tiene total libertad para decidir cómo actuar con el servicio cómo por ejemplo en nuestro caso le estamos indicando que únicamente (como servicio) podremos recibir 1Mb y mediante Streamed. El cliente debe responder con acuerdo a nuestras configuraciones pero por ejemplo puede hacer que el tamaño máximo que se envié al servicio sea de 0,5Mb.

Bien, los parametros a configurar del lado cliente van a ser el maxBufferSize, maxReceivedMessageSize y transferMode. Se debe indicar que el tamaño maximo sea igual o menor que el tamaño que permite el servico (>= 1mb), en caso contrario el servicio lanzará una excepción indicando que no soporta un tamaño superior. Con el último parámetro debemos tener cuidado, debe ser transferMode=StreamedResponse” porqué nuestro servicio espera recibir las cabeceras SOAP en un buffer y recibir la transmisión del mensaje al momento del envío. De esta forma evitamos tener un buffer con miles de bytes debido al gran peso del fichero.

Comentar que las carpetas de subida y descarga se crearan dentro de los proyectos correspondientes. La de subida dentro del App_Code del servicio y la de descarga dentro del bin del cliente.Para obtener el código, que esta adjunto, debéis abrir el archivo MTOM.zip y copiar cada una de las subcarpetas que contiene Projects y WebSites dentro de las carpetas de proyecto de Visual Studio 2008 y abrir la solución Ficheros.sln. Está hecho con el Framework 3.5 y c#, como no :)

Espero que no dudéis en preguntar y que os guste el post! Happy code!

MTOM