Category Archives: ASP.net

[ASPNET] Manejadores de paquetes en ASP.NET 5

Hola a todos, ASP.NET 5 o también conocido como ASP.NET vNext ofrece nuevas vías para el manejo de paquetes en nuestras aplicaciones Web, anteriormente Nuget era el componente diseñado para gestionar/administrar todos los paquetes que requería nuestro proyecto, y ahora en ASP.NET 5 disponemos de otros dos manejadores de paquetes (NPM y Bower), es decir, ahora tenemos:

  • Nuget: La idea con Nuget es manejar todas las dependencias a paquetes como Entity Framework, MVC,  ASP.NET MVC y en general todos aquellos paquetes que sean assemblies, se verá reflejado en el archivo project.json
  • Bower: Bower está pensado para administrar componentes que son utilizados en el front-end de la aplicación, es decir, tanto frameworks y librerías JavaScript como css. se verá reflejado en el archivo bower.json.
  • npm: npm fue creado para administrar paquetes en Node.js, y en nuestro proyecto se verá reflejado en el archivo package.json.

Ahora, veamos como funciona cada uno, lo primero es crear una nueva aplicación web, en este caso utilizando el template vacío (ASP.NET 5 Empty):

package_managers1

Una vez finalizada la creación del proyecto, nos damos cuenta que la estructura del proyecto es totalmente diferente a sus versiones anteriores (en el siguiente post tocaré este punto) y que el archivo project.json (para los paquetes Nuget) ya está dentro del proyecto:

package_managers2

Ahora, abrimos dicho archivo y vamos a añadir una dependencia a ASPNET MVC, para ello dentro de la sección e iniciamos a teclear el nombre del paquete, afortunadamente disponemos de intellisense tanto para el nombre del paquete como para la versión:

package_managers3

Igualmente, es posible seguir utilizando el entorno gráfico de Nuget (vamos a añadir Entity Framework):

package_managers4

Ahora, project.json se ve:

{
    "webroot": "wwwroot",
    "version": "1.0.0-*",
    "exclude": [
        "wwwroot"
    ],
    "packExclude": [
        "node_modules",
        "bower_components",
        "**.kproj",
        "**.user",
        "**.vspscc"
    ],
    "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta2",
        "Microsoft.AspNet.Mvc": "6.0.0-beta2",
        "EntityFramework": "6.1.2.0"
    },
    "frameworks": {
        "aspnet50": { },
        "aspnetcore50": { }
    }
}

Sigamos con Bower, lo primero es añadir un nuevo elemento al proyecto, en este caso un archivo de configuración de Bower:

package_managers5

Como se mencionó al principio del post, Bower permite administrar los recursos que se necesitan en el front-end como librerías JavaScript y framework css, para el ejemplo añadiremos Bootstrap y AngularJS, al igual que con el paquete anterior disponemos de intellisense:

package_managers6

Ahora, el archivo bower.json se ve como:

{
    "name": "PackageManeger",
    "private": true,
    "dependencies": {
        "bootstrap": "3.3.2",
        "angularjs": "1.3.13"
    },
    "exportsOverride": {
    }
}

Ahora, es necesario Dependecies -> Bower y restaurar los paquetes que se han añadido:

package_managers8

Una vez restaurados los paquetes, se visualiza el paquete instalado y sus dependencias en caso que se necesiten, para este caso jQuery que es una dependencia de Bootstrap:

package_managers9

Y finalmente llegamos a npm, al igual que con Bower, se debe añadir un archivo de configuración, en este caso un archivo de configuración npm:

package_managers7

Sobre el archivo package.json añadimos una dependencia a Grunt, el cual permite ejecutar tareas automáticamente sobre archivos JavaScript (en uno de los siguiente post lo voy a tratar más en detalle), de nuevo y como en los manejadores de paquetes anteriores también disponemos de intellisense:

package_managers10

No olviden luego de referenciar el paquete ir a la carpeta Dependencies -> NPM y dar en restaurar paquetes (Restore Packages):

package_managers11

El archivo package.json debe verse:

{
    "version": "1.0.0",
    "name": "PackageManeger",
    "private": true,
    "devDependencies": {
        "grunt": "0.4.5"
    }
}

Bueno, y hasta aquí llegamos, espero haya quedado un poco más claro los nuevos manejadores de paquetes en ASP.NET 5 y que fin tiene cada uno.

Si te ha gustado no te olvides de compartir!

Saludos!

[ASPNET MVC] Personalizando helpers en ASPNET MVC

