miércoles, octubre 30, 2013

Manejar excepciones en ASP.Net Web API (Handling Exceptions in ASP.NET Web API)

Cuando en una aplicación ASP.Net Web API se produce una excepción en una función interna (por ejemplo busco un cliente que no existe y no tengo controlada esta situación), si no se captura la excepción se enviará un error HTTP 500 (Internal Server Error) con el mensaje genérico

<Error><Message>An error has occurred.</Message></Error>

Si queremos enviar un mensaje que aporte más información al cliente podemos crear una clase derivada de
ExceptionFilterAttribute, tal como esta

  1. public class MyExceptionFilter:ExceptionFilterAttribute
  2. {
  3. public override void OnException(HttpActionExecutedContext context)
  4. {
  5. HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.InternalServerError)
  6. {
  7. Content = new StringContent("Se ha producido un error en la app."),
  8. ReasonPhrase = "Se ha producido un error en la app."
  9. };  
  10. context.Response = msg;
  11. }
  12. }

Para usarla de forma global (que todos los controllers la usen) podemos añadir la siguiente línea al Application_Start() de Global.asax:

GlobalConfiguration.Configuration.Filters.Add(new WebAPIExceptionsDemo.MyExceptionFilter());

O bien, para usarla para determinados controllers, añadir a estos el atributo creado:

  1. [MyExceptionFilter]
  2. public class CustomerController
  3. {
  4. ...

Esta entrada es un extracto del artículo http://www.codeguru.com/csharp/.net/net_asp/handling-exceptions-in-asp.net-web-api.htm.

Estrategia a seguir para el manejo de excepciones (Exception handling strategy)

En esta entrada copio el post http://stackoverflow.com/questions/14973642/how-using-try-catch-for-exception-handling-is-best-practice tal cual por parecerme muy interesante:

[...]

My exception handling strategy is :
  • To catch all unhandled exceptions by hooking to the Application.ThreadException event, then decide :
    • For a UI application: to pop it to the user with an apology message (winforms)
    • For a Service or a Console application: log it to a file (service or console)
Then I always enclose every piece of code that is run externally in try/catch :
  • All events fired by the Winforms infrastructure (Load, Click, SelectedChanged...)
  • All events fired by third party components
Then I enclose in 'try/catch'
  • All the operations that I know might not work all the time (IO operations, calculations with a potential zero division...). In such a case, I throw a new ApplicationException("custom message", innerException) to keep track of what really happened
Additionally, I try my best to sort exceptions correctly. There are exceptions which:
  • need to be shown to the user immediately
  • require some extra processing to put things together when they happen to avoid cascading problems (ie: put .EndUpdate in the finally section during a TreeView fill)
  • the user does not care, but it is important to know what happened. So I always log them:
    • In the event log
    • or in a .log file on the disk
It is a good practice to design some static methods to handle exceptions in the application top level error handlers.
I also force myself to try to:
  • Remember ALL exceptions are bubbled up to the top level. It is not necessary to put exception handlers everywhere.
  • Reusable or deep called functions does not need to display or log exceptions : they are eigher bubbled up automatically or rethrown with some custom messages in my exception handlers.
So finally :
Bad:
// DON'T DO, THIS IS BAD
try
{
    ...
}
catch 
{
   // only air...
}
Useless:
// DONT'T DO, THIS IS USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}
What I do at the top level:
// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(ex); // Log exception
    // ex.LogAndDisplay(ex); // Log exception, then show it to the user with apologies...
}
What I do in some called functions:
// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module :", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}
There is a lot to do with exception handling (Custom Exceptions) but thoses rules I try to keep in mind are enough for the simple applications I do.


lunes, junio 03, 2013

Herramienta para crear entradas en blogger.com

Para mí la mejor Windows Live Writer.

Consumir ASP.NET Web API desde Excel

En Excel existe la posibilidad de importar datos de una aplicación ASP.NET Web API, lo que nos permite trabajar con ellos y/o analizarlos de forma sencilla. Para ello se debe pulsar la opción Datos / Desde Web e indicar la URL adecuada.

image

Sin embargo, si no se indica lo contrario, Excel recogerá los datos en formato json, lo cual no sirve. Para solventar este problema se puede forzar a que la aplicación Web API retorne xml (aunque eso impedirá que podamos ver formato json en otros clientes, por ejemplo los navegadores, tal como se explica en el post anterior).

Para forzar a la aplicación que siempre envíe formato xml se puede añadir al método Application_Start de Global.asax la siguiente sentencia:

    GlobalConfiguration.Configuration.Formatters.Remove(         GlobalConfiguration.Configuration.Formatters.JsonFormatter     );

