Compartir a través de


Desarrollando Aplicaciones en Facebook con ASP.NET MVC, Entity Framework y Facebook SDK (Javascript y .NET) (es-ES)

El día de hoy aprenderán como desarrollar el flujo de cualquier aplicación que vayamos a realizar en Facebook haciendo uso de las excelentes herramientas que tenemos a la mano. La idea es aprovechar el siguiente ejemplo para que tengamos una plantilla base para nuestros futuros proyectos. La plantilla se encuentra en GitHub, el enlace esta al final del artículo.

Diapositiva de la Charla

https://cdn3.iconfinder.com/data/icons/tango-icon-library/48/x-office-presentation-128.png

Creando una aplicación en Facebook

Para iniciar nos dirigimos a facebook developers, en la sección de aplicaciones.

http://www.nicholls.co/blog/image.axd?picture=image_thumb_151.png

Procedemos a dar Click en “Crear una nueva aplicación”, les recomiendo que siempre tengan un aplicación en Facebook para depurar nuestros desarrollos con nuestro Visual Studio debido a que una aplicación solo puede tener configurado un dominio, por lo cual si nuestro desarrollo ya esta en producción y tenemos errores o vamos a realizar alguna modificación realicemos los cambios pertinentes de manera local con la aplicación para depurar y cuando ya estén listos subirlos a nuestro Servidor.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_115.pngIngresamos nuestros datos correspondientes, generalmente desarrollamos aplicaciones que serán accedidas desde nuestras Fan Pages por lo cual en este caso seleccioné esa categoría.

Antes de continuar configurando nuestra aplicación en Facebook procedemos a crear un nuevo proyecto en ASP.NET MVC.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image16_thumb.png

Seleccionamos la plantilla de Facebook que viene por defecto en nuestro Visual Studio, la cuál modificaremos y ya les explicaré el porque.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_117.pngRápidamente me voy a adelantar y configuraré la plantilla que viene por defecto, podemos observar que en un flujo idóneo la aplicación funcionará muy bien.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image28_thumb.png

Pero a mi como desarrollador me interesa probar todas las acciones que pueda tomar un usuario y que mi aplicación siempre tenga el control de estas, del flujo que se pueda invocar.

Entonces que pasaría si no acepto la aplicación?

http://jdnichollsc.azurewebsites.net/image.axd?picture=image32_thumb.png

Upps me llevaría al Centro de Aplicaciones, lo cual no es lo que yo quiero, porque si mi aplicación se esta abriendo desde una Fan Page no quiero que se salga, en vez de eso quiero que hasta que no instale la aplicación no prosiga para ninguna parte.

Y que pasaría si no he iniciado sesión?

http://jdnichollsc.azurewebsites.net/image.axd?picture=image39_thumb.png

Pero a mi me gustaría que se viera mi aplicación, como la vista que aparece cuando un usuario no la ha instalado.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_130.png

Entonces procederemos a realizar unos cambios a esta plantilla, además de configurar un flujo genérico que tendrán nuestras aplicaciones.

Creando nuestra Plantilla

Entonces cual es el flujo deseado? Yo lo definiría de la siguiente manera.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_121.png

Para realizar esto procedemos a limpiar nuestra plantilla eliminando de nuestro proyecto lo siguiente que tengo seleccionado. Además dejamos al Index completamente vacío.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_122.png

Nosotros realizaremos nuestro propio Modelo por lo cual no necesitamos de todo esto y lo demás lo controlaremos desde el cliente para evitarle al Servidor trabajo extra mediante el Javascript SDK. No se si van a emplear WebServices Rest con WebAPI en su aplicación, al menos yo en este tipo de proyectos cuando voy a realizar un Post con AJAX y no voy a redirigir la aplicación a otra página, prefiero utilizar Acciones en mi Controlador que devuelvan un JsonResult. Como expliqué en el diseño del flujo de nuestras aplicaciones nuestra página por defecto será Install, por lo cuál nuestra regla de enrutamiento quedará configurada de la siguiente manera.