Hola, anteriormente vimos como es posible personalizar las pantillas tanto para ASPNET MVC como para Web API, así que hoy vamos a ver como personalizar los helpers en MVC, para que cumplan mejor con lo que nuestra aplicación requiere y revisaremos par opciones para llevar a cabo dicha personalización.

Los helpers son una excelente característica cuando trabajamos con ASPNET MVC los cuales permiten generar un determinado HTML que pueda ser enlazado con una propiedad de una clase por ejemplo, sin embargo en algunas ocasiones puede ser necesario personalizar el HTML que es generado por algún determinado helper o crear uno nuevo.

Para iniciar, creamos un nuevo proyecto Web (para el ejemplo he seleccionado la plantilla en blanco y adicionado únicamente la referencia a MVC) y luego una sencilla clase que será el modelo:

public class Author
{
	public int Id { get; set; }

	public string Name { get; set; }

	public string LastName { get; set; }

	public string Bio { get; set; }

	[DataType("Gender")]
	public string Gender { get; set; }
}

Ahora, creamos un controlador AuthorsController utilizando la plantilla que crea las acciones y las vistas usando Entity Framework, desde ahora solo vamos a trabajar con la vista Create.cshtml.

Los helpers generan el HTML basado en el tipo de dato del campo, por lo tanto vamos a personalizar la plantilla para el tipo cadena (string), para ello creamos una carpeta llamada EditorTemplates en la ruta Views/Shared, y allí agregamos una vista parcial llamada String.cshtml, el código de dicha vista es bastante sencillo:

@model string
@Html.TextBox("",
    ViewData.TemplateInfo.FormattedModelValue,
    htmlAttributes: new { @class = "form-control" })

En la vista anterior el primer parámetro hace referencia al nombre del campo, el segundo al y para no estar repitiendo la clase css asociada en cada helper, acá la dejamos centralizada, entonces pasamos de tener un helper:

@Html.EditorFor(model => model.Name, 
	new { htmlAttributes = new { @class = "form-control" } })

a tener:

@Html.EditorFor(model => model.Name)

El helper ya no tiene la necesidad de referenciar/definir la clase css asociada.

Ahora vamos a ver otra posible forma de cambiar el HTML generado, en el modelo se ha definido una propiedad con el nombre Bio, dicha propiedad puede tener bastantes caracteres, por lo cual necesitamos hacer uso de una caja de texto multilinea, así que primero agregamos una nueva vista parcial con el nombre TextArea.cshtml dentro de EditorTemplates con el siguiente código:

@model string
@Html.TextArea("", 
    ViewData.TemplateInfo.FormattedModelValue.ToString(),
    htmlAttributes: new { @class = "form-control", rows = 10 })

Muy parecido al creado anteriormente, sin embargo hemos añadido la propiedad rows = 10 para que sea más alto el campo, ahora para que el helper EditorFor relacionado a la propiedad Bio haga uso de la plantilla creada, utilizamos una de las sobrecargas de dicho helper la cual permite establecer el nombre de la plantilla a usar, en este caso TextArea:

@Html.EditorFor(model => model.Bio, "TextArea")

Y para finalizar, otra forma para personalizar la plantilla a utilizar es definiendo un tipo de dato diferente en la propiedad, como en el caso de la propiedad Gender que se ha decorado con el atributo DataType(“Gender”) y creando una vista parcial llamada Gender.cshtml, el código de dicha vista parcial es:

@model string
@{
    var options = new List<string>() { "Male", "Female" };
}
@Html.DropDownList("", new SelectList(options), htmlAttributes: new { @class = "form-control" })

La vista anterior simplemente crea un combo para seleccionar el género con dos posibles valores.

Finalmente, la vista se ve correctamente:

customhelpers

Espero el post sea interesante y no te olvides de compartir!

Saludos!

[ASP.NET] Personalización de plantillas en MVC y Web API

Hola a todos, hoy quiero mostrar un pequeño “tip” con el cual es posible personalizar los templates que usamos tanto en ASP.NET MVC y Web API para que se adecuen más a lo que necesitamos.

Por ejemplo, es bastante común que se defina una clase base de la cual hereden los controladores, dicha clase base es la que hereda de Controller para MVC o de ApiController cuando es Web API.

Lo primero es que vamos a crear una clase base llamada BaseController, la cual para el demo solo va a hereder de Controller pero puedes tener objetos que requieras en todos tus controladores:

public abstract class BaseController : Controller {}