Obtener Json en vez de XML (o al revés) en ASP.NET Web API

ASP.NET Web API permite retornar XML ó Json modificando los parámetros adecuados en el Application_Start() del Global.asax. Así por ejemplo:
    GlobalConfiguration.Configuration.Formatters.Remove( GlobalConfiguration.Configuration.Formatters.JsonFormatter );
elimina el formato Json y por lo tanto siempre retorna xml. Otras posibilidades son:
    GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true; //Retorna json!!!???
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.UseDataContractJsonSerializer = true;     
    GlobalConfiguration.Configuration.Formatters.Add(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
     GlobalConfiguration.Configuration.Formatters.Add(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
Si no se cambia esta configuración en el Application_Start(), el retorno será Json ó xml dependiendo del request header 
enviado. Así, si por ejemplo utilizamos el Internet Explorer para probar el web api implementado, el resultado lo obtendremos 
en un fichero en formato json, mientras que si lo probamos con Firefox o Chrome el resultado lo obtendremos en formato 
xml. Esto es porque cada uno de los navegadores envía un request header diferente. 
Lo mismo ocurrirá con 
nuestra aplicación cliente: dependiendo del request header que use, la aplicación servidor web api retornará un formato 
u otro (json ó xml). Así si queremos forzar a que nos retorne json habría que forzar Accept: json/xml en el request header, 
mientras que si nos interesa xml habría que forzar Accept: text/xml ó Accept: application/xml.
Si nos interesa utilizar como cliente un navegador web, habría que hacer algo similar utilizando alguna extensión. Por 
ejemplo, para Chrome podemos utilizar la extensión Header Hacker que nos permite configurar los request headers. Para 
obtener json (que es más legible) habría que añadir una línea para reemplazar:
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
por:
    Accept: json/xml

De forma análoga, para Firefox se puede utilizar la extensión Modify Headers.

jueves, abril 25, 2013

Mostrar entidad relacionada como lista desplegable (combo) en ASP.Net

En un proyecto ASP.Net, supongamos que 2 entidades relacionadas como por ejemplo Cliente y Provincia (relación N a 1). En la ficha de Cliente podemos mostrar la Provincia como una lista desplegable de selección (combo) de la siguiente forma:

1) En la acción Edit (y/o Create) del ClientesController añadir:

      var ctx = new MyProject.Models.MyContext();
      ViewData["ProvinciaId"] = new SelectList(ctx.Provincias, "Id""Name");

...donde Id y Name son campos de la entidad Provincias

OJO: El identificador del ViewData debe llamarse exactamente igual que el campo de la entidad principal que hace referencia a la entidad relacionada.



Si queremose un elemento seleccionado por defecto, podemos hacerlo así:

      var ctx = new MyProject.Models.MyContext();
      var provinciasSelectList = 
            from p in ctx.Provincias
            select new { Value = p.Id, Text = p.Name, Selected = (p.Name == "Málaga") };
      ViewData["ProvinciaId"] = new SelectList(provinciasSelectList , "Value""Text");

 2) En al vista que toque (Clientes/Edit y/o Clientes/Create) añadir:

     Html.DropDownList("ProvinciasList")
 
 
 


miércoles, abril 24, 2013

Sentencia SQL que retorna el número de registros en cada fecha

Imaginemos que tenemos una tabla ArticleIds que contiene un campo CreationDate definido como datetime con valor predeterminado = getDate( ), y queremos averiguar cuántos registros se han creado cada día entre las 9:00 y las 12:00:

Sentencia SQL:

SELECT t.Fecha,  COUNT(*) AS Cuenta FROM (
  SELECT CONVERT(CHAR(10), creationDate , 103) AS Fecha, CONVERT(CHAR(8), creationDate , 114) as Hora
    FROM [AESQL2008].[dbo].ArticleIds
    WHERE CONVERT(CHAR(5), creationDate , 114) > '09:00' AND CONVERT(CHAR(5), creationDate , 114) < '12:00'
) t
GROUP BY t.Fecha
ORDER BY t.Fecha


Resultado:

Fecha           Cuenta
04/17/2013    113
04/18/2013    1050
04/19/2013    8278
04/20/2013    1889
04/22/2013    1936
04/23/2013    1269
04/24/2013    235



Exinten otras maneras de hacer lo mismo (p.e. utilizando DATEADD como se explica aquí) pero ésta quizás es la más simple.