http://www.nicholls.co/blog/image.axd?picture=image_thumb_153.png

Procedemos a dirigirnos a nuestro _Layout.cshtml que esta ubicado en la carpeta Shared, este es para los que iniciamos con ASP.NET WebForms como nuestro MasterPage, el cual en pocas palabras es el archivo que tendrá la estructura que compartirán todas las páginas que nosotros deseemos, útil cuando se tiene un mismo diseño en diferentes páginas y queremos reutilizar código. Por lo cuál nuestro limpio Layout (el cuál modificarás más adelante) se verá de la siguiente manera.

_Layout.cshtml

  1. <!DOCTYPEhtml>
  2. <htmllang="es">
  3. <head>
  4.     <metacharset="utf-8"/>
  5.     <metahttp-equiv="X-UA-Compatible"content="IE=edge,chrome=1">
  6.     <metaname="viewport"content="width=device-width"/>
  7.     <title>@ViewBag.Title</title>
  8.     @Styles.Render("~/blog/Content/css")
  9.     @Scripts.Render("~/blog/bundles/modernizr")
  10.     @RenderSection("head", required: false)
  11. </head>
  12. <body>
  13.     <divid="fb-root"></div>
  14.     @RenderBody()
  15.     @Scripts.Render("~/blog/bundles/jquery")
  16.     <scriptsrc="//connect.facebook.net/en_US/all.js"type="text/javascript"></script>
  17.     <scripttype="text/javascript">
  18.         FB.init({
  19.             appId: '@Microsoft.AspNet.Mvc.Facebook.GlobalFacebookConfiguration.Configuration.AppId', // App ID
  20.             status: true, // check login status
  21.             cookie: true, // enable cookies to allow the server to access the session
  22.               xfbml: true// parse XFBML
  23.         });
  24.     </script>
  25.     @RenderSection("scripts", required: false)
  26. </body>
  27. </html>

Tenemos 3 espacios importantes, donde:

  • head: Cada página puede incluir hojas de estilo diferentes (Por ejemplo si están empleando jQuery Plugins).
  • Body: Estará ubicado el contenido específico de cada página.
  • scripts: Cada página puede emplear archivos Javascript externos, tanto de manera local como desde un CDN.

Procedemos a crear en nuestro Modelo la Clase User en donde definiremos los datos que vamos a capturar de los usuarios en nuestras aplicaciones mediante el Registro.

User.cs

  1. using System.ComponentModel.DataAnnotations;
  2.  
  3. namespace WebApp.Models
  4. {
  5.     publicclassUser
  6.     {
  7.         [Key]
  8.         publicstring IdUser { get; set; }
  9.  
  10.         [Required(ErrorMessage = "Debe ingresar su nombre.")]
  11.         [StringLength(250)]
  12.         publicstring FirstName { get; set; }
  13.  
  14.         [Required(ErrorMessage = "Debe ingresar sus apellidos.")]
  15.         [StringLength(250)]
  16.         publicstring LastName { get; set; }
  17.  
  18.         [Required(ErrorMessage = "Debe ingresar un email vlido.")]
  19.         [StringLength(250)]
  20.         publicstring Email { get; set; }
  21.  
  22.         [Required(ErrorMessage = "Debe ingresar su cdula.")]
  23.         [StringLength(50)]
  24.         publicstring Document { get; set; }
  25.     }
  26. }

Agregamos Entity Framework a nuestro Proyecto desde el Manage NuGet Packages, creamos nuestro DataContext, configuramos la conexión con la Base de Datos y realizamos nuestra Migración.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_126.png 

 

DataContext.cs

  1. using System.Data.Entity;
  2.  
  3. namespace WebApp.Models
  4. {
  5.     publicclassDataContext : DbContext
  6.     {
  7.         public DataContext()
  8.             : base("DefaultConnection")
  9.         {
  10.  
  11.         }
  12.  
  13.         #region Tables
  14.  
  15.         publicDbSet<User> Users { get; set; }
  16.  
  17.         #endregion
  18.  
  19.         #region Stored Procedures
  20.  
  21.         #endregion
  22.     }
  23. }

 

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_127.png

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_128.png

Ahora desarrollaremos el Controlador que se encargará del flujo de nuestra aplicación, este se compondrá de tres regiones.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_129.png

  • En la región Main tendremos lo siguiente
  1. private Facebook.FacebookClient FBClient(string accessToken)
  2. {
  3.     returnnew Facebook.FacebookClient(accessToken)
  4.     {
  5.         AppId = Microsoft.AspNet.Mvc.Facebook.GlobalFacebookConfiguration.Configuration.AppId,
  6.         AppSecret = Microsoft.AspNet.Mvc.Facebook.GlobalFacebookConfiguration.Configuration.AppSecret
  7.     };
  8. }

En donde configuramos el Cliente de Facebook con los datos de nuestra aplicación.

  1. ///<summary>
  2. ///Controla el flujo de la aplicacion segun el estado del usuario
  3. ///</summary>
  4. ///<param name="user">Obtener los datos del usuario de la base de datos.</param>
  5. ///<param name="actionName">Nombre de la accion que dispara el evento.</param>
  6. privatestring AppFacebookControl(refUser user, string actionName)
  7. {
  8.     string response = null; //Mostrar la vista del controlador actual por defecto
  9.     string accessToken = (string)Session["AccessToken"];
  10.     if (!string.IsNullOrWhiteSpace(accessToken))
  11.     {
  12.         try
  13.         {
  14.             dynamic me = FBClient(accessToken).Get("me"); //Obtener datos de un Usuario desde Facebook.
  15.             string id = me != null ? me.id : null;//Obtener Id de Facebook, de lo contrario retorna null.
  16.             if (!string.IsNullOrWhiteSpace(id)) //Comprobar el id del usuario.
  17.             {
  18.                 using (var context = new Models.DataContext())
  19.                 {
  20.                     user = context.Users.SingleOrDefault(x => x.IdUser == id); //Obtener al Usuario de la Base de Datos.
  21.                 }
  22.                 if (user != null)
  23.                 {
  24.                     if (actionName != "Ok")
  25.                     {
  26.                         response = "Index"; //El usuario instalo la aplicacion y realizo el registro exitosamente.
  27.                     }
  28.                 }
  29.                 else
  30.                 {
  31.                     if (actionName != "Register")
  32.                     {
  33.                         response = "Register"; //El usuario instalo la aplicacion pero no ha registrado sus datos.
  34.                     }
  35.                 }
  36.                 return response;
  37.             }
  38.         }
  39.         catch
  40.         {
  41.             Session.Remove("AccessToken"); //El token ha expirado
  42.         }
  43.     }
  44.     if (actionName != "Install")
  45.     {
  46.         response = "Install"; //El usuario no ha instalado la aplicacion o el token ha expirado.
  47.     }
  48.     return response;
  49. }

Como podemos observar según el estado del Usuario en Facebook y en nuestra Base de Datos se determinará el comportamiento de nuestra aplicación, para esto analizamos si existe un accesstoken válido para obtener los datos del Usuario en Facebook, de lo contrario se redireccionará al Install, si es válido observamos si el usuario existe en nuestra Base de Datos, si no existe lo redireccionaremos al Register de lo contrario al Inicio de nuestra Aplicación por defecto (Index), los condicionales nos permiten validar que nuestra aplicación no redireccione si ya nos encontramos en la acción de nuestro Controlador correspondiente.  Para terminar nuestra región Main tendremos las Vistas principales definidas anteriormente, además de la Acción de registrar a un Usuario.

 

  1. ///<summary>
  2. ///El usuario debe instalar la aplicacion, se encarga de controlar si el usuario esta logueado.
  3. ///</summary>
  4. publicActionResult Install(string accessToken)
  5. {
  6.     if (!string.IsNullOrWhiteSpace(accessToken))
  7.     {
  8.         Session["AccessToken"] = accessToken;
  9.     }
  10.     User user = null;
  11.     string action = AppFacebookControl(ref user, "Install");
  12.     if (action != null)
  13.     {
  14.         return RedirectToAction(action);
  15.     }
  16.     return View();
  17. }
  18.  
  19. ///<summary>
  20. ///El usuario debe registrar sus datos en la base de datos.
  21. ///</summary>
  22. publicActionResult Register()
  23. {
  24.     User user = null;
  25.     string action = AppFacebookControl(ref user, "Register");
  26.     if (action != null)
  27.     {
  28.         return RedirectToAction(action);
  29.     }
  30.     return View();
  31. }
  32.  
  33. ///<summary>
  34. ///Es el Inicio de la aplicacion, despues de que el usuario instalo la aplicacion y registro sus datos.
  35. ///</summary>
  36. publicActionResult Index()
  37. {
  38.     User user = null;
  39.     string action = AppFacebookControl(ref user, "Ok");
  40.     if (action != null)
  41.     {
  42.         return RedirectToAction(action);
  43.     }
  44.     return View();
  45. }
  46.  
  47. ///<summary>
  48. ///Registrar los datos de un usuario en la base de datos.
  49. ///</summary>
  50. ///<param name="user">Datos ingresados por el usuario.</param>
  51. [HttpPost]
  52. publicActionResult NewUser(User user)
  53. {
  54.     if (!ModelState.IsValid)
  55.     {
  56.         return View("Register", user);
  57.     }
  58.     else
  59.     {
  60.         //Registrar el Usuario en la Base de Datos si previamente no existe
  61.         using (var context = new Models.DataContext())
  62.         {
  63.             if (!context.Users.Any(x => x.IdUser == user.IdUser))
  64.             {
  65.                 context.Users.Add(user);
  66.                 context.SaveChanges();
  67.             }
  68.         }
  69.         return RedirectToAction("Index");
  70.     }
  71. }

 

En la sección Other Methods tendríamos las acciones que llegáramos a necesitar mediante nuestras peticiones AJAX y en la sección Other Pages las demás páginas que necesitemos. Como podemos observar en el Main, las Acciones de nuestras páginas se componen de 2 tipos:

  • Páginas que no requieren obtener los datos del Usuario:
  1. publicActionResult Ejemplo()
  2. {
  3.     return View();
  4. }
  • Páginas que requieren obtener los datos del Usuario:
  1. publicActionResult Ejemplo()
  2. {
  3.     User user = null;
  4.     string action = AppFacebookControl(ref user, "Ok");
  5.     if (action != null)
  6.     {
  7.         return RedirectToAction(action);
  8.     }
  9.     return View();
  10. }

Ahora procedemos a crear la Vista de nuestro Install y a configurarla.

Install.cshtml

  1. <divclass="loading"></div>
  2. <divclass="instalar"style="display:none;">
  3.     <aid="install"href="#">Instalar Aplicacion</a>
  4. </div>
  5.  
  6. @section scripts{
  7.     <scripttype="text/javascript">
  8.         function WindowRedirect(accessToken) {
  9.             window.location = '@(Url.Action("Install"))?accessToken=' + accessToken;
  10.         }
  11.         FB.getLoginStatus(function (response) {
  12.             if (response.status === 'connected') {
  13.                 WindowRedirect(response.authResponse.accessToken);
  14.             } else {
  15.                 $(".loading").hide();
  16.                 $(".instalar").fadeIn();
  17.             }
  18.         });
  19.         $("#install").on("click", function (e) {
  20.             e.preventDefault();
  21.             FB.login(function (response) {
  22.                 if (response.authResponse) {
  23.                     WindowRedirect(response.authResponse.accessToken);
  24.                 }
  25.             }, { scope: 'email' });
  26.         });
  27.     </script>
  28. }

 

Como podemos observar comprobamos si el Usuario ya ha instalado la aplicación en Facebook, de ser así obtenemos el accessToken y redireccionamos la aplicación a la Acción de nuestro Controlador correspondiente. Mientras se comprueba el estado el Usuario verá un loading en la aplicación, si no la ha instalado tendrá que dar Click en el botón para instalarla.

Luego procedemos a configurar nuestro formulario de Registro con los datos requeridos de nuestros usuarios, tendremos un elemento form que empleará jQuery Validation al momento de realizar el submit de nuestro botón Registrar, el cuál automáticamente validará nuestros campos y de ser válidos realizará el Post a nuestra Acción de registrar un usuario NewUser (Recuerda: Las verdaderas validaciones se encuentran en el lado del Servidor, en el cliente es solo por apariencia). He podido observar en las aplicaciones que he realizado existen casos (Bug de Facebook) en donde a pesar de solicitar el permiso de email a la API de facebook, este campo no es retornado por lo cual se le debe de solicitar al Usuario que lo ingrese de ser así.

 

Register.cshtml

  1. @model WebApp.Models.User
  2.  
  3. @using (Html.BeginForm("NewUser", "Home", FormMethod.Post, new { id = "newuser" }))
  4. {
  5.     @Html.HiddenFor(x => x.IdUser, new { id = "idfacebook" })
  6.     @Html.TextBoxFor(x => x.Email, new { id = "email", autocomplete = "false" })
  7.     @Html.TextBoxFor(x => x.FirstName, new { id = "firstname", autocomplete = "false" })
  8.     @Html.TextBoxFor(x => x.LastName, new { id = "lastname", autocomplete = "false" })
  9.     @Html.TextBoxFor(x => x.Document, new { id = "document", autocomplete = "false" })
  10.     <aid="register"href="#">Registrar</a>
  11. }
  12.  
  13. @section scripts{
  14.     @Scripts.Render("~/blog/bundles/jqueryval")
  15.     <scripttype="text/javascript">
  16.         FB.getLoginStatus(function (response) {
  17.             if (response.status === 'connected') {
  18.                 FB.api('/me', function (response) {
  19.                     $("#idfacebook").val(response.id);
  20.                     if (response.email) {
  21.                         $("#email").val(response.email).attr('readonly', true);
  22.                     }
  23.                     $("#firstname").val(response.first_name);
  24.                     $("#lastname").val(response.last_name);
  25.                 }, { scope: 'email' });
  26.             } else {
  27.                 window.location.reload();
  28.             }
  29.         });
  30.         $("#register").on("click", function (e) {
  31.             e.preventDefault();
  32.             $("#newuser").submit();
  33.         });
  34.     </script>
  35. }

 

Para terminar procedemos a continuar con la configuración de nuestra aplicación en Facebook para obtener los datos que necesita nuestra Aplicación ASP.NET MVC.

Después de crear la aplicación en Facebook Developers procedemos a editarla.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_132.png

Capturamos los datos necesarios en nuestro Web.config.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_133.png

Como pueden observar esta aplicación la estoy depurando desde mi Visual Studio (localhost), para obtener la dirección y la URL segura realizamos el siguiente procedimiento.

Nos situamos en nuestro Proyecto, F4 y habilitamos SSL, automáticamente nos generará la URL Segura.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_134.png

Click derecho a nuestro Proyecto, Propiedades y configuramos la URL obtenida para crear un directorio virtual.

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_135.png

Y depuramos…

http://jdnichollsc.azurewebsites.net/image.axd?picture=image_thumb_136.png

Esta es toda la configuración que necesitamos básicamente para desarrollar nuestras aplicaciones en Facebook, cualquier sugerencia de modificación es totalmente bienvenida. La idea es mejorar constantemente esta plantilla y poder recibir retroalimentación entre todos.

http://octodex.github.com/images/baracktocat.jpg