sábado, 3 de octubre de 2009

BUG Warehouse. CSPS. La cadena vacía '' no es un nombre válido (The empty string ‘’ is not a valid name)

CSPS: Contexto, Situación, Problema, Solución
Bug: Defecto

Contexto
  • Microsoft Visual Studio 2008, NET Framewotk 3.5 SP1
  • Servicio Web ASMX existente expuesto mediante SOAP y HTTPS.
  • Cliente del servicio mediante protocolo SOAP.
Situación
  • Se tiene un servicio Web asmx operativo y conforme con Basic Profile Version 1.1 expuesto a través del protocolo SOAP.
  • Se toma una aplicación cliente cualesquiera (por ejemplo una consola) y se adiciona un referencia de servicio (Service Reference). Las referencias a servicios funcionan tanto con servicios de WCF o bien con servicios heredados tipo asmx. Se hace notar que también podría ser una referencia web (Web Reference) heredada de NET Framework 2.0 (asmx) y se produce el mismo comportamiento.
Problema

Al ejecutar el cliente se obtiene el siguiente mensaje de error:

   1: System.ServiceModel.FaultException was unhandled



   2:  



   3: Message="System.Web.Services.Protocols.SoapException: El servidor no puede procesar la solicitud. ---> System.ArgumentException: La cadena vacía '' no es un nombre válido.\n en System.Xml.XmlTextWriter.ValidateName(String name, Boolean NCName)\n en System.Xml.XmlTextWriter.InternalWriteName(String name, Boolean NCName)\n en System.Xml.XmlTextWriter.WriteQualifiedName(String localName, String ns)\n en System.Web.Services.Protocols.Soap11ServerProtocolHelper.WriteFault(XmlWriter writer, SoapException soapException, HttpStatusCode statusCode)\n en System.Web.Services.Protocols.SoapServerProtocol.WriteException(Exception e, Stream outputStream)\n en System.Web.Services.Protocols.WebServiceHandler.WriteException(Exception e)\n en System.Web.Services.Protocols.WebServiceHandler.Invoke()\n en System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()\n --- Fin del seguimiento de la pila de la excepción interna ---"



   4:  



   5: Source="mscorlib"



   6:  



   7: StackTrace:



   8:  



   9: Server stack trace: 



  10:  



  11: en System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)



  12: ...



  13: Exception rethrown at [0]: 



  14:  



  15: en System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)



  16:  



  17: en System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)



  18: ...



  19: en ....MyApp.MyMethod(UploadCollectionFileRequest request) en C:\...\Reference.cs:línea 357



  20: ...




El error no dice mucho y tampoco contiene información detallada de dónde ha sucedido como para hacer un seguimiento del código fuente. En la línea 19 se muestra un texto de ejemplo del detalle máximo referente al código, donde sólo se observa que hay algún problema con el “request” que se ha creado en nuestra referencia de servicio “Reference.cs”.



Solución


Si observamos la primera parte el problema se refiere a la dificultad de escribir el resultado de una serialización XML de forma correcta, debido a que hay un nombre inválido (vacío).



Al revisar el código fuente del Servicio Web se encontró que el servicio capturaba cualquier excepción y la convertía en una SoapException personalizada con las siguientes características:



try
{
//----------------------------------------------------------
// Custom code that throws an exception...
//----------------------------------------------------------

}
catch (Exception ex)
{
throw new SoapException("Mi descripción personalizada del error.", XmlQualifiedName.Empty, ex);
}
finally
{
// ...


}



Como se observa al preparar la excepción SOAP el parámetro XmlQualifiedName se está pasando con el valor “Empty”, lo que provoca que al intentar serializar la excepción esta no pueda procesarse.



Si bien es cierto que usted podría colocar cualquier nombre calificado, en vez de XmlQualifiedName.Empty y podría evitar este bug referido, por ejemplo, new XmlQualifiedName("Mi nombre para el código de excepción SOAP"), lo recomendable es respetar el estándar para el protocolo, ya que el parámetro del constructor de SoapException que acepta un XmlQualifiedName se llama “code” y se refiere al tipo de código de error de SOAP, por lo tanto el deber ser es, elegir alguno de los códigos disponibles, conocidos como códigos de error SOAP para la versión 1.1 del protocolo SOAP. En nuestro caso así queda la línea de excepción completa:



catch (Exception ex)
{
throw new SoapException("Mi descripción personalizada del error."
, SoapException.ServerFaultCode, ex);
}



Lo anterior resuelve el bug presentado y en su caso permite obtener la excepción subyacente que mostraría el verdadero error que se está produciendo en el servicio web.



---(FIN)---