Jugando con Restlet y Astoria

Tras leer el artículo Como crear aplicaciones en Java que puedan conectarse con .NET me di a la tarea de realizarlo sin mayores problemas (una vez aclarado que la versión de Restlet a utilizar es la 2.0 y agregar la tabla Categories al modelo de Entity Framework).

Mas al tratar de extender el ejemplo proporcionado para realizar el resto de las operaciones CRUD (apoyándome en el Tutorial de Restlet WCF Data Services extension) tuve algunos problemas.

Al principio el problema era la línea en el servicio Astoria:

config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);

Misma que cambie a:

config.SetEntitySetAccessRule("*", EntitySetRights.All);

Con ello logré que al menos me permitiera el Borrado de entidades, pero seguía teniendo problemas para la Actualización y Creación. Este problema no lo tengo en un cliente similar hecho con .NET, donde si puedo realizar todas las operaciones.

Al parecer el problema era que el formato de las peticiones con Restlet no era el correcto ya que el servicio me devolvía un Error 400 (Bad Request). Tras verificar y comparar las peticiones de ambos clientes encuentro varias diferencias, desde el tipo de petición (PUT vs MERGE en el caso de Actualización) así como cabeceras HTTP y namespaces XML faltantes con Restlet, así como en la estructura del XML enviado por el mismo.

No obstante "jugando" con TcpMon para alterar las peticiones, me doy cuenta que con todo y lo anterior el verdadero problema son un par de números agregados antes y después del XML, así como la falta de la cabecera HTTP "Content-Length" con su correspondiente valor exacto.

Lo curioso es que eso es visible incluso en el ejemplo mostrado en el Tutorial de Restlet WCF Data Services extension (sección "Add a new Entity"):

POST /TestAssociationOneToOne.svc/Cafes HTTP/1.1
Host: restlet.cloudapp.net
User-Agent: Noelios-Restlet/2.0snapshot
Accept: */*
Content-Type: application/atom+xml
Transfer-Encoding: chunked
Connection: close

281
<?xml version="1.0" standalone='yes'?>
<entry xmlns="http://www.w3.org/2005/Atom">
   <content type="application/xml"><properties xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<ZipCode xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">12345</ZipCode>
<ID xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">3</ID>
<Name xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">Bar des sports</Name>
<City xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">Paris</City>
<Article xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices"/></properties></content>
</entry>

0

Los "números" (en este caso 280 y 0) -así como espacios en blanco- deben eliminarse y especificar la cabecera Content-Length con el valor adecuado correspondiente para que la operación se lleve a cabo con éxito.

Por ello el problema solo ocurre en aquellas peticiones en donde se tiene que anexar un contenido XML: Crear y Actualizar, pero no en aquellas donde solo hace una petición simple sin contenido. ¿Alguna idea de como sufragarlo?

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

intenta esto.

en tu proyecto de .NET agrega esto en el servicio que estas exponiendo:

config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);

esta configuración permite que el servicio exponga las operaciones. avisame que tal.

Saludos

Jaime Sánchez
http://blogs.msdn.com/jaimesb
twitter @jaimesanchez
jaime.sanchez@microsoft.com

**Estos comentarios son hechos de manera personal y no reprensentan el punto de vista de la empresa que me contrata de ninguna manera.

Imagen de willyxoft

No hay problema con IIS

Jaime, esa línea ya la tenía establecida de esa forma, así que no era por ahí...

Pero el problema ya quedó (entre comillas) "solucionado", el problema se daba porque el servidor web de desarrollo, embebido con VS, es muy quisquilloso y no me aceptaba/procesaba las peticiones tal y como lo señalo en ésta entrada. Pero si publico el servicio en el IIS ya no tengo problema alguno.

Aclaro pues que las peticiones se siguen haciendo con los extraños "números" antes y después del XML (así como la ausencia de la cabecera "Content-Length"), pero aún así el IIS y el servicio los aceptan sin problema, Vaya cosa...

Por lo que de todas formas no estaría mal que algún experto en HTTP me diga si es válido que las peticiones tengan dichos "números" para que en caso contrario hacérselo saber al equipo de Restlet.

Saludos y gracias,
Willy Mejía.

Imagen de ezamudio

Transfer-Encoding: chunked

El asunto está en ese encabezado de Transfer-Encoding: chunked. Ese también se usa por default en Axis por ejemplo y es un problema porque generalmente los web services hechos en .NET no lo saben manejar.

Lo que significa ese encabezado es que el cliente va a estar enviando la petición en trozos (chunks). Lo que hace es enviar primero un número indicando el tamaño del siguiente trozo, y luego lo envía; al final, envía un 0 para indicar que ya terminó la transmisión. Es útil para peticiones o respuestas con un contenido grande, para que el servicio web (ya sea el server o el cliente) puedan ir procesando la información conforme llega, sin tener que esperar a recibir toda la respuesta para empezar a procesar.

En Axis se desactiva la opción de usar el "chunked" de esta forma, suponiendo que tienes un stub de Axis:

AxisStub ws = new AxisStub();
ws._getServiceClient().getOptions().setProperty(HTTPConstants.CHUNKED, false);

Pero si creas un cliente de web service con las herramientas de Java 6 estándar, entonces es un poco más tedioso y necesitas Apache CXF:

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

Client _client = ClientProxy.getClient(serviceStub);
HTTPConduit httpConduit = (HTTPConduit)_client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setAllowChunking(false);
httpConduit.setClient(httpClientPolicy);

Con eso ya se envía todo de un jalón y se usa Content-Length en vez de Transfer-Encoding: chunked, para que .NET no la haga de tos (no sé cómo le hiciste para poder invocar el WS de .NET porque yo hasta con web services de .NET en producción he tenido broncas y tengo que deshabilitar el chunked siempre).

Imagen de willyxoft

Chunked / IIS

Gracias por la info, ya me queda claro el asunto.
http://en.wikipedia.org/wiki/Chunked_transfer_encoding

Por lo que en conclusión el pseudo-problema fue que servidor web de desarrollo de VS (aka Cassini) no entiende/soporta el mentado "chunked".

Y pues no hice absolutamente nada del lado cliente (ni en el servicio como tal), y de hecho el "chunked" sigue funcionando/enviándose igual. La única diferencia fue el servidor web que aloja el servicio. Pasé del Cassini al IIS7. Faltaría ver como se comporta en otras versiones de IIS...

Para habilitarlo en IIS5/IIS6 encontré esto:
http://support.microsoft.com/kb/278998

¿Bajo que escenarios específicos haz tenido problemas con el "chunked"? ¿Solo contra servicios .NET (ASMX o WCF)? ¿Alojados en que versiones de IIS? Para ir probando...

Saludos,
Willy Mejía.

Imagen de ezamudio

No sé

He tenido broncas conectándome a web services hechos en .NET; sé que están hechos en .NET porque estuve preguntando muchas veces e insistiendo o en ocasiones husmeando en la IP destino para ver qué web server era, pero nadie me dio información de qué versión de Windows ni IIS ni .NET ni de qué tipo era el servicio ni nada, así que no sé, pero ya van como 3 veces que tengo esa bronca, la última vez ya ni pregunté, solamente quité lo del chunked en mi cliente y funcionó.

Mejoras de Restlet para el 2.0 RC

Que tal Willy, Restlet va a mejorar su framework, aceptaron los cambios y en la siguiente version deben de arreglar los errores. muchas gracias por el feedback.
http://restlet.tigris.org/issues/show_bug.cgi?id=1030