Luego de definir la clase, creamos una nueva carpeta llamada CodeTemplates en la cual vamos a tener la pantilla personalizada. Ahora, debemos ir a la ruta de instalación de Visual Studio y allí buscar las plantillas, generalmente la ruta es: C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates, allí por cada plantilla existe una carpeta que contiene la definición tanto C# como para Visual Basic del template, dichos archivos son plantillas T4.

Para el ejemplo vamos a personalizar la plantilla que genera un  MVC Controller vacío, para ello copiamos la carpeta MvcControllerEmpty a la carpeta CodeTemplates que creamos en el punto anterior, como vamos a seguir usando C# podemos eliminar de nuestro proyecto la plantilla que corresponde a Visual Basic, es decir el archivo Controller.vb.t4, la solución se debe ver:

customtemplates

Ahora, para personalizar la plantilla abrimos el archivo T4 que inicialmente luce:

<#@ template language="C#" HostSpecific="True" #>
<#@ output extension="cs" #>
<#@ parameter type="System.String" name="ControllerName" #>
<#@ parameter type="System.String" name="ControllerRootName" #>
<#@ parameter type="System.String" name="Namespace" #>
<#@ parameter type="System.String" name="AreaName" #>
<#
string routePrefix;
if (String.IsNullOrEmpty(AreaName))
{
    routePrefix = ControllerRootName;
}
else
{
    routePrefix = AreaName + "/" + ControllerRootName;
}
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace <#= Namespace #>
{
    public class <#= ControllerName #> : Controller
    {
        // GET: <#= routePrefix #>
        public ActionResult Index()
        {
            return View();
        }
    }
}

Los cambios que vamos a hacer son:

  • Cambiar la clase para que hereden de BaseController
  • Añadir el atributo [Authorize] al controlador
  • A la acción Index añadirle los atributos [HttpGet] y [AllowAnonymous]

Luego de implementar los cambios anteriores la T4 se ve como:

<#@ template language="C#" HostSpecific="True" #>
<#@ output extension="cs" #>
<#@ parameter type="System.String" name="ControllerName" #>
<#@ parameter type="System.String" name="ControllerRootName" #>
<#@ parameter type="System.String" name="Namespace" #>
<#@ parameter type="System.String" name="AreaName" #>
<#
string routePrefix;
if (String.IsNullOrEmpty(AreaName))
{
    routePrefix = ControllerRootName;
}
else
{
    routePrefix = AreaName + "/" + ControllerRootName;
}
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace <#= Namespace #>
{
	[Authorize]
    public class <#= ControllerName #> : BaseController
    {
        [HttpGet]
		[AllowAnonymous]
        public ActionResult Index()
        {
            return View();
        }
    }
}

Para validar que los cambios sean correctos, añadimos un nuevo controlador usando la plantilla  MVC 5 Controller – Empty:

customtemplates2

Luego asignamos un nombre al controlador, una vez finalizado el proceso lo que tenemos es un nuevo controlador que tiene los cambios realizados sobre la plantilla:

[Authorize]
public class HomeController : BaseController
{
	[HttpGet]
	[AllowAnonymous]
	public ActionResult Index()
	{
		return View();
	}
}

Espero el post les sea interesante y no te olvides de compartir!

Saludos!

[ASP.NET MVC] Iniciando con las plantillas ASP.NET Boilerplate

Hola a todos, una de los temas que más discusión tiene cuando se va a iniciar un proyecto Web es el tipo de aplicación que se va a desarrollar así como la arquitectura sobre la cual se va a construir la aplicación, luego de definir esos puntos comenzamos a crear una estructura básica de nuestra solución, donde generalmente creamos algunos proyectos que casi siempre vamos a utilizar, proyectos y/o componentes como la capa de presentación, el acceso a datos, la lógica de negocio, el dominio y algunos elementos transversales como seguridad, logging, etc.

Adicional a lo anterior, usualmente agregamos algunos componentes/herramientas en cada uno de los proyectos creados con los cuales vamos a trabajar ya sea porque son los que más conocemos, los que más nos gustan, el que esta de moda, en fin, podemos tener un sin fin de motivos para usarlos, algunos de esas herramientas pueden incluir un contenedor de de inyección de dependencias, una herramienta de log para los errores, algunas librerías/framework JavaScript entre otros.

Para solucionar el problema anterior, podemos trabajar con algo que se conoce como plantillas ASP.NET Boilerplate, el cual nos ofrece un marco de trabajo inicial que ya viene con varias características comunes necesarias en la mayoría de los proyectos, así entonces tenemos:

En el lado del servidor:

  • ASP.NET MVC
  • ASP.NET Web API
  • Castle Windsor
  • Log4Net
  • AutoMapper
  • ASP.NET Boilerplate

En el lado cliente:

  • Twitter Bootstrap
  • jQuery
  • jQueryUI
  • jQuery.Validation
  • jQuery.blockUI
  • jQuery.Spinjs
  • Moment.js
  • Modernizr
  • ASP.NET Boilerplate

boilerplate3

Para iniciar a trabajar, debemos ir a la página oficial ASP.NET Boilerplate y lo primero será escoger entre una aplicación de tipo SPA con Angularjs o con Durandaljs (depende lo que más te guste) y una clásica aplicación Web, luego viene la elección del ORM, acá es posible escoger entre Entity Framework o NHibernate, y finalmente el nombre del proyecto, una vez que hemos realizado esos tres sencillos pasos seleccionamos Create My Project:

boilerplate1

Una vez descargo el proyecto, al abrirlo desde Visual Studio vamos a encontrar una aplicación construida que contiene 5 proyectos:

boilerplate2

Y listo, ya tenemos un muy buen punto de partida sobre el cual seguir desarrollando nuestra aplicación, por el momento dejo el post hasta acá, sin embargo publicaré otro sobre como seguir trabajando con este template.

Espero les haya gustado y aprovechen está excelente herramienta, saludos.

[ASP.NET Web API] Routing por atributos en Web API 2

Hola a todos, aunque ya hace un buen tiempo que ha salido la versión 2 de Web API, una de las características que más me ha gustado es el nuevo tipo de routing que tenemos disponible conocido como routing por atributos (attribute routing), el cual básicamente permite definir el routing desde nuestros controladores, además que nos ayuda a solucionar problemas cotidianos que teníamos en versiones anteriores.

Partamos de un sencillo controlador como el siguiente:

public class CustomerController : ApiController
{
	readonly List<Customer> customers = new List<Customer>()
	{
		new Customer() { Id = 1, Name = "Walker", LastName = "Sosa" },
		new Customer() { Id = 2, Name = "Reese", LastName = "Todd" },
		new Customer() { Id = 3, Name = "Jason", LastName = "Woodward" },
		new Customer() { Id = 4, Name = "Samuel", LastName = "Cole" },
		new Customer() { Id = 5, Name = "Harding", LastName = "Mcgowan" }
	};

	public IEnumerable<Customer> Get()
	{
		return customers;
	}

	public Customer GetById(int id)
	{
		return customers.Where(c => c.Id == id).FirstOrDefault();
	}

	public Customer GetByName(string name)
	{
		return customers.Where(c => c.Name.ToLower().Contains(name.ToLower())).FirstOrDefault();
	}
}

Y la clase Customer

public class Customer
{
	public int Id { get; set; }

	public string Name { get; set; }

	public string LastName { get; set; }
}

Ahora si probamos el servicio, tenemos un problema si hacemos una petición del tipo <domain>/api/customer/sam:

routing

Para solucionarlo vamos a aprovechar el nuevo tipo de routing por atributos disponible desde Web API 2 y superior, lo primero entonces es habilitarlo, para ello añadimos la línea config.MapHttpAttributeRoutes(); en el archivo que defina nuestras rutas, generalmente en la carpeta App_Start, clase WebApiConfig, por lo tanto la clase ahora se ve:

public static void Register(HttpConfiguration config)
{
	config.MapHttpAttributeRoutes();

	config.Routes.MapHttpRoute(
		name: "DefaultApi",
		routeTemplate: "api/{controller}/{id}",
		defaults: new { id = RouteParameter.Optional }
	);
}

El siguiente paso es decorar nuestro controlador con el atributo RoutePrefixAttribute, en este caso [RoutePrefixAttribute(“api/customertask”)], este atributo permite definir un nombre diferente al nombre del controlador para ser utilizado:

[RoutePrefixAttribute("api/customertask")]
public class CustomerController : ApiController
{
	...
}

Para los métodos GetById y GetByName usamos el atributo Route:

[Route("{id:int}")]
public Customer GetById(int id)
{
	return customers.Where(c => c.Id == id).FirstOrDefault();
}

[Route("{name:alpha}")]
public Customer GetByName(string name)
{
	return customers.Where(c => c.Name.ToLower().Contains(name.ToLower())).FirstOrDefault();
}

Finalmente nuestro controlador quedaría:

[RoutePrefixAttribute("api/customertask")]
public class CustomerController : ApiController
{
	readonly List<Customer> customers = new List<Customer>()
	{
		new Customer() { Id = 1, Name = "Walker", LastName = "Sosa" },
		new Customer() { Id = 2, Name = "Reese", LastName = "Todd" },
		new Customer() { Id = 3, Name = "Jason", LastName = "Woodward" },
		new Customer() { Id = 4, Name = "Samuel", LastName = "Cole" },
		new Customer() { Id = 5, Name = "Harding", LastName = "Mcgowan" }
	};

	[Route("")]
	public IEnumerable<Customer> Get()
	{
		return customers;
	}

	[Route("{id:int}")]
	public Customer GetById(int id)
	{
		return customers.Where(c => c.Id == id).FirstOrDefault();
	}

	[Route("{name:alpha}")]
	public Customer GetByName(string name)
	{
		return customers.Where(c => c.Name.ToLower().Contains(name.ToLower())).FirstOrDefault();
	}
}

Luego de los cambios, las siguientes llamados funcionan sin problema:

  • http://<domain>/api/customertask -> Método Get()
  • http://<domain>/api/customertask/2 -> Método GetById(int id)
  • http://<domain>/api/customertask/sam -> Método GetByName(string name)

Si quieren profundizar en el tema les recomiendo el siguiente link: Attribute Routing in Web API 2

Espero les sea de utilidad, saludos!

[ASP.NET] Subiendo archivos a un blob storage con RadCloudUpload

Hola a todos, actualmente es muy común que en las aplicaciones Web el cargue de archivos se haga a un storage en la nube, y generalmente es necesario crear todo el código para implementar dicha funcionalidad, sin embargo Telerik nos hace la vida más sencilla ya que en su gama de controles ahora disponemos de RadCloudUpload, control que en esencia nos permite realizar la subida de un archivo a un storage en la nube (Azure, Amazon S3 y Telerik Backend Services).

Pues bien, su implementación es bastante sencilla y la vamos a realizarm para cargar archivos a un blob storage de Microsoft Azure, una vez añadimos el control, como es costumbre tenemos algunas opciones para comenzar su parametrización como:

  • Tamaño del archivo
  • Proveedor del storage
  • Extensiones permitidas
  • Posibilidad de subir múltiples archivos
  • Skin

cloudupload1

Como hemos seleccionado en el proveedor Azure, se habilita un wizard para ingresar los datos del storage:

cloudupload3

No olvides agregar por Nuget el paquete Windows Azure Storage:

cloudupload2

Y listo, ya es solo probar, una vista previa del control funcionando:

cloudupload4

Si luego requieren cambiar los datos del storage, todos los datos se han replicado en el web.config:

<telerik.web.ui>
	<radCloudUpload>
	  <storageProviders>
		<add name="Azure" type="Telerik.Web.UI.AzureProvider" 
			 accountKey="681o+9PweN+2k....." 
			 accountName="demomvcsignalr" 
			 blobContainer="democloudupload" 
			 ensureContainer="true" 
			 uncommitedFilesExpirationPeriod="1" 
			 defaultEndpointsProtocol="" />
	  </storageProviders>
	</radCloudUpload>
</telerik.web.ui>

Saludos!

[ASP.NET] Integrando ASP.NET con Telerik ASP.NET AJAX

Hola a todos, muchos sabrán de mi gusto por los controles y herramientas de Telerik, así que quiero comenzar a compartir regularmente post sobre sus productos. En esta ocasión vamos a ver cómo es de sencillo integrar los controles ASP.NET AJAX en una aplicación nueva de tipo Web Forms, tarea que Telerik nos pone bastante fácil (con un wizard :)).

Una vez ya tienes instalados los controles, y te recomiendo utilices el Telerik Control Panel (luego hablamos hablaremos de él), tenemos un template que nos va a llevar paso a paso:

Primero seleccionamos el template Telerik Web Application:

telerik 1

Luego se abre un wizard en donde podemos configurar algunas partes importantes del proyecto, iniciando con el tema a utilizar y si se desea agregar una referencia de los assemblies a la solución:

telerik 2

En el siguiente paso es posible establecer algunas opciones, como por ejemplo si usar CDN entre otras:

telerik 3

Luego si queremos tener soporte para jQuery y templates:

telerik 4

Y finalmente si queremos usar Telerik Data Access, el ORM de Telerik (que la verdad va bastante bien):

telerik 5

Y listo, ya solo queda usar y aprovechar la potencia de los controles!

Saludos!

[ASP.NET Web API] Web API IX – Consumiendo un servicio externo, CORS

Hola a todos, hoy vamos a retomar la serie de post sobre Web API, para hablar específicamente el como podemos consumir dichos servicios pero de un dominio diferente, lo primero que vamos a realizar es desplegar lo que se ha venido trabajando, y lo he hecho ayudandome de Azure y en un par de clicks ya esta listo: http://testwebapi.azurewebsites.net, ahora el siguiente paso es crear un cliente para consumir dicho servicio (que ya todos conocemos), para este caso he replicado lo que hemos venido trabajando, la diferencia es que en el archivo person.js al realizar el llamado al servicio ahora es necesario utilizar el dominio del sitio:

http://testwebapi.azurewebsites.net/api/person/...

Bueno, hasta el momento todo parece listo, entonces si probamos vamos a obtener el siguiente error:

error domain

Revisando el error, podemos deducir que el problema se da porque estamos realizando la petición entre dominios diferentes, y es acá donde iniciamos a hablar de CORS!

CORS que quiere decir algo como intercambio de recursos entre dominios cruzados (Cross-Origin Resource Sharing, si mucho mejor la definición en inglés) lo que hace es definir un modelo para poder acceder a recursos de diferentes dominios, en ese caso tanto el cliente como el servidor digamos que trabajan de la mano usando encabezados HTTP.

Cuando estamos usando el método GET para la petición, en la cabecera del request se envía la propiedad Origin con el dominio desde el cual estamos realizando la petición, luego el servicio Web API válida si el dominio que realiza la petición es permitido, en caso afirmativo en la cabecera de la respuesta en la propiedad Access-Control-Allow-Origin se retorna el mismo dominio que realizo la petición o un * para permitir todos los dominios, si la respuesta no cumple esa condición el browser elimina la respuesta:

request 1

Y ahora la solución, nos vamos al método Get del servicio y en el header de la respuesta le agregamos la propiedad Access-Control-Allow-Origin con el valor *, o bien si se tiene un listado de dominios permitidos allí hacer la validación y retornar el dominio específico en lugar del *, por lo tanto el código quedaría:

public HttpResponseMessage GetPerson()
{
	var data = db.Person.AsEnumerable();

	var httpResponseMessage = Request.CreateResponse&lt;IEnumerable&lt;Person&gt;&gt;(HttpStatusCode.OK, data);
	httpResponseMessage.Headers.Add(&quot;Access-Control-Allow-Origin&quot;,&quot;*&quot;);

	httpResponseMessage.Headers.CacheControl = new CacheControlHeaderValue()
	{ 
		MaxAge = TimeSpan.FromMinutes(1)
	};

	return httpResponseMessage;
}

[ActionName(&quot;getbyid&quot;)]
public HttpResponseMessage GetPerson(Int32 id)
{
	var person = db.Person.Find(id);

	if (person == null)
	{
		var httpResponseMessage = Request.CreateResponse&lt;Person&gt;(HttpStatusCode.NotFound,person);
		httpResponseMessage.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);

		return httpResponseMessage;
	}
	else
	{
		var httpResponseMessage = Request.CreateResponse&lt;Person&gt;(HttpStatusCode.OK, person);
		httpResponseMessage.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);

		return httpResponseMessage;
	}
}

Ahora si probamos de nuevo la petición Get funciona correctamente:

response

Bueno, ya tenemos el GET, ahora vamos a trabajar con los demás verbos Http, por ejemplo si intentamos realizar una petición PUT obtenemos:

put

Revisando la información del request, tenemos dos cosas importantes, la primera es que en Request Method el valor es OPTIONS y la segunda que el Status Code es el 405 (Método no permitido), bueno y ahora? Lo que debemos hacer ahora es leer el encabezado de la petición y hacer algunas pequeñas adiciones como especificar que se permitan los verbos PUT y DELETE, permitir todos los dominios y aceptar en el header el atributo Content-Type, para este caso, vamos a crear un Message Handler con el nombre RequestMethodHandler:

public class RequestMethodHandler : DelegatingHandler
{
	protected override async Task&lt;HttpResponseMessage&gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
	{
		if (request.Headers.Contains(&quot;Origin&quot;) &amp;&amp; request.Method == HttpMethod.Options)
		{
			var response = new HttpResponseMessage(HttpStatusCode.OK);
			response.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
			response.Headers.Add(&quot;Access-Control-Allow-Methods&quot;, &quot;PUT, DELETE&quot;);
			response.Headers.Add(&quot;Access-Control-Allow-Headers&quot;, &quot;Origin, X-Requested-With, Content-Type, Accept&quot;);
			return response;
		}

		return await base.SendAsync(request, cancellationToken);
	}
}

y no olviden llamarlo en WebApiConfig:

config.MessageHandlers.Add(new RequestMethodHandler());

Ahora replicamos los cambios que realizamos en el método GET del controlador para los demás métodos que tenemos allí (POST, UPDATE, DELETE), por lo que ahora el controlador quedaría:

public class PersonController : ApiController
{
	private PersonDBContext db = new PersonDBContext();

	/// &lt;summary&gt;
	/// Get all persons
	/// &lt;/summary&gt;
	public HttpResponseMessage GetPerson()
	{
		var data = db.Person.AsEnumerable();

		var httpResponseMessage = Request.CreateResponse&lt;IEnumerable&lt;Person&gt;&gt;(HttpStatusCode.OK, data);
		httpResponseMessage.Headers.Add(&quot;Access-Control-Allow-Origin&quot;,&quot;*&quot;);

		return httpResponseMessage;
	}
	
	[ActionName(&quot;getbyid&quot;)]
	public HttpResponseMessage GetPerson(Int32 id)
	{
		var person = db.Person.Find(id);

		if (person == null)
		{
			var httpResponseMessage = Request.CreateResponse&lt;Person&gt;(HttpStatusCode.NotFound,person);
			httpResponseMessage.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);

			return httpResponseMessage;
		}
		else
		{
			var httpResponseMessage = Request.CreateResponse&lt;Person&gt;(HttpStatusCode.OK, person);
			httpResponseMessage.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);

			return httpResponseMessage;
		}
	}

	/// &lt;summary&gt;
	/// Get a person by an id
	/// &lt;/summary&gt;
	/// &lt;param name=&quot;id&quot;&gt;&lt;/param&gt;
	/// &lt;returns&gt;&lt;/returns&gt;
	[ActionName(&quot;getbyotherid&quot;)]
	public Person GetPersonByOtherId(Int32 id)
	{
		Person person = db.Person.Find(id);
		if (person == null)
		{
			throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
	}

		return person;
	}

	// PUT api/Person/5
	public HttpResponseMessage PutPerson(Int32 id, Person person)
	{
		HttpResponseMessage response;

		if (!ModelState.IsValid)
		{
			response = Request.CreateResponse(HttpStatusCode.BadRequest, ModelState);
		}
		else if (id != person.Id)
		{
			response = Request.CreateResponse(HttpStatusCode.BadRequest);
		}
		else 
		{
			db.Entry(person).State = EntityState.Modified;

			try
			{
				db.SaveChanges();
				response = Request.CreateResponse(HttpStatusCode.OK);
			}
			catch (DbUpdateConcurrencyException ex)
			{
				response = Request.CreateResponse(HttpStatusCode.NotFound, ex);
			}
		}
		
		response.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
		return response;
	}

	// POST api/Person
	public HttpResponseMessage PostPerson(Person person)
	{
		if (ModelState.IsValid)
		{
			db.Person.Add(person);
			db.SaveChanges();

			HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, person);
			response.Headers.Location = new Uri(Url.Link(&quot;DefaultApi&quot;, new { id = person.Id }));
			response.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
			return response;
		}
		else
		{
			return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
		}
	}

	// DELETE api/Person/5
	public HttpResponseMessage DeletePerson(Int32 id)
	{
		Person person = db.Person.Find(id);
		HttpResponseMessage response;

		if (person == null)
		{
			response = Request.CreateResponse(HttpStatusCode.NotFound);
		}
		else
		{
			db.Person.Remove(person);

			try
			{
				db.SaveChanges();
				response = Request.CreateResponse(HttpStatusCode.OK, person);
			}
			catch (DbUpdateConcurrencyException ex)
			{
				response = Request.CreateResponse(HttpStatusCode.NotFound);
				
			}
		}

		response.Headers.Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
		return response;
	}

	protected override void Dispose(bool disposing)
	{
		db.Dispose();
		base.Dispose(disposing);
	}
}

y si probamos de nuevo, podemos ver que ahora si todo nos funciona correctamente.

Espero este post les sirva bastante, saludos!

Descarga el ejemplo!

[ASP.NET Web API] Web API VIII – Trabajando con los ActionName

Hola a todos, volviendo con la serie sobre ASP.NET Web API, en esta oportunidad quiero mostrarles como podemos personalizar el nombre de las acciones y además poder tener dos o más métodos que trabajen con el mismo verbo HTTP y una misma firma.

Para personalizar el nombre de las acciones, es necesario decorar cada acción con el atributo ActionName y especificar el nombre que deseamos utilizar:

[ActionName(&quot;nombre_de_la_acción&quot;)]

Para nuestro ejemplo vamos a crear una nueva acción con el nombre GetPersonByOtherId(Int32 id), dicha acción obedece al verbo Http Get y tiene la misma firma que la acción GetPerson, adicionalmente decoramos las acciones con el atributo ActionName y le asignamos un nombre (si en este caso la lógica de cada acción es la misma):

[ActionName(&quot;getbyid&quot;)]
public Person GetPerson(Int32 id)
{
	Person person = db.Person.Find(id);
	if (person == null)
	{
	   throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
	}

	return person;
}

[ActionName(&quot;getbyotherid&quot;)]
public Person GetPersonByOtherId(Int32 id)
{
	Person person = db.Person.Find(id);
	if (person == null)
	{
		throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
	}

	return person;
}

Adicionalmente agregamos un nuevo elemento en nuestro HTML:

&lt;input id=&quot;btnSearch2&quot; type=&quot;button&quot; value=&quot;Search by Other Id&quot; data-bind=&quot;click:getPersonByOtherId&quot; /&gt;

Ahora modificamos el ViewModel y agregamos la función getPersonById, así como en la función getPersonById hacemos un pequeño cambio a la url que se esta llamando, en este caso agregando el nombre de la acción:

...
self.getPersonById= function () {
	var url = '/api/person/getbyid/' + self.id();
	$.getJSON(url)
		.done(function (data) {
			self.name(data.Name);
			self.lastname(data.LastName);
			self.twitter(data.Twitter);
		})
		.fail(function (erro) {
			self.clearForm();
	});
},

self.getPersonByOtherId = function () {
	var url = '/api/person/getbyotherid/' + self.id();
	$.getJSON(url)
		.done(function (data) {
			self.name(data.Name);
			self.lastname(data.LastName);
			self.twitter(data.Twitter);
		})
		.fail(function (erro) {
			self.clearForm();
		});
},
...

Luego es necesario agregar una nueva ruta en la tabla de routing, en este caso en la clase WebApiConfig:

config.Routes.MapHttpRoute(
	name: &quot;ApiByOtherId&quot;,
	routeTemplate: &quot;api/{controller}/{action}/{id}&quot;,
	defaults: new { id = RouteParameter.Optional }
);

Y ahora si ejecutamos y probamos podemos ver como en efecto los ActionName funcionan correctamente:

ActionName

Espero el post les haya gustado, hasta el próximo!

Descarga el ejemplo!

[ASP.NET Web API] Web API VII – Message Handlers

Hola a todos, luego una pequeña ausencia, retomo el blog con un nuevo artículo sobre Web API, en este caso tratando el tema de los Message Handlers, un Message Handler se ejecuta antes que cualquier action filter además que son ejecutados para todos las acciones de los controladores, por lo anterior, un Message Handler es ideal para tener lógica centralizada que se deba ejecutar en cada request.

Como parte informativa, el primer Message Handler que se ejecuta es el HttpServer, y luego si se ejecutarán los que nosotros definamos.

Para crear un Message Handler personalizado, se debe crear una clase que herede de DelegatingHandler y sobrescribir el método SendAsync, generalmente los Message Handler son utilizados para temas como autenticación y autorización.

En nuestro caso, vamos a implementar un Message Handler que valide el dominio desde el cual están realizando la petición, para autorizarla o no, lo primero es crear una nueva clase a la que llamaremos ValidationHandler y como ya se comento dicha clase heredara de DelegatingHandler:

public class ValidationHandler : DelegatingHandler
{
	protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
	{
		var domain = &quot;localhost&quot;;
		if (!string.Equals(request.RequestUri.Host, domain, StringComparison.OrdinalIgnoreCase))
			return request.CreateResponse(HttpStatusCode.Unauthorized);

		var response = await base.SendAsync(request, cancellationToken);
		return response;
	}
}

Allí, accedemos al valor del host que realiza la petición por medio del objeto request que es del tipo HttpRequestMessage, en caso que el host no sea válido se crea al vuelo la respuesta a la petición y se retorna un código 401 que indica no autorizado (HttpStatusCode.Unauthorized).

Finalmente para que el Message Handler entre en ejecución, se debe relacionar en el Application_Start, y recuerden que tenemos una clase llamada WebApiConfig donde se tiene configurado el routing para Web API, por lo tanto es un buen lugar para relacionarlo, recuerda tener presente el orden en que se añaden, ya que en ese mismo orden serán ejecutados:

public static void Register(HttpConfiguration config)
{
	...
	config.MessageHandlers.Add(new ValidationHandler());
}

no autorizado

Espero les haya gustado el post, ya nos seguiremos viendo por acá con otras entregas sobre Web API.

Descarga el ejemplo!

1 2 3 10