Angularjs

El motivo de esta entrada no es otro que informaros que mi gran amigo y compañero @XaviPaper junto con un servidor, nos hemos marcado como meta escribir en desarrolloweb una serie de post de Angularjs, con no otro objetivo que transmitir nuestras experiencias con esta librería, derivado de su utilización en varios proyectos realizados a lo largo de 2013.

Como no soy de escribir esta serie de entradas, lo que voy a hacer es poneros a todos un aperitivo de cuales son nuestros objetivos y por tanto empieza la fiesta……

La realidad que el titulo de este post no debería de ser este sino más bien y fiel a mi estilo este otro.

Server-sent DOM events o más bien con la iglesia hemos topado.

Y porque?, pues sencillo simplemente hay que mirar el siguiente cuadro para darnos cuenta a que me estoy refiriendo.

image 

Referencia original: caniuse.

Aunque está claro que este tipo de tecnologías actualmente las podemos reemplazar con otras más modernas como websocket o con otras más antiguas como Long polling, lo que me gusta de esta frente a websocket no es otra que utilizamos el protocolo http sin más.

Como referencia he utilizado una serie de artículos y sobre todo el confirmar que existe algún que otro pollyfill.

Stream Updates with Server-Sent Events

Introduction to Server-Sent Events

EventSource polyfill

W3C Candidate Recommendation 11 December 2012

Y como con artículos de 2010 y una recomendación de diciembre de 2012 sigue IE sin implementarlo.

Será por algo?. La verdad es que es algo inexplicable y tampoco he encontrado razón alguna que justifique el por que noTriste. Más bien coincido con esta opinión.

http://stackoverflow.com/questions/9397528/server-sent-events-vs-polling.

Y porque me estoy preocupando yo por esto, sencillo todo fue a raíz de este post del amigo @Marc_Rubino y mis comentarios junto a los de los amigos @panicoenlaxbox y @gulnor

Seguridad en aplicaciones SPA.

Si os habéis molestado en leer los comentarios veréis que en uno de ellos yo defiendo la usabilidad más que evidentemente la seguridad que creo que todos los que intervinimos estamos de acuerdo en que este no debe de ser tu mecanismo de seguridad.

Con lo cual vamos manos a la obra y defender con código como a mí me gustaría encontrarme una aplicación.

Para ello lo primero que voy a hacer es plantear que existen dos usuarios (UsuarioA y Admin).

En un principio el UsuarioA no tiene permisos para entrar a una serie de páginas pero a lo largo de la mañana el usuario Admin le da permisos por un momento y se los revoca una vez realizada una tarea.

La pregunta es sencilla os gustaría ser el UsuarioA y entrar a esa opción de la App, pasarte un rato rellenando información y cuando pulses guardar que te diga tu app que no tienes permisos, yo creo que no.

Yo más bien y poniéndome en la piel del UsuarioA, prefiero que me diga directamente que no tengo permisos y no hacerme perder el tiempo.

Para ver todo lo que hay que hacer he creado una SPA con MVC y Angularjs y veréis que se puede hacer para luchar con el problema de la cache en una SPA.

Aplicación de Servidor(MVC).

Aunque os voy a dejar el código completo de la app vamos a explicar el porque de las cosas.

1. Nos encontramos con un controlador “HomeController” donde podemos observar una diferencia notable la acción Index no devuelve un PartialView sino directamente un View. Y por que?.

Sencillo, este es el marco principal del Spa y me sirve para aprovechar la potencia de Razor para insertar en JavaScript algún que otro dato.

image

Como podéis ver en el código. Es en la vista Index donde configuro las rutas que después vamos a consumir en nuestros módulo de Angularjs.

Los motivos son claros.

1. Me permite crear el JavaScript dinámico en función de la información del usuario.

2. Puedo utilizar Url.Action en el servidor para indicar cual es la ruta de mis templates, que algún que otro susto me he llevado con no utilizarlo en MVC.

Los controladores como ya os he dicho devuelven todos PartialView(que a la postre son mis templates de Angular,pero aprovechando la potencia de Razor) excepto el Index.

image

Como podéis ver existen un par de controladores a tener en cuenta aparte de los ya comentados.

Forbidden: Si el usuario no tiene permisos para ver una página se le mostrará este html.

<h2>No tiene permisos para ver la página</h2>

NotFound: Si el usuario va a una ruta que no existe desde angular no me gusta redirigirlo a “/” sino a una página que le diga que no existe.

<h2>La página no se encuentra</h2>

Lo siguiente es hacer un par de modificaciones en el Layout Principal de nuestra app.

1. Agregar un enlace al modulo de Angularjs.

<body ng-app=»myApp»>

ngApp

2. Modificar nuestro menu para apuntar directamente a las rutas de Angularjs.

image

3. Si no estoy autenticado para que quiero cargar Angularjs y el resto de JavaScript innecesario.

image

Si yo siempre y por principios Sonrisa voy autenticar contra un servidor y para ello voy a utilizar una vez autenticado el clásico patrón.

http://www.variablenotfound.com/2009/11/el-patron-post-redirect-get.html

http://es.wikipedia.org/wiki/Post/Redirect/Get

Vamos que veo un peligro y no pequeño en poner en mi código JavaScript algo como esto.

if(login()) {….} 

Web Api

Necesitamos crear dos métodos en un controlador.

1. Un GET que es el que se encarga de hacer la magia de Server-sent DOM events.

image

Si observamos este código podemos ver que se inicializa un ManualResetEvents y espera hasta que el administrador cambia los permisos en el siguiente método.

2. Otro controlador PUT que es desde donde el Administrador cambiará los permisos de la aplicación(Ojo que en el código no esta contemplado nada a nivel de roles).

image

Como podéis ver se llama al método Set del ManualResetEvent y se inicializa a un nuevo objeto.

3. Un MediaTypeFormatter que nos permite devolver al cliente un contentType con valor igual a text/event-stream.

image

Y por último vamos a describir que es lo que hemos hecho en el módulo de Angularjs.

Esta no es la forma correcta de organizar el código JavaScript, pero espero que me perdonéis que no es más que una demoSonrisa.

image

1. Definimos un módulo.

var app = angular.module(«myApp», [«ngRoute»]);

2. Configurar nuestra app.

Primero cargamos todas las rutas que previamente configuramos en nuestra página Index(Razor).

Definimos un decorator para ampliar funcionalidades a $routeProvider (concretamente chequear si el servidor nos informa con un cambio de permisos y refrescar la pagina del cliente).

image

3. Una constante. Que es el identificador de evento asignado al usuario por parte del servidor y que se pasa como parámetro al método GET del controlador de nuestra Api.

4.Un servicio que es el que se encarga de toda la magia de Server-sent DOM events.

image

Si nos fijamos en el código observamos lo siguiente.

1. Se le inyecta a esta función el $routeProvider y la constante event.

2. Creamos al señor de la discordia EventSource.

3. Nos suscribimos al evento del usuario y es aquí una vez que nos llegue la información del servidor la convertimos a Json y chequeamos la ruta con el método checkRoute que previamente incluimos en el config del módulo, concretamente en el decorador de $routeProvider.

4. Por ultimo exportamos las funciones connect y close(una vez que el usuario hace logout se desconecta del servidor.

Si el usuario cierra la pestaña del navegador se cancela automáticamente.

4. Controladores. Poco tienen que explicar… Uno se encarga de cambiar los permisos del usuario y el otro de llamar al método close del apartado anterior.

5. Run. Una vez que se ejecuta nuestro módulo se ejecuta esta función y se llama a suscribeEvents.connect().

image

Pues señores que nos falta simplemente ver esto en ejecución para ello desde Firefox voy a hacer de Administrador y desde Chrome seré el usuario agraciado.

Siento no poder hacer la demo con IE11, pero los motivos son obvios.

image

Una vez que el señor Administrador pulsa en el botón Cambiar Permisos y transcurridos 5 segundos( Thread.Sleep(5000) en el servidor ) el señor “UsuarioA” ve el contenido de la página.

image

Si abrimos cualquiera de las peticiones de la carpeta NetWork podemos ver lo siguiente.

image

El Accept que envía el cliente  con valor text/event-stream y la respuesta del servidor Content-Type con valor text/event-stream.

Código de la aplicación completo

Conclusiones.

Muy breve, simplemente es una pena que de una vez por todas esta lucha sin cuartel de los exploradores no se acabe y que siempre tengamos que luchar con este tipo de cosas, que desde mi punto de vista no conducen a nada.

Por último ya sabéis cual es nuestro destino para escribir de Angularjs  desarrolloweb y espero que esto solo sea eso un aperitivo de lo que vais a ver.

Gracias!!!!

Observable o dirty, menos lobos caperucita.

Ni que decir tiene que hacer  Binding con Observable sobre el papel debe de ser más rápido que utilizar Dirty . Pero cuando uno analiza un Binding se debe de preocupar de muchas cosas y la más importante y la que quizá de mejor experiencia de usuario es la carga inicial y por tanto el uso inmediato de nuestra página.

Todos sabemos que existen varios tipos de binding. Aunque en este post solo voy a hacer énfasis sobre OneWay.

1. OneWay: actualiza la propiedad de destino únicamente cuando cambia la propiedad de origen(descripción copiada íntegramente de la página de msdn).

Y porque solamente este, pues sencillo porque voy a hacer medidores de rendimiento de tres frameworks JavaScript y en ningún caso me va a preocupar el rendimiento de TwoWay, por malo que sea, será inapreciable por parte del usuario.

Yo personalmente siempre he utilizado Dirty para hacer Binding y jamás me he preocupado del rendimiento en ese aspecto y también puedo decir que ningún cliente se me ha quejado de esto.

Bueno la verdad es que miento, alguna vez que otra he desarrollado una app con xaml y he utilizado Observable y esa magnífica interfaz llamada INotifyPropertyChanged. Vamos mi amada.

Lo que también digo es que en todos esos desarrollos he terminado por poner un if y la definición de un flag para evitar que se actualizara algo en mi property set, de eso puedo dar fe, sobre todo cuando utilizamos TwoWay.

Dicho esto os voy a decir que tres Framework JavaScript voy a analizar.

Angularjs: Utiliza dirty.

Knockoutjs: Utiliza observable

Durandaljs: Utiliza knockoutjs para los binding y por tanto es observable.

Una cosa que os recomiendo es esta lectura Databinding in angularjs puesto que describe a la perfección el patrón ENGAÑABOBOS (Muestra a los humanos lo que sus ojos pueden ver y despreocúpate del rendimiento,puesto que es implícito cuando aplicas esta técnica ).

Para ver realmente las bondades de observable y dirty os voy a proponer el siguiente supuesto,requisito,etc.

Una empresa nos encarga un software y tenemos que hacer una sencilla página.

Un vendedor está en la calle y se dedica entre otras cosas a la gestión de cobros. Cuando va a un cliente le tienen que aparecer las facturas pendientes de cobro y un botón para cambiar el estado, damos por hecho que las cobra todas,para no complicar el ejemplo que al final es de lo que se trata.

Todas estás pruebas las he hecho simplemente con las herramientas de desarrollo de Google Chrome.

Chrome DevTools Revolutions 2013.

Voy a hacer medidores de tiempo para el siguiente numero de registros.

100. Que alguien te deba 100 facturas  y que lo permitas ya es complicado.

El resto de mediciones las voy a realizar sobre 500,1000,5000 y 10000 simplemente por ver la capacidad de estrés de estos tres Frameworks.

En estos medidores voy a tomar tiempos del Binding,Completed y Program.

Binding: Tiempo en ejecutar el binding y no renderizar, que es lo realmente preocupante.

Completed: La ejecución integra del StackTrace o CallStack.

Program: Aunque no he logrado encontrar documentación al respecto,parece estar ligado a la liberación completa por parte del explorador después de ejecutar Completed y por tanto el momento exacto en el que el usuario puede volver a interactuar con nuestra página. Pero que a diferencia de Binding y Completed que van juntos de la mano puesto que se ejecutan en la misma Pila de llamadas (un usuario pulsa un click y por tanto se ejecuta binding). Este parece ser otro thread que hay que sumar a completed.

Para calcular el binding de cada librería he implementado un sencillo tomador de tiempo y he modificado las librerías originales para insertarlo en el punto donde se inicia el binding y de esta forma comprobar que la herramienta Chrome era fiable.

image

Por cierto alguien ha dicho que no se puede implementar en Prototype Pattern miembros privados, es que me ha parecido haberlo escuchado un montón de veces. ¿Es cierto? Lengua fuera.

Dicho todo esto os voy a mostrar un ejemplo con Angularjs para que veáis como he tomado los tiempos.

image 

Como bien podéis observar por el numero de la consola escrito con mi medidor de tiempos ese 94 coincide con el 93,21 mostrado en el Binding. Para ser exactos hay una diferencia de 0,79 milisegundos que me confirman que estoy tomando bien los tiempos del Binding.

Por resumiros un poco todo si os fijáis creo un nuevo clock antes de llamar a scope.$Apply que es la función que a su ves llama $rootscope.$digest  que ejecuta el famoso DIRTY de Angularjs.

finally

Y porque “Evitar su uso” pues sencillo el propio Chrome la marca como función no optimizada de JavaScript.

Menos mal que Angularjs está desarrollada, apadrinada por el propio Google.

Dejando al margen a Angularjs del que os prometo capítulos muy interesantes a lo largo del 2014, vamos a pasar a ver números de los tres frameworks.

Angularjs

image

Knockoutjs.

image

Durandaljs.

image

Analisis de la información.

Observamos que efectivamente Observable es más rápido que DIRTY pero con unas diferencias muy pequeñas que nunca pasan de los 500 milisegundos en casos extremos.

Pero también podemos ver una cosa muy significativa y es que un Binding no es lo más importante, al final lo verdaderamente costoso es la renderización y es aquí donde Angularjs gana por goleada en cuanto a experiencia de usuario se refiere.

Jamás pasa de los 5 segundos mientras que Knockoutjs y por tanto Durandal superan los 20 segundas en situaciones de estrés extremo, incluso en las pruebas he llegado a ver 38 segundos por parte de Durandaljs. Me temo que por un abuso de promise (Que esto no es c# que es JavaScript).

Que  no es bueno poner muchos.

setTimeout(function(){},1);

Para quien quiera verlo le recomiendo revisar el archivo system.js y dentro de este la función acquire del objecto system.

image

Vamos que deja los task  para c# y piensa que el JavaScript es síncrono excepto en cuatro cositasSonrisa.

Pero hay un dato muy significativo que quizás no habéis observado y es la poca diferencia que hay en el Binding de Knockoutjs y Durandal para el Load y el Update(cambiar el estado de las facturas) y es que los tiempos se parecen, entonces es que estoy poniendo un ejemplo malo para ver la potencia de Observable frente a Dirty. Pues noooooooo.

Lo que pasa es que he tuneado Knockoutjs  para que la carga sea más rápida utilizando Mustachejs, porque sino señores ni con Observable cualquiera de los otros dos frameworks es más rápido en ninguna situación.

Simplemente por llevar la contraria a mi gran amigo @gulnor (Juanma) si hombre el bueno de Koalite. He hecho que los Bindings en Knockoutjs  sean menos VERBOSE Sonrisa gracias a Mustachejs.

Es decir de tener que hacer un curso de ingeniería para hacer un Binding o más bien acordarte por donde vas.

He convertido esto.

image

En esto otro y que funciona en Knockoutjs   gracias a este otro script ko.mustache.js vamos y el tío se pone Copyright XDDDDD. Por implementar estas cuatro líneas.

image

Más agregar esta línea de código.

ko.setTemplateEngine(new ko.mustacheTemplateEngine());

La verdad que es libre de poner Copyright o lo que le apetezca pero el caso es que de lo VERBOSE pasamos a algo más agradable.

image

A costa de evidentemente mejorar el Load de la página y penalizar el único sentido de Observable.

Con lo cual mejoramos el binding gracias Mustachejs y no Knockoutjs  en el Load y lo penalizamos en el Update. Puesto que aquí Knockoutjs pierde la noción del tiempo y ya no sabe que es lo que realmente ha cambiado y vuelve a renderizar todo.

En definitiva que de este análisis sacamos una cosa clara y es que por mucho que me cuenten no es el Binding lo más importante sino la situación completa es decir “desde que el usuario pulsa un boton hasta que obtiene sus resultados”. Y es en esto donde AngularJs gana por goleada.

Con lo cual si utilizamos lo propuesto por Knockoutjs  === Xaml nos vamos de los 4040 milisegundos en la carga a los 8800 utilizando el método no VERBOSE.

Y en el Update===Observable bajamos de los 6718 milisegundos a 248 milisegundos frente a los 265 milisegundos que tarda Angularjs en cambiar el estado de los 10000 registros.

Vamos que ni con esas ganan en situaciones extremas y si a eso le sumo el código que necesito en uno y otro sitio como que me quedo donde estoy y espera a verlas llegar. Es decir me quedo con Angularjs y todos los días mi obligación es estar al tanto de que es lo que mejor y hasta el momento se quien es. Os presento al ganador de la batalla.

image

 

Por último os muestro el código de Angularjs Durandal con el que he hecho las pruebas, aunque si alguno lo necesita que lo pida por twitter y se lo mando.

Angularjs.

image

Durandaljs

image

Madre mía ese ko.observable y ese ko.observableArray hace que entre en éxtasis y desenfreno pensando que llamo a PropertyChanged de c#. Pero el máximo es la siguiente línea para modificar el estado de las facturasSonrisa .

oldvalues[i].state(!oldvalues[i].state());

No os parece más normal esto en Angularjs.

image

que no esto otro en Durandal/Knockoutjs.

image

Que no hemos acabado, que viene lo mejor, que es mi critica personal a ambas librerías(Angular Knockoutjs) por no tener en cuenta lo que los viejos guerreros llamamos fuente de la sabiduría.

Es decir no hacerle caso a este señor, cuando lo que vas a leer y ver está soportado desde el principio de los principios en todos los browser y que no es otro que DOM DocumentFragments.

Vamos para los desarrolladores de componentes entre los que me incluyo el DoubleBuffered de toda la vida.

Construye y lee poquito y  cuando todo esté listo pinta y no lo hagas a pasitos que las pantallas parpadean.

Bueno como uno de los lemas de JavaScript es el Duck typing y el otro es si eres desarrollador de JavaScript eres un Duck(cua,cua y guarro como tu solo). Yo me he convertido en patito y os voy a demostrar como con un código guarro y pensado para lo que es gana y por goleada.

Es decir un lenguaje de Script y que solo se debe de utilizar para esto y en esto es bueno, os voy a demostrar como esos segundos los bajo a milisegundos. Es decir voy a pasar de casi los 20 segundos de Knockoutjs y los 5 segundos de Angularjs. A …. “replique de tambores” ¿Cuantos? 3 segundos, no 820 milisegundos  y lo puedo baja más.

Pintar en el Browser una lista con 10000 elementos.

image

Venga os paso el html y JavaScript a ver si os convierto en patitos, que por cierto bastante guarros son. Y sino regalar a los niños uno por reyes y veréis como acaba el Jardín.

Html .

image

JavaScript.

image

Venga os copio la vista completa, que solo sea copiar y pegar Sonrisa

   1: @{

   2:     ViewBag.Title = "DocumentFragment";

   3: }

   4:  

   5:  

   6: <script id="template" type="text/template">
   1:  

   2:     {{#invoices}}<li><span>{{id}}-{{description}} Dicen que soy lento pero están equivocados,SOY: <strong>{{who}}</strong> | estado : {{state}}</span></li>{{/invoices}}    

</script>

   1:  

   2: <button onclick="render();return false;">Ejecutar como un spa</button>

   3: <button onclick="clearlist(); return false;">Limpiar Lista</button>

   4: <ul id="list">

   5: </ul>

   6:  

   7: @section scripts{

   8:     

   9:     

  10:     

  11:     <script src="~/Scripts/mustache.js">

   1: </script>

   2:     <script>

   3:         var render = function () {

   4:             var fragment, i, templateText = document.getElementById('template').innerText.trim(), clock = util.clock();

   5:             list = document.getElementById('list'),

   6:             createInvoices = function () {

   7:                 var array = [], i, invoiceNumber;

   8:                 for (i = 0; i < util.records; i++) {

   9:                     invoiceNumber = i + 1;

  10:                     array.push({ id: invoiceNumber, description: "Invoice:" + "(" + invoiceNumber + ")", who: 'DocumentCreateFragment', state: false });

  11:                 }

  12:                 return array;

  13:             },

  14:             array = createInvoices(),

  15:             html = Mustache.to_html(templateText, { invoices: array }),

  16:             element = document.createElement('div');

  17:  

  18:             element.innerHTML = html;

  19:             fragment = document.createDocumentFragment();

  20:             for (i = 0; i < element.childNodes.length; i++) {

  21:                 fragment.appendChild(element.childNodes[i]);

  22:             }

  23:  

  24:             list.appendChild(fragment.cloneNode(true));

  25:             console.log(clock.writeTime());

  26:         },

  27:         clearlist = function () {

  28:             list = document.getElementById('list');

  29:             list.innerHTML = "";

  30:         };     

  31:     

</script>

   7:  

   8: }

   9:  

  10:  

Así que hoy hemos visto el patrón CUAC-CUAC.

Es aquel que dice que para pasar de un entorno a otro lo primero es preocuparte de conocer el nuevo y una vez hecho esto no hay otra cosa más sencilla que elegir lo más apropiado, pero si eres desarrollador de c# y piensas que pasar a JavaScript es utilizar TypeScript y los frameworks que más se asemejan a mis conocimientos lo tienes claro, es más no claro la leche que te vas a dar va a ser no pequeña sino muy grande.

Con lo cual musa mía @jersiovic aplícate lo leído y dile al ausente del @pacomonfort que me debe una consultoría que me va a pagar con unos gustoso G….tonic y PAGINA.

Colorín, colorado. Que bien que hemos empezado el Año.

Va por ti “Manolete” que tu si que eras grande.

Buscando a Nemo y a Dory con Katana

Anda que no estoy buscando pececillos. Que estoy buscando puertos y misterios.

Los puertos es sencillo si Owin/Katana son un clon de  Node quiero saber quien abre esos puertos. Con lo cual ya tenemos a Nemo localizado.

Lo otro es un poco más complicado pero encontrando a Dory vamos a encontrar a Nemo.

Que maravilla de la ciencia!!! que crees una clase llamada Startup y que tenga un método con una firma determinada y entonces pases de tener una web pesada a una fluida y alegre tiene que esconder algo.

Firma del Metodo.

public void Configuration(IAppBuilder app)
{
}

Vamos a ver que creo que algo se nos está olvidando , es que nos acordáis del ViewState.

Tu sabes WinForms pues corriendo que haces Web. Lo mismo te acabas de dar un golpe en la nuca que te deja como a Dory(con la memoria fuera de juego).

Que no señores, que no son velocípedos simplemente son menos pesadas y por tanto al cumplir con el patrón “engañabobos” dan la sensación de ir fluidas y  por tanto es de agradecer.

Pues la misma sensación tengo con Katana, pero esta vez va a ser peor, cuidado con el que se olvide de instalar un módulo porque el susto va a ser gordo, seguridad, etc, etc….

Bueno como siempre menos royos y vamos a encontrar a Dory que a la postre es nuestra iniciadora.

¿Porque Startup y Configuration?

La primera de las deducciones es sencilla, si esto lo hago correr en Selft Host. Lo mismo entro en colisión con un Main.

La segunda es más sencilla, porque me apetece y me parecen nombres lo suficientemente significativos como para que alguien se acuerde, buena impresión.

Pero como todo esto sale a la luz?

Pues mirando nuestro magnífico Visual Studio y concretamente el Stack Trace.

Stack

Bueno pues ya tenemos un Main y un Configuration Sonrisa.

Pero vemos un MakeDelegate, hablamos de nuevo de Reflection?

Yo creo que sí.

Vamos a irnos a este código que ya sabéis de donde lo saco….

 

Configuration

Que si señores, que se puede ver que por defecto siempre es este y sino al Xml del WebConfig. Y a lidiar con el post del más listo.

http://www.variablenotfound.com/2013/10/owin-y-katana-iv-startup-y-configuration.html

http://www.asp.net/aspnet/overview/owin-and-katana/owin-startup-class-detection

Así que ya hemos sacando a Dory  de su escondite Sonrisa.

Ahora vamos con el pez payaso y veréis que tiene capa sobre capa y más capa alguien sabe lo que es un wrapper pues cuidado que sino te duchas lo más normal es que alguna vez te llamen eso…Casposo….

Listener

Madre mía si el Owin corre sobre el System.Net.HttpListner

HttpListener

Versión del Framework 2.0 y mientras tanto yo jugando con binarios y con WCF RestFull.

Ya lo dije en mi post anterior.

http://geeks.ms/blogs/phurtado/archive/2013/12/12/la-casta-241-a-con-gusanos.aspx

WebServices

Que ocurre que todo lo que corra en Owin  a fecha de hoy sobre IIS y si alguno está interesado se lo puedo explicar corre sobre HttpApplication.

De lo que puedo deducir que no me la quito ni queriendoSonrisa y mira que tengo ganas….

Os paso ahora unas pruebas en #Node,#Go y C#.

Ojo a los test que no es lo que yo quería pero me gusta jugar en campo neutral..

Node.

var http = require(‘http’);
http.createServer(function (req, res) {
  res.writeHead(200, {«Content-Type»: «application/json»});
  res.end(JSON.stringify({Id:1,Name:’Pedro Hurtado’}));
}).listen(3000, ‘localhost’);

Test.

Node

Go.

package main
import (
    «encoding/json»
    «net/http»
)
type customer struct {
    Id   int
    Name string
}
func Get(w http.ResponseWriter, req *http.Request) {
    w.Header().Set(«Content-Type», «application/json»)
    b, _ := json.Marshal(customer{1, «Pedro Hurtado»})
    w.Write(b)
    return
}
func main() {
    http.HandleFunc(«/api/test», Get)
    http.ListenAndServe(«localhost:4000», nil)
}

Test

Go

 

Owin

Controlador.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;

namespace ClassLibrary2.Controllers
{
    public class TestController:ApiController
    {
        public HttpResponseMessage Get()
        {          
            return Request.CreateResponse(HttpStatusCode.Ok, new { Id = 1, Name = «Pedro Hurtado» });
        }

    

    }
}

Configuration(Dory).

using Microsoft.Owin.Hosting;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Owin;

namespace ClassLibrary2
{
    public class Program
    {
        public static void Main()
        {
            using (WebApp.Start<Startup>(«
http://localhost:5000″))
            {
                Console.WriteLine(«Server startet»);
                Console.ReadLine();
            }
           
        }
    }
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
                name: «DefaultApi»,
                routeTemplate: «api/{controller}/{id}»,
                defaults: new { id = RouteParameter.Optional }
            );
            app.UseWebApi(config);
        }
    }
}

Test.

c#

Así que señores en unos combates bastante ajustados creo que podemos hablar de tablas entre Go,NodeWebApi(Ya no cuento lo que puede pasar si Owin se hostea en IIS).

Ojo que no tengo ni idea de que va esto de los gráficos, me estoy ciñendo a las alturas y a “Pages/Sec” corroborado con esta otra herramienta Apache HTTP server benchmarking tool donde los resultados eran parecidos y tras esta referencia he pedido el favor a un Visual Studio Ultimate para las pruebas,

What is concurrent request (-c) in Apache Benchmak?

Y de este otro magnifica entrada

Should I use Threads or Tasks – Multiple Client Simulation.

Donde os prometo que yield return no entra en mi reducida cabeza….

Así que si cualquiera quiere opinar es libre y todo es sabiduría para la comunidad.

Conclusiones.

Que si sobre caspa somos capaces de no llevarnos todas las leches en el mismo lado, sino que nos mantenemos en pie hasta el último asalto, que está pasando?

Seguimos ocultando conocimientos al resto, donde todos piensan que las versiones actuales son la final four (eso dice nuget).

!==pre.

Pues no señores. Queda mucho por llegar y estoy convencido de que vamos a ganar.

Pero si a esto le sumas, que ciertas herramientas solo están disponibles para….

Y que cierta información no es pública y lo entiendo.

Al final todo el mundo corre en torno a unas corrientes que no nos llevan a otra cosa que por muchos intentos de Open Source, no se traducen en más que en un constante Tira Códigos(Esa es mi opinión a fecha de hoy. Otro más y hasta cuando.

Coincido  con este post del amigo @r_corral. no al 100% sino en más del 120%.

¿Hasta donde podemos llegar?.

Ahora no coincido con estás otras cosas cosas

 No hace falta renovar Geeks. 

Pienso que estamos montados sobre un Dodge Negro que voló hace 40 años(La Cía  me persigue).

No discuto visitas ni números, discuto una cosa sencilla.

Hace tiempo quizás no mucho me hubiese sentido cómodo con un coche viejo pero en la actualidad estamos acostumbrados a cambiar de coche o que nos digan.

No te montes más que te cuesta más el arreglo que uno nuevo. cc @wasat.

Veis que sencillas son mis quejas. Claras y concisas y creo que las entiende toda la comunidad que para eso estamos……

Ahora hoy uno del Barça declarado le acaba de meter en el Bernabeu a uno del Madríd un 0-5( que ya es costumbre).

Quieres que te meta el 6 pues este es seudónimo de un gas ligero que tiene un peso atómico de 4,0026 y que se encuentra en la misma fila que el

Neon ,Argon, Kripton, Xenon y Radon.

Te voy a explicar lo que pasa con los Routes. Que si están optimizados de aquella manera en WebApi 2.0 Sonrisa

Que les falta un campo un string[] ExcludeRoutes. Atento que este es el 0-7 y ya se convierte en Historia, pero en situaciones extremas sigue arrastrandose.

No es lo mismo guardar un Dictionary<string,object> donde string es el path y object el metodo como lo hace Node y Go que descubrir e invocar el Object con ….

Recuperar información almacenada en atributos.

Con lo cual no me retes que te meto el 0-8 Lengua fuera.

Edu cual prefieres este https://github.com/Bobris/Nowin o el gas ligero xDDDDD.

P.D. Ahora si que es FELIZ NAVIDAD Y PROSPERO AÑO NUEVO. Que lo mismo no puedo volverSonrisa  Pero las dudas de Owin y WebApi creo que han quedado claras y es que lo mejor está por llegar .

 

La castaña con gusanos

Una de las cosas que más me gusta de estas fechas es poder pasear con un cucurucho de castañas. Por tres cosas principalmente

  1. Se me calientan las manos.
  2. Se me llena la barriga.
  3. Me sale barato y en tiempos de crisis no hay nada como algo barato y bueno.

 

castañas

 

En contra tiene una pega y es que alguna puede llevar premio en forma de gusano.

Sí a estás alturas no has cerrado el post, vas a ver que es lo que para mí es una “castaña con gusanos”.

Si señores las routas de mvc y WebApi por lo menos con la versión con la cual yo estoy trabajando(4.0).

Mientras que los métodos en WebApi se llamán Get,Post,Put,Delete, todo es perfecto, ahora cuando en un mismo controlador necesitas otra cosa, ponte a llorar.

Bueno pues dicho esto ya sabéis de que va el post. Los otros días tras esta entrada en twitter.

TwitterRutas

Para los que quieran la conversación completa os paso la url

https://twitter.com/_PedroHurtado/status/410525801314938881.

Mi buen amigo @nicolocodev me recomendó que utilizase Attribute Routing in Web API 2. Aunque ya había leído sobre esto se me planteaba algún que otro problema y es que yo no tenía mi proyecto en 2.0. Lo típico te vas a Nuget, buscas y te lleva a este sitio http://attributerouting.net/ la verdad que es un proyecto de lo más interesante y se puede ejecutar en mi escenario, pero…. Que veo!!!

Un símbolo rojo en WebApi y este cartel más bien llamativo.

Performance

Con lo cual al leer algo de performance como que se me activan las neuronas y de una conversación de las muchas que mantengo por twitter nace todo lo que vais a ver.

Si hablamos de performance, porque no creamos 1000 controladores con un método “GET” que simplemente devuelva una cadena y vemos si es verdad lo que dicen.

Manos a la obra.

1. Vía Nuget o bien desde el Pakage Manager Console instalas AttributeRouting.

2. Una vez que se instala se crea una clase en la carpeta App_Start con nombre AttributeRoutingHttpConfig. Donde rápidamente puedes ver que cuando la web se activa se ejecuta el método Start.

[assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplication18.AttributeRoutingHttpConfig), "Start")] 

3. Pero como creo yo 1000 controladores sin T4 y sin copiar ni pegar. Sencillo pero misterioso. De esas cosas que dan miedo hasta que no las pruebas. Os presento a …. Don Reflection Emit.

En esa misma clase voy a crear un método llamado CreateManyControladores con el siguiente código.

   1: private static Assembly  CreateManyControladores()

   2: {

   3:    AppDomain ad = AppDomain.CurrentDomain;

   4:    AssemblyName am = new AssemblyName();

   5:    am.Name = "TestAttributeRouting";

   6:    AssemblyBuilder ab = ad.DefineDynamicAssembly(am, AssemblyBuilderAccess.RunAndSave,"c:\mvc");

   7:    ModuleBuilder mb = ab.DefineDynamicModule("testmod", "TestAttributeRouting.dll");

   8:    Type t = null;

   9:    for (int i = 0; i < 1000; i++)

  10:    {

  11:        var controllerName = string.Format("Type{0}Controller", i);

  12:        var routeUrl = string.Format("/Hello{0}", i);

  13:        var returnMessage = string.Format("This is a string from method {0}", i);

  14:  

  15:        var typeBuilder = mb.DefineType(controllerName, TypeAttributes.Public, typeof(ApiController));

  16:  

  17:        var methodBuilDer = typeBuilder.DefineMethod("Get", MethodAttributes.Public, typeof(string), null);

  18:        var constructor = typeof(GETAttribute).GetConstructor(new Type[] { typeof(string) });

  19:        methodBuilDer.SetCustomAttribute(new CustomAttributeBuilder(constructor, new object[] { routeUrl }));

  20:  

  21:        ILGenerator il = methodBuilDer.GetILGenerator();

  22:        il.Emit(OpCodes.Ldstr, returnMessage);

  23:        il.Emit(OpCodes.Ret);

  24:        t = typeBuilder.CreateType();

  25:    }

  26:    

  27:    ab.Save("TestAttributeRouting.dll");

  28:    return t.Assembly;

  29: }

No te asustes que te lo explico.

    1. Crear un AssemblyBuilder y le dices que es para ejecutar y guardar e indicas una ruta donde guardar la dll, en mi caso c:mvc.
    2. Creamos un modulo que es donde vamos a compilar todos nuestros tipos.
    3. Creamos 1000 controladores con nombre Type000Controller, 001,002,3 etc,etc con el Atributo “GET”  y  con la ruta “Hello0,1,2,3”
    4. Devolvemos una cadena que nos indica que método se está ejecutando.
    5. Por último guardamos la dll “TestAttributeRouting.dll” y devolvemos el Assembly.

¿Que no te lo crees?

Pues mira si existe el archivo y con ILSpy descompílalo y vas a obtener esto.

ilspy

Hay Dios, “dichoso del que cree sin ver” Sonrisa.

Y ahora vamos con la confirmación de una mala “performance”.

Para ello en el método RegisterRoutes de la clase AttributeRoutingHttpConfig vamos a agregar el siguiente código y a tomar tiempos.

   1: public static void RegisterRoutes(HttpRouteCollection routes) 

   2: {    

   3:     // See http://github.com/mccalltd/AttributeRouting/wiki for more options.

   4:     // To debug routes locally using the built in ASP.NET development server, go to /routes.axd

   5:  

   6:    

   7:     routes.MapHttpAttributeRoutes();

   8:     

   9:     //Create assembly

  10:     var stopwatch = Stopwatch.StartNew();            

  11:     var assembly = CreateManyControladores();

  12:     stopwatch.Stop();

  13:     var miliseconds = stopwatch.ElapsedMilliseconds;

  14:  

  15:     //Compile routes

  16:     stopwatch.Start();

  17:     routes.MapHttpAttributeRoutes(c => c.AddRoutesFromAssembly(assembly));

  18:     stopwatch.Stop();

  19:     miliseconds = stopwatch.ElapsedMilliseconds;

  20:     

  21:     

  22: }

Creación de la dll dinámica.

Assembly

 

Compilación de rutas.

CompileRutes

Efectivamente hemos confirmado que existe en esta dll un serio problema de rendimiento. No os voy a explicar más de esto puesto que tenemos que pasar a otra cosa, pero yo me he bajado el código fuente de AttributeRouting y si que tiene alguna que otra sentencia rara, yo creo que desconocimiento de Reflectión como el siguiente de los protagonista.

Debería decir lo que es, pero lo dejo para otro capítulo o mejor para que investigues tú Sonrisa.

Si ya tenemos todo esto. Es el momento de ejecutar desde nuestro explorador cada una de las rutas y ver lo que pasa.

Response

Pín,Pín,Pum!!! algo raro está pasado. Venga ánimo que vamos a ver lo que es.

Dentro de todo lo que pasa en WebApi hay una serie de Servicios que se encargan de algunas cosas y concretamente el problema lo tenemos en el servicio DefaultHttpControllerTypeResolver.

En este servicio hay bajo mi punto de vista un problema y es que una dll dynamica no devuelve los tipos con esta instrucción.

assembly.GetExportedTypes();

Y claro el señor que escribió esta clase por lo visto no conocía,no recuerda o tuvo un lapsus para saber como obtener los tipos públicos de un ensamblado dinámico.

assembly.GetTypes();  

Es por eso por lo que escribió el siguiente código.

Código malo

O lo mismo estaba en la especificación y entonces el no es el culpable sino otro.

No permitir definir 1000 controladores al vuelo y tampoco ejecutar dll’s dinámicas de serie cosa que se hace una y otra vez con las vistas. Pero bueno… Vamos con la solución.

Crea una clase con el siguiente código.

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Reflection;

   5: using System.Web;

   6: using System.Web.Http.Dispatcher;

   7:  

   8: namespace MvcApplication18.Handler

   9: {

  10:     public class MyDefaultTypeResolver : DefaultHttpControllerTypeResolver

  11:     {

  12:         public MyDefaultTypeResolver()

  13:             :base()

  14:         {

  15:         }

  16:         public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver)

  17:         {

  18:             List<Type> result = new List<Type>();

  19:             var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

  20:             

  21:             foreach (var assembly in assemblies)

  22:             {

  23:                 

  24:                 Type[] exportedTypes = null;

  25:                 if (assembly == null)

  26:                 {

  27:                     // can't call GetExportedTypes on a dynamic assembly

  28:                     continue;

  29:                 }

  30:  

  31:                 try

  32:                 {

  33:                     if (assembly.IsDynamic)

  34:                     {

  35:                         exportedTypes = assembly.GetTypes();

  36:                     }

  37:                     else

  38:                     {

  39:                         exportedTypes = assembly.GetExportedTypes();

  40:                     }

  41:                 }

  42:                 catch (ReflectionTypeLoadException ex)

  43:                 {

  44:                     exportedTypes = ex.Types;

  45:                 }

  46:                 catch

  47:                 {

  48:                     // We deliberately ignore all exceptions when building the cache. If 

  49:                     // a controller type is not found then we will respond later with a 404.

  50:                     // However, until then we don't know whether an exception at all will

  51:                     // have an impact on finding a controller.

  52:                     continue;

  53:                 }

  54:                

  55:                 if (exportedTypes != null)

  56:                 {

  57:                     result.AddRange(exportedTypes.Where(x => IsControllerTypePredicate(x)));

  58:                 }

  59:             }

  60:  

  61:             return result;

  62:            

  63:         }

  64:  

  65:            

  66:     }

  67: }

Y en el archivo Global.asax en el método Application_Start incluir esta línea de código.

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerTypeResolver),

                new MyDefaultTypeResolver());

Como podéis ver en el código, yo si permito ensamblados dinámicos y luego cargo los tipos correctos, queréis que  todos vuestros controladores sean privados, pues otra cosa para investigar.

Assembly.GetTypes()

Ves que sencilloSonrisa.

Ahora puedo ejecutar desde el explorador estás dos rutas, pero no le des que fallaSonrisa.

http://localhost:50150/Hello999

http://localhost:50150/api/type999

Claro esto pone en favor de “AttributeRouting” otro problema y es que nuestra api ahora responde a dos rutas con lo que plantéate problemas de auditoría y me dices por donde te están llamando.

Que es? un todo o nada. Me obliga a decorar todos mis métodos con un atributo.

Hemos vuelto a

mvc restfull webservices o no hemos salido aún

Conclusiones.

1. Si esto pasa en WebApi20 es para llorar, no lo he probado.

2. Ten claro que hasta que no llega la llamada a tu controlador todo es Reflection que puede ser optimo si lo haces bien o poco optimo si lo haces mal y en este caso esta mal hecho.

3. Que no inventes lo ya inventado que todo el mercado funciona con una convención de rutas y que se ha demostrado que funciona.

4. Que no os hago una demo en #nodejs y en #go porque se está haciendo pesado.

Pero repito menos papas, guardianes ,que  funcione bien y se testee para escenarios de este tipo que con dos controladores todo funciona y tener 1000 controladores es fácil (200 entidades x 5 Métodos).

Hasta la próxima, que dada las fechas será para 2014.

FELIZ NAVIDAD Y PROSPERO AÑO NUEVO!!!

P.D. Me despido desde el blog porque desde el twitter aún os quedan aprox. unos 15 de días de guerra.

Go que no Goto

Hace aproximadamente un año, si recordáis os empecé a dar el follón con Node.

Todo vino por un twitter de @0GiS0 que no recuerdo muy bien que decía algo así como “hoy toca probar nodejs” y después este post.

Node.js & Windows Azure – ¿Qué es realmente Node.js? ¿Por dónde empiezo? Creando mi Web server

Eso hizo que se me abriera el gusanillo y demostraros que no fui el primero, ahora sí fui el más pesado e insistente hasta convencer a muchos que lo probasen y dio sus frutos y no pocos Sonrisa.

Como todos sabéis tuve el gusto de haceros alguna que otra demostración y charla a lo largo del año, la más apoteósica de todas la podéis leer y ver en este post del amigo Sergio León.

GUSENET, un EVENTO mayúsculo.

Luego fui invitado al gran evento de

http://www.geek-a-paloozaaa.com/speakers/

Organizado por mi gran e incondicional amigo Don Lluis Franco’s Corner, con lo cual si este año me vuelve a invitar más la invitación que tengo garantizada al #yonodetuxaml con el permiso de Pin y Pooooon , os quiero hablar de algo no tan nuevo pero si novedoso The Go Programming Language, aquí tenéis una pequeña introducción en la wiki, que no me gusta copiar y pegarSonrisa.

El 23 de noviembre tuve el gusto de asistir a una charla de este lenguaje en el GCG Murcia y me entro el gusanillo.

Y yo pensando de que iba a hablar a lo largo del 2014 Lengua fuera.

Bueno vamos con una pequeña demo y verás como os gusta que es bastante fácil.

1. Descarga del nuevo juguete Download the Go distribution.

2. Una vez descargado e instalado yo concretamente en la carpeta c:go voy a crear una carpeta nueva con el nombre jugando con go.

3. Te sitúas en esta carpeta y abres el magnifico ide de desarrollo tecleando notepad, a mi cuando empiezo me gusta este que es el más rápido, pero podéis mirar alguno más en este link.

Comparison of IDEs for Google Go

4. Copias el siguiente fragmente de código en el editor.

package main

import (
    "io"
    "net/http"
    "log"
)

// hello world, the web server
func HelloServer(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "hello, world mi niño!n")
}

func main() {
    http.HandleFunc("/hello", HelloServer)
    err := http.ListenAndServe("localhost:4000", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

Y lo guardas con el nombre HelloWord.go acuérdate de la extensión go que después no puedes compilar ni ejecutar.

5. Para ejecutar simplemente tienes que teclear “go run HelloWorld.go” y vas a obtener este resultado.

utf8

¿Que pasa? que no funciona en Windows. No señores que me ha pasado lo mismo que a mi amigo del alma Carrillo en su primer estreno a lo grande Primeros pasos dentro de Visual Studio Online “Monaco”, solo que el pudo compilar y yo ni he podido una buena cosa a favor de “Go”.

Será un lenguaje tipado Sonrisa ?.

Amigo Carrillo que  como fecha máxima 28 de Noviembre de 2014 te veo publicando un post de “Go” en el flamante Visual Studio "Monaco". Ya sabes lo que dice @2Flores de mi :

“Pedro es un visionario y se mete en tus sueños”.

Solución.

Tienes que guardar como UTF-8 que por defecto “Notepad” lo hace como “ANSI” y en honor a Carrillo he escrito en el response.

“hello, world mi niño”, para recordarle que arregle el postSonrisa.

6. Si haces todas estás cosas y te vas al explorar puedes ver el resultado.

hellogo

7. No pienses que solamente puedes ejecutar un programa “Go” con la instrucción “go run HelloWorld.go” sino que puedes crear un ejecutable e imagino que se puede alojar en Azure. Pero eso lo dejaremos para el próximo capítulo. Para ello tienes que teclear la siguiente instrucción.

“go build HelloWorld.go” y en este momento puedes ver como 378 bytes se convierten en un .exe que puedo desplegar y ejecutar en diferentes plataformas ,simplemente como todos sabéis tecleando.

“HelloWorld”

Eso si con un crecimiento considerable tal y como os muestro.

Y yo “luchando por un byte

gordito

Para los amantes de c# donde me incluyo voy a hacer una pequeña comparativa.

Go c#
package main namespace main{}
import (etc.etc.) using etc;

El resto como podéis observar se parece al “Hello World” de Node con lo cual ya todos sabéis interpretar lo que hace.

Una cosa que me tiene trastocado del lenguaje es que no hay clases pero si interfaces y sobre todo como podéis observar punteros, pero sin aritmética.

El mismo ejemplo pero con interfaces lo podéis ver en este link.

http://tour.golang.org/#57

Conclusiones.

Que prometo no ser muy pesado el próximo año. Pero al ver que era un binario sin dependencias y que puedo hacer correr en diferentes plataformas.He aparcado de momento la idea de aprender phyton en detrimento de Go. Ves Eduard como paso de la recluta del Linkedin Sonrisa.

En este último link podéis hacer un tour introductorio que yo por las noches ejecuto y estudio desde mi flamante Nokia Lumia 8210, vamos que no necesito para estudiar y estar en el sofá ningún IPad Sonrisa.

Se me olvidaba lo más importante, este año en el #yonodetuxaml voy a hacer la demo del año pasado pero este año funciona, que ya no tengo instalado el IE9 por suerte y ningún listillo me va a poder decir “ejecútalo en IE9”.

Los gordos,los flacos y los generosos

Que no os parezca una película de los años 20, simplemente con esto quiero dar mi visión de oauth y hacer que cada uno se sienta identificado según vaya leyendo.

Faltan en el título cinco protagonistas importantes y que vais a ver a lo largo de la historia y el papel tan importante que juegan en nuestras vidas

Pero solo voy a darles el placer del título a los verdaderamente protagonistas “Gordo,Flaco y Generoso”.

Al resto de personajes los catalogo. Como especies a extinguir, pero que actualmente ruedan sobre nuestras cabezas, con todos mis respetos a todos.

-(1) El de la poltrona

-(2) El jefe del flaquito

-(3) El lacayo del jefe del flaquito.

-(4) El lacayo del señor de la poltrona.

-(5) Después un quinto que vais a descubrir. quien a capa y espada defiende la producción  de los gorditos y según que circunstancias ….. las cosas son una forma u otra.

Como siempre voy a empezar por un orden ilógico, lo primero es describir al 4 de los protagonistas y así en orden descendente hasta el primero, para que el quinto se califique solo.

4). lacayo del señor de la poltrona.

Este es el más fácil de describir propone ideas como estás:

Ley de cookies

LOPD

Por lo visto el consejero delegado del señor de la poltrona esta lejos de pensar en la propuesta de ley oauth, pero bien cerca de los buenos cheques y de leyes absurdas.

Pensáis que se entera, flacos?.

Bastante más que nosotros(soy flaquito y generoso). El cobra y nosotros malgastamos nuestros conocimientos===800€ y datos===GB.

Yo creo que más bien piensa en los euros de cada mes “que no son pocos”.

No os sintáis ofendidos, responsables de informática, de centros ineficientesTriste.

Me voy a permitir calificaros

Os empiezo a contar, algunas de vuestras decisiones.

DNI-Electrónico

https://lexnet.justicia.es

Madre mía donde os habéis metido. Pero en el fondo os justifico (por el plato de comida de vuestros hijos).

Estáis al servicio de la poltrona o de los gordos y lo más triste es que estáis al servicio de los gordos sin saberlo.

En los 90 y refiriéndonos a informática, pocos había mejor que vosotros, pero tristemente estáis directamente o indirectamente colaborando  con cosas como esta Angel Custodio === Ignorantes (falta de conocimiento o sentados en la pseudo-poltrona).

Los hay más graves los que piensan que esto es un cargo vitalicio y por tanto no me pueden mover, es decir los que  pasan sus vidas de compras y repartiendo contratos al tercero de los protagonistas, menos mal que nos queda poco que repartir Triste.

La semana pasada en la prensa escrita de nuestro periódico local “http://www.laverdad.es/”, me veo en primera página lo siguiente:

“Los médicos y enfermeros tienen que fichar”.

Que injurias, una profesión tan honesta y al servicio de la sociedad.

“Lacayo del señor de la poltrona” vas a hacer algo para que el reconocimiento de identidad sea algo como  “huella digital o parecido” o les vas a dar una tarjetita?

Uno de estos señores intocables puede decir a uno de sus alumnos, pásame la tarjeta que esta mañana llego tarde, por lo menos que se corte el dedo. Me has entendido Sonrisa.

3). El lacayo al servicio de jefe del flaquito.

Un señor llamado consultor y capaz de convencer a un cliente con sus habilidades,  no hace más que vender motos  o zanahorias largas y duras.

Le propone al cliente una autenticación/autorización #oauth (la competencia lo hace), casi nada.

Las preguntas desde la perspectiva de los flaquitos son obvias.

-¿Sabes lo que dices?

-¿En que mundo vives?

¿Cuanto te cuesta?

¿Cuanto vas a cobrar? pero mejor no me aprietes en el 2015.

Sentencia el flaquito del sillón azul.

Narrador:  El flaquito susurra, pero sin que se oiga que mañana tiene reunión con el que le ingresa la nómina.

-Yo ya te lo he dicho, total vas a hacer lo que te plazca.

Pero tío, te has pasado, sabes lo que estas proponiendo?”.

Este personaje es capaz de hacerse amigo del más listo de la cuadrilla de los flacos, os lo prometo.

Pero “colegui, si esto ya lo hiciste en el proyecto con “referencia 2013/45”.

Automáticamente el flaquito se queda fuera de juego, no es su trabajo pelear con el padre de todos los males, pero en su mente reina la confusión.

Bueno lo último que hace el personaje es llevar al flaquito a tomar unas cañas y decirle algo como esto.

Con este proyecto vamos a “triunfar”.

Memos mal que la mujer del flaquito juega un papel importante y le dice “Es que eres tonto, te sigues fiando del lacayo”, otra larga temporada sin acurrucarme.

Primeras sospechas infundadas del flaquito, que feliz llegue anoche a casa y me lo fastidiaron,le dice al otro flaquito del sillón rojo.

2).  El jefe del flaquito y del lacayo.

Al día siguiente el que te invito a cervezas. Y ante el cual dadas sus habilidades has dicho poco más o menos  “si bwana”.

Se presenta ante otro del circulo del mal y le cuenta el proyecto.

Lacayo del jefe: tengo un proyecto que nos puede salvar el mes y el AÑO!!.

El primer personaje me ha aprobado el proyecto y es un 1.000.000 de €. Ya sabes que es amigo de mi primo Onofre.  Cuidado Jefe que posiblemente no los cobres y lo vas a ver.

Lacayo del jefe: Me ha costado un montón firmar este proyecto, en la reunión había un flaquito que me ha puesto las cosas difíciles con oauth( mi primo ya sabes ese que es muy listo, me ha dicho que eso lo monta el en un par de semanas) .

Jefe ya sabes que soy capaz de vender motos. Y más cuando se trata de un proyecto que nos permite salvar el año.

Lacayo del jefe:  Anoche me lleve al flaquito de la silla azul a tomar unas cerveza y me ha dicho lo mismo  que mi primo, que en un par de semanas el tema lo tiene listo. Gran error flaquito.

Narrador. flaquito que ya te lo dijo tu mjuer. Otra vez te esta engañando. El flaquito sospecha que le liaron con #oauth

“Me Liaron com===#oauth”. Triste Dos noches sin dormir y sin acurrucarse.

Narrador: El resto  os lo podéis imaginar (llega el 30 de Noviembre del año siguiente y aparece el jefe y dos lacayos (aparece RR.HH)  preguntan…).

-Has terminado?

-Es que tu me dijiste!!!

En definitiva flaquito que te ha tocado el gordo de navidad de 2015Sonrisa.

1) El de la poltrona. Ni se enteraSonrisa

-Dice a su lacayo.

A mi no me hables de cosas raras, dime donde tengo que firmar que he quedado con unos amigos y por un millón de euros no me vas a j….r una comida.

 

La historia real

Después de todo esto, alguien no se siente autenticado, autorizado e identificado?

Pues es justo el momento de dejar de leer o bien seguir con la historia  del  “FLAQUITO” en la que sin más conocimientos el generoso colabora y el  gordito se frota las manos a costa de todos,recauda información.

El 5 personaje esta a punto de aparecer. 

–En resumen empieza la parte técnica de #oauth 2.0.

Lo primero es dejar claro que yo soy partidario de tener una sola contraseña para todo, pero regulada y en estos momentos ese tipo de cosas están en el limbo.

Lo primero es dejar claro que todos aparte de flaquitos somos generosos. Os hago varias preguntas.

¿Quien no es Facebook,Twitter,Google o Microsoft?

Por tanto quien no se ha registrado en las siguientes páginas:

https://www.facebook.com/

https://twitter.com/

https://accounts.google.com/SignUp

https://signup.live.com/signup.aspx

Bueno pues ya os he descrito al primero de los personajes,todos ellos tienen sus mecanismos de privacidad y demás. Si queréis flaquitos que vuestras páginas se autentiquen con más,  solo tenéis que ir a esta página OAuth 2.0 y mirar por ejemplo que twitter no soporta Oauth 2.0, o por lo menos yo no lo veoSonrisa.

Y es en este punto donde se va a describir al 5 de los personajes(el incondicional) que normalmente es la empresa del tercero “el jefe del flaquito”, si señores el que dice que esto es buenísimo,fácil y maravilloso, autenticarse con mvc 4 y oauth es una línea de código.

Vamos a ver si después de esto sigue diciendo lo mismoSonrisa.

Para ello vas a seguir los pasos detenidamente que te voy a mostrar a continuación.

1. Creas un nuevo proyecto mvc4.

2. Te vas a la capeta App_Start y abres el archivo AuthConfig y quitas los comentarios a esta línea.

OAuthWebSecurity.RegisterGoogleClient();

No es que yo tenga un amor incondicional por Google sino que es una forma rápida de ver la demo puesto que no te obliga a registrar ninguna app en su sistema, La autenticación se hace mediante OpenId.

3. Una vez que has hecho esto si pulsas F5 puedes ver que claramente ya me puedo autenticar con Google, pulsando en tu página “iniciar sesión”

Inicio sesión

4. Ahora antes de acceder con tu cuenta de Google a tu app quiero que abras el archivo “AccountController” de la carpeta “Controller” y pongas dos puntos de interrupción en los siguientes métodos.

ExternalLogin.

ExternalLoginCallback.

Con lo cual puedes observar rápidamente que el primero es un POST y el segundo un GET, pero fíjate en el atributo AllowAnonymous, vamos que aunque tu controlador esta decorado con “Autorize” esos dos métodos son públicos.

Por otra parte es lógico puesto que si no fuese así quien se podría autenticar y como pretendes que en este caso Google te envié un token y tu puedas comprobar que lo que te esta enviando es correcto, mediante un nuevo POST a Google, básicamente en este párrafo hemos descrito una gran parte de oauth.

En definitiva que te tiene que quedar claro que para realizar la autenticación con oauth en este caso es con OpenID, se hacen los siguientes pasos.

1. Un POST a tu página o si quieres puedes hacer GET y lidiar con XSRF CSRF tu mismo nadie te lo impide Guiño.

2. Rediriges a tu proveedor oauth/openid.

Es este paso pueden pasar dos cosas si no estás autenticado el proveedor te debe de pedir que te autentiques y una vez que te autenticas debes de autorizar a tu app. Si ya estás autenticado y autorizaste a tu app directamente el proveedor te va a redirigir al método ExternalLoginCallback.

3. Haces un post al servidor de oauth/openid “Server to Server” y este  valida  tu código de autorización y obtienes el token definitivo.

4. Si miras la lógica de este método ves un comentario con este texto.

         “// El usuario es nuevo, solicitar nombres de pertenencia deseados”

Pues gracias a las maravillas de MVC4 lo que se esta haciendo es guardar en una bb.dd información del usuario que se ha autenticado, con lo cual el generoso acaba de compartir parte de sus datos con dos sitios, ahora es por lo que empiezo a entender porque tengo mi bandeja de entrada en este estado.

 

Generoso

Por ser vago y muy generosoSonrisa.

5. Abre tu explorador de bb.dd desde visual studio y céntrate en las dos entidades que tengo desplegadas (me gusta un poco más definirlas como tablas) .

Membership

 

En la primera de ellas mira el campo UserName y verás un correo electrónico, te suena?.

El la segunda mira el campo Provider y ProviderUserId y empieza a acojonarte porque por los tiempos de los tiempos en esta web es tu usuario y password.

Y porque me tendría que acojonar te pongo el siguiente escenario.

En el proceso de desarrollo tu tienes que ir al cliente y la aplicación ya esta desplegada, con lo cual para sacar pecho delante del cliente no se te ocurre decir al cliente autenticate tu. Sino que lo haces tu  en Google con tu usuario y password. El cliente se queda súper contento y ve que esa parte esta terminada, que tíos más rápidos y más eficientes.

Sales del cliente y te vas a rendir cuentas al lacayo y al jefe , vas que no cabes en ti mismo, vamos que este día vas a tener una discusión con la mujer, porque es una mal pensada y tu un tío muy listo. En ese momento tu jefe factura 300.000 € que es 1/3 del valor del proyecto y que muy gustosamente el cliente paga.

Trascurridos los días suena el teléfono de tu mesa “rin,rin”.

Flaquito:Diga.

Cliente: Hola Flaquito.

Flaquito: Te funciona todo correcto no?.

Cliente:Si funciona también que hemos observado que la competencia tiene información de la página que estamos haciendo.

Flaquito:  Eso no puede ser, si la página esta toda protegida, si hemos diseñado  un controlador base del que heredamos al que le hemos puesto el atributo “Autorize”.

Cliente: Bueno pásame con el lacayo que esto hay que solucionarlo.

Flaquito: Venga te paso.

Narrador: En ese momento el sillón azul se empieza a mojar de un sudor frio y el flaquito no es capaz de concentrarse, se le ve nervioso, inquieto y sobre todo asustado, no entiende lo que ha podido pasar.

Pues te lo voy a empezar a contar que lo mismo te suena, por tener mucho cariño a tu cliente o presionado por el lacayo diciendo este, que eso no lo podemos hacer ahora que ya le hablaremos al cliente de https Triste.

Has hecho un despliegue en un sitio http y no https con lo cual mientras tu disfrutabas enseñando tu trabajo otro disfrutaba haciendo esto Ataque Man-in-the-middle.

Otro error que cometiste es que acabada la demo no te fuiste a la página de revocación de permisos de google y quitaste la app.

Y por último y más grave como estabas eufórico no pensaste que te habías dejado la sesión con Google abierta en el cliente del pc ,con lo cual cualquiera podía entrar en tu nombre a esa página.

Otra cosa que se te olvido fue advertir al cliente que tal y como estaba la página cualquier usuario con una cuenta Google o bien con una cuenta propia podría ver el contenido privado y por tanto tendrías que haber parado el sitio después de la demo, o mejor haber luchado por no hacer estas cosas,sin advertírselas al cliente.

 

Login asp.net

De que te esta protegiendo MVC4 de un ataque Cross Site Request Forgery,  pero  de lo que no te protege es de un JANUS.

Después de todo esto, tienes que hacer lo siguiente ver todo lo que ocurre con esa autenticación en tu sitio, para ello te recomiendo que sigas los siguientes pasos.

Si no conoces Fiddler descárgatelo y miras lo que pasa en todo este circo. Una herramienta recomendable, al igual que también lo es SQL Server Profiler  para cuando gratis en todas las versiones.

Una vez capturado todo el flujo de tu página cuando autenticas con Google te vas a encontrar con esto.

fiddler

Olvídate del Tunel to y vamos a analizar  todas las llamadas que no son HTTPS y que muy gustosamente alguien que no conoces ha podido leer por ti.

Vamos a elegir la primera la quinta y la última y como siempre siguiendo mi orden ilógico de las cosas voy a explicaros la última.

Para ello voy a pulsar en la carpeta cookies y me voy a quedar con una muy bonitaSonrisa .ASPXAUTH.

Después de “cerrar sesión”  vamos a ver lo que pasa si alguien te captura esto.

Vamos a la  carpeta Composer y voy a confeccionar esta llamada.

image

 

Estoy viendo al “flaquitoazul” y he cerrado sesión como puede ser Sonrisa

1. No estás convencido de utilizar https incluso en tus desarrollos?.

2. Has revisado alguna vez esto en tu webconfig

   1: <authentication mode="Forms">

   2:      <forms loginUrl="~/Account/Login" timeout="2880" />

   3: </authentication>

Esos 2880 son segundos o minutosSonrisa. Pero claro aún siendo segundos que no lo son(tiempo de vida igual a 48 horas) el atacante tiene las suficientes armas como expiar completamente tu web.

2. Vamos a centrarnos en la primera llamada del flujo, pero no en el request(llama a ExternalLogin) sino en el response.

Para ello quiero que te centres en “Location” y en “Set Cookie” vas a ver un par de cosas misteriosas.

__sid"__ : Identificador de sesión generado con este algoritmo

Guid.NewGuid().ToString(“N”).

__csid__": Cookie de sesion generada de forma un poco más seria pero que vive por el resto de nuestras vidas.

Como casi seguro que no te lo crees, vas a utilizar otra herramienta ILSPY y con esta vas a explorar lo que hace el método

“OAuthWebSecurity.RequestAuthentication” Pulsa F12 en el constructor de este código. Otra vez una sola línea era necesaria, que maravilla y que facil.Sonrisa

   1: [HttpPost]

   2: [AllowAnonymous]

   3: [ValidateAntiForgeryToken]

   4: public ActionResult ExternalLogin(string provider, string returnUrl)

   5: {

   6:   return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));

   7: }

Bueno menos rollos y con toda esta información eres capaz de llegar al código fuente de este método

   1: // DotNetOpenAuth.AspNet.OpenAuthSecurityManager

   2: /// <summary>

   3: /// Requests the specified provider to start the authentication by directing users to an external website

   4: /// </summary>

   5: /// <param name="returnUrl">

   6: /// The return url after user is authenticated. 

   7: /// </param>

   8: public void RequestAuthentication(string returnUrl)

   9: {

  10:     Uri uri;

  11:     if (!string.IsNullOrEmpty(returnUrl))

  12:     {

  13:         uri = UriHelper.ConvertToAbsoluteUri(returnUrl, this.requestContext);

  14:     }

  15:     else

  16:     {

  17:         uri = this.requestContext.Request.GetPublicFacingUrl();

  18:     }

  19:     uri = uri.AttachQueryStringParameter("__provider__", this.authenticationProvider.ProviderName);

  20:     string text = Guid.NewGuid().ToString("N");

  21:     uri = uri.AttachQueryStringParameter("__sid__", text);

  22:     byte[] input = MachineKeyUtil.Protect(Encoding.UTF8.GetBytes(OpenAuthSecurityManager.GetUsername(this.requestContext)), new string[]

  23:     {

  24:         "DotNetOpenAuth.AspNet.AntiXsrfToken.v1",

  25:         "Token: " + text

  26:     });

  27:     HttpCookie httpCookie = new HttpCookie("__csid__", HttpServerUtility.UrlTokenEncode(input))

  28:     {

  29:         HttpOnly = true

  30:     };

  31:     if (FormsAuthentication.RequireSSL)

  32:     {

  33:         httpCookie.Secure = true;

  34:     }

  35:     this.requestContext.Response.Cookies.Add(httpCookie);

  36:     this.authenticationProvider.RequestAuthentication(this.requestContext, uri);

  37: }

Aquí tienes el __sid__ y el __csid__ y puedes comprobar si me estoy inventando algoSonrisa.

Después de todo el flujo  Google porque alguien se lo ha indicado, tu guardian, te devuelve la llamada al método “ExternalLoginCallback” que es donde verificamos haciendo un post a Google que lo que nos ha llegado el lo aprueba, si a Google le envías dos peticiones con la tercera de las llamadas te la va a rechazar porque para ello utiliza un “nonce”, es decir se preocupa .

Para ello con la herramienta ILSPY vas a llegar según lo que has podido leer hasta el método“ValidateRequestAgainstXsrfAttack” del que gustosamente te voy a enseñar el código.

   1: private bool ValidateRequestAgainstXsrfAttack(out string sessionId)

   2: {

   3:     sessionId = null;

   4:     string text = this.requestContext.Request.QueryString["__sid__"];

   5:     Guid guid;

   6:     if (!Guid.TryParse(text, out guid))

   7:     {

   8:         return false;

   9:     }

  10:     HttpCookie httpCookie = this.requestContext.Request.Cookies["__csid__"];

  11:     if (httpCookie == null || string.IsNullOrEmpty(httpCookie.Value))

  12:     {

  13:         return false;

  14:     }

  15:     string b = null;

  16:     try

  17:     {

  18:         byte[] protectedData = HttpServerUtility.UrlTokenDecode(httpCookie.Value);

  19:         byte[] bytes = MachineKeyUtil.Unprotect(protectedData, new string[]

  20:         {

  21:             "DotNetOpenAuth.AspNet.AntiXsrfToken.v1",

  22:             "Token: " + text

  23:         });

  24:         b = Encoding.UTF8.GetString(bytes);

  25:     }

  26:     catch

  27:     {

  28:         return false;

  29:     }

  30:     string username = OpenAuthSecurityManager.GetUsername(this.requestContext);

  31:     bool flag = string.Equals(username, b, StringComparison.OrdinalIgnoreCase);

  32:     if (flag)

  33:     {

  34:         HttpCookie cookie = new HttpCookie("__csid__", string.Empty)

  35:         {

  36:             HttpOnly = true,

  37:             Expires = DateTime.Now.AddYears(-1)

  38:         };

  39:         this.requestContext.Response.Cookies.Set(cookie);

  40:     }

  41:     sessionId = text;

  42:     return flag;

  43: }

Revísalo bien que no tiene desperdicio, sobre todo la comprobación que controla si estás autenticado y entonces  la cookie de sesión se establece a un año después de la fecha actual, buena táctica para ahorrar tráficoSonrisa.

Guardián te cuesta tanto poner en  la “DotNetOpenAuth.AspNet.dll” un parámetro que indique si el __SID__ es de un solo uso y que lo guardes con tu magnífico sistema SimpleMembershipProvider. Lo mismo a partir de ese momento me planteo utilizar tu mecanismo de oauth, de momento y por culpa de no haber escritos dos if más.

Yo no lo voy a utilizar, y por tanto tampoco lo voy a recomendar.

Esa mezcla de __SID__ Y __CSID__ es una caja de bombas y que con un ataque del tipo MitM existe un gran agujero de seguridad, cada día me fio menos de la eficiencia de https y nuestros señores de la poltrona preocupados por la ley de cookies Triste. Realmente quien te esta salvando es que al intentar reproducir el flujo, siempre te pide que te autentiques con Google, excepto que también te hayan robado las cookies de este.

Vamos con la quinta de las llamadas.

Aquí pueden pasar una cosa que por un error x tu no seas capaz de conectar con Google y verificar esa llamada con lo cual alguien que esta muy feliz tiene esta información mas la cookie __CSID__.

Si lo quieres reproducir en el método “ExternalLoginCallback” pon esta línea de código al principio del método para simular un posible error.

if (null==Request.QueryString["exception"])

                throw new Exception();

Analicemos esta llamada.

Error 500

Redirect desde Google a tu Web

   1: http://localhost:64324/Account/ExternalLoginCallback?

   2: __provider__=google&

   3: __sid__=84b05c1bcc0e4ab19c0abb60aea4dd64&

   4: dnoa.userSuppliedIdentifier=https://www.google.com/accounts/o8/id&

   5: openid.ns=http://specs.openid.net/auth/2.0&

   6: openid.mode=id_res&

   7: openid.op_endpoint=https://www.google.com/accounts/o8/ud&

   8: openid.response_nonce=2013-12-02T09:42:14ZNQdlYw60CWoxIg&

   9: openid.return_to=http://localhost:64324/Account/ExternalLoginCallback?__provider__=google&__sid__=84b05c1bcc0e4ab19c0abb60aea4dd64&

  10: dnoa.userSuppliedIdentifier=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid&

  11: openid.assoc_handle=1.AMlYA9VFrtR0V1W4MTd_MnFq5lR-xeOYnhk5NjkMlxwVh4gtY6K3xLsbmwBycWiT&

  12: openid.signed=op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,ns.ext1,ext1.mode,ext1.type.alias1,ext1.value.alias1&

  13: openid.sig=EcSZxHAYCD7AxJOy+oiR6a/9OMQ=&

  14: openid.identity=https://www.google.com/accounts/o8/id?id=AItOawlA27JaQxHU_0iJ0VL4tPnWqqZgd00uo88&

  15: openid.claimed_id=https://www.google.com/accounts/o8/id?id=AItOawlA27JaQxHU_0iJ0VL4tPnWqqZgd00uo88&

  16: openid.ns.ext1=http://openid.net/srv/ax/1.0&

  17: openid.ext1.mode=fetch_response&

  18: openid.ext1.type.alias1=http://axschema.org/contact/email&

  19: openid.ext1.value.alias1=flaquitoazutl@gmail.com

Fíjate que de nuevo tienes el __sid__ , __csid__ y algún que otro dato interesante.

openid.identity que puedes comparar con el campo ProviderUserId que hay en la tabla OauthMemberShip y contarnos si no es el mismoSonrisa

Y otro parámetro interesante

openid.ext1.value.alias1=flaquitoazutl@gmail.com

Alguien más es conocedor de tu email y según se haga la petición posiblemente de algún dato más,sexo,nombre,apellidos,idioma.

Una vez capturado esto y quitada la línea que provocaba la excepción vamos con la consumación del acto.

Vamos a realizar una llamada a nuestro servidor componiendo con fiddler el siguiente request.

   1: GET http://localhost:64324/Account/ExternalLoginCallback?

   2: __provider__=google&

   3: __sid__=51b1d2614111410f8ff573088435ceaf&

   4: exception=1&

   5: dnoa.userSuppliedIdentifier=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid&

   6: openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&

   7: openid.mode=id_res&

   8: openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud&

   9: openid.response_nonce=2013-12-02T11%3A43%3A39ZUv5SlO_0gY_TaQ&

  10: openid.return_to=http%3A%2F%2Flocalhost%3A64324%2FAccount%2FExternalLoginCallback%3F__provider__%3Dgoogle%26__sid__%3D51b1d2614111410f8ff573088435ceaf%26dnoa.userSuppliedIdentifier%3Dhttps%253A%252F%252Fwww.google.com%252Faccounts%252Fo8%252Fid&

  11: openid.assoc_handle=1.AMlYA9VV738T0nHJ2Sv9UxlUA4tL4dDgKavmu_zRka3MvccGoushjWDTJEbM87J5&

  12: openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle%2Cns.ext1%2Cext1.mode%2Cext1.type.alias1%2Cext1.value.alias1&openid.sig=tf1%2BIuqIeqIoC2nrJD%2FxhbAbMc0%3D&

  13: openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawlA27JaQxHU_0iJ0VL4tPnWqqZgd00uo88&

  14: openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawlA27JaQxHU_0iJ0VL4tPnWqqZgd00uo88&openid.ns.ext1=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0&

  15: openid.ext1.mode=fetch_response&

  16: openid.ext1.type.alias1=http%3A%2F%2Faxschema.org%2Fcontact%2Femail&

  17: openid.ext1.value.alias1=flaquitoazutl%40gmail.com HTTP/1.1

  18: User-Agent: Fiddler

  19: Cookie: __csid__=tARvCFSW5-uh8MQ_PTrmDdNz1pje0HMEjrbj7-w74yiVbqaUSMne8XHbWtw5kzKYzlKEZdscLyAZ1bRM8OV-Lg2

  20: Host: localhost:64324

Observa que después del __sid__ he puesto el parámetro exception esta vez con un valor para que nuestro servidor complete todo el flujo. Junto con la cookie __csid__

Y ahora observa el response

Response

Esto es difícil que se produzca por muchas cosas, pero se puede. Con lo cual otra cosa más para no utilizar el sistema de oauth de MVC4 .

1. Se tiene que dar que el usuario este logeado en Google o que te hayan robado las cookies de este.

2. Esto solo se puede ejecutar una vez puesto que Google controla.

openid.response_nonce=2013-12-02T11%3A43%3A39ZUv5SlO_0gY_TaQ

Una vez que esto llegue a Google ya no lo vas a poder enviar más, recibes una respuesta de este tipo.

HTTP/1.1 200 OK

Cache-Control: no-cache, no-store, max-age=0, must-revalidate

Pragma: no-cache

Expires: Fri, 01 Jan 1990 00:00:00 GMT

Date: Mon, 02 Dec 2013 11:46:01 GMT

Content-Type: text/plain; charset=UTF-8

X-Content-Type-Options: nosniff

X-Frame-Options: SAMEORIGIN

X-XSS-Protection: 1; mode=block

Server: GSE

Alternate-Protocol: 443:quic

Connection: close

is_valid:true

ns:http://specs.openid.net/auth/2.0

pero con “is_valid:false”.

Como puedes observar difícil  es pero tampoco imposible.

Todo esto lo he analizado gracias a una implementación que he tenido que hacer de oauth e integrar con:

SkyDrive

Rechazado por tener una api de juguete.

Google Drive

Nuestra herramienta de oficina para esa aplicación. Nos parece bastante mejor la api sin ser perfecta y las herramientas de usuario superan con creces a las de SkyDrive.

Para ello quiero que leáis con detalle estas dos entradas

http://msdn.microsoft.com/en-us/library/live/hh243647.aspx

https://developers.google.com/accounts/docs/OAuth2Login?hl=es

y que os centréis en ver el parámetro “state” si alguno se aburre le voy a pasar otro link a la especificación para que le quede claro el significado.

Primero os copio el texto.

state

         RECOMMENDED.  An opaque value used by the client to maintain

         state between the request and callback.  The authorization

         server includes this value when redirecting the user-agent back

         to the client.  The parameter SHOULD be used for preventing

         cross-site request forgery as described in Section 10.12.

No dejéis de leer la especificación y sobre todo no olvidar pasar por la section 10.12, lo mismo alguien descubre que lo que intenta hacer es lo mismo que “__SID__” Y “__CSID__”, Lo que no me gusta es que Micro me remita a la especificación sin mas explicaciones y lo que me gusta de Google es que por lo menos me copie el texto.

Ahora después de ver esto, tenemos que ver si “__CSID__” cumple con la ley de cookies que no me he leído y que por tanto desconozco. De antemano no me parece más que una causa recaudatoria y que poco soluciona el problemas de los flacos y de los generosos.

Otra cosa que se nos ocurrió fue disminuir el tráfico de nuestros servidores(se gestionan muchos documentos y descargarlos a tu servidor para después devolverlos sube la factura).

Para ello según  la especificación tu puedes pasar  en la url como parametro un  “acess_token” que es lo mismo que enviar Authorization en la cabecera de cada request, ni se te ocurra enviarlo en la url Sonrisa.

Lo  que hacemos es hacer una autenticación “Server to Server”  con JWT  y Google permite hacer Perform Google Apps Domain-wide Delegation of Authority (para cuando en  Office365) con lo cual si un administrador me autoriza a una serie de scopes el usuario no tiene que hacer dos login uno en nuestra app y otro en Google, no hace falta que se haga login en Google, simplemente conocer un usuario de Google y que el administrador haya autorizado tu app.

También puedes obviar el prn o sub de la  especificación JWT y tienes unas 15GB para almacenar documentos en tu app y los gdoc me parece que no cuentanSonrisa.

Pero porque digo que ni se te ocurra, sencillo el administrador me ha autorizado con lo cual en esa parte ando cubierto.

Pero la que no puedo cubrir es la de que un usuario malintencionado de tu propia organización pueda enviar la url a la competencia para que durante una hora pueda hacer uso de ella, tampoco solucionas mucho con el Authorization Request Header Field.

Si es mal intencionado y con un poquito de idea puede copiar el token simplemente con las developers tools del explorador, pero aún es mas malo.

No lo hace desde su ordenador sino que espera a que te vayas a tomar café y lo hace desde el tuyo, con lo cual sin comerlo y sin beberlo generoso inocente se te ha caído el pelo. El generoso malo por tu no haber cerrado sesión ha aprovechado tu momento de relax para limpiarse las manos.

Tenemos un registro de auditoría de los token generados por terceros y para cada usuario.

Solución Patata.

   1: var token = BuildToken("mi usuario en google");              

   2:  

   3: using (var client = new HttpClient())

   4: {

   5:     client.BaseAddress = new Uri("https://accounts.google.com");

   6:     var pairs = new List<KeyValuePair<string, string>>

   7: {

   8:     new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"),               

   9:     new KeyValuePair<string,string>("assertion",token),                

  10:  

  11: };

  12:  

  13:     var result = client.PostAsync("/o/oauth2/token", new FormUrlEncodedContent(pairs)).Result;

  14:     var json = result.Content.ReadAsStringAsync().Result;

  15:     JToken tokenobject = JObject.Parse(json);

  16:  

  17:     string url = "No se muestra por motivos de seguridad"

  18:  

  19:     Task.Run(() =>

  20:     {

  21:         System.Threading.Thread.Sleep(2000);

  22:         using (var wcl = new HttpClient())

  23:         {

  24:             wcl.BaseAddress = new Uri("https://accounts.google.com");

  25:             var x = wcl.GetAsync(string.Format("o/oauth2/revoke?token={0}", tokenobject["access_token"].ToString())).Result;

  26:         }

  27:     });

  28:  

  29:     return Redirect(url);

  30: }

No he visto por ningún sitio la posibilidad en la especificación que esto se pueda hacer.

Que ocurre que unos gordos se reunieron para hacer esto y por lo visto no vieron una solución al “Authorization Request Header Field”

Authorization: Bearer XXXXXX.

Que pasa que no interesa hacer un Set-Autorization(Cambiar los browser cuesta dinero) en el que especifiques.

-El tipo “Bearer”

-El Token” XXXXX

-El dominio a quien va dirigido.

-La url para refrescar el token si se puede.

Vamos algo asi en el Response.

Set-Autorization: Bearer XXXXX;www.google.com;/oauth/refresch

Y que de una vez las dev tools  se puedan activar o no dependiendo de algo que envía tu página, es decir que puedas utilizarlas para desarrollo pero no cuando despliegas, muchos de los males se inician simplemente con esto.

Le doy un beso al primero que securize un <a href=””> de un sitio externo, que además me ha autorizado. Sin hacer chapuzas como la que nosotros hemos montado. Limitar el token a un solo uso. Hacemos revoque del token emitido, trascurrido unos segundos, Google solo emite token de 1 hora, posible errorSonrisa.

Mucho trafico “server to server” y poco usable para el usuario, si refresca la página le va a aparecer un 401.

Que andamos con pies de tortuga y mientras que los señores de la poltrona no intervengan y dejen de hacer leyes absurdas y se dediquen a lo que hacen, seguiremos igual.

Vamos que al final voy a proponer “Oauth 3.0” nombre en clave “El Oauth de la Onu”

El Oauth de la Onu.

1. Yo no soy ni Microsoft,Google,Twitter,Facebook soy de España al igual que “Sir Charles Spencer Chaplin” era de Inglaterra con lo cual ni sus datos ni los míos tienen que estar en EE.UU. o donde Dios quiera.

2. Que todo el que exponga el protocolo oauth y por tanto api o bien los consumidores(nosotros) nos registremos en un sitio oficial para poder trabajar, eso si que nos permita “el localhost” mientras desarrollamos(Micro revisa tu registro de app’s).

3. Que los fabricantes de apis no solo hagan herramientas de este tipo https://developers.google.com/oauthplayground/ sino que cada scope lo registren documentado para el developper y para el usuario, es decir que el generoso tenga muy claro que es lo que autoriza.

Creo que estoy defendiendo el principio de Oauth que es el no tener que estar pasando tu login y password a cada web, sino que cada país sea el responsable de sus ciudadanos.

Lo mismo te das cuenta señor de la poltrona que en vez de recaudar con leyes absurdas, estoy proponiendo que regules y nos des servicios.

4. Que te preocupes tu de regular los certificados y que sean gratis. Verás como nadie más utiliza http y se pasa a https  aunque ya se empiezan a tener dudas sobre su fiabilidad.

5. Una ley de Browser que cumpla con el w3c y el que no lo haga que automáticamente se le multe y no con 25000 €, lo mismo te cuesta menos esfuerzos y recaudas más.

5. Que no te quedes con estas 4 letras sino que hagas algo productivo que veo cerca la revolución de los generosos y flaquitos. La última vez que recuerdo  una de estas la  Poltrona se quedo vacía. Veo muchos Luises y alguna María Antonieta en la Wiki.

Conclusiones

Que me he hecho muy pesado y seguro que desde el punto de vista de algunos incluso me he pasado, pero he intentado describir cosas con las que me encuentro casi todos los días.

-Flaquito si cobramos 800 € es porque los hemos aceptado con lo cual, que los cobres no quiere decir que tu profesión y por eso la elegiste, no sea exclusivamente tu trabajo. Hay que estudiar y entonces puede ser que no cobres 800 ni tampoco te fíes de la mitad de lo que ves y te cuentan.

-Lacayos menos comisiones y menos primos Onofre y no vendas más motos ni zanahorias.

-Jefe hay que pagar y ganar dinero pero creo, por lo que he demostrado en el post, que tu afán por ello te ha llevado quizá a una demanda y no ha devolver los 300.000 € sino a pagar alguno más, En resumen tu sabrás si quieres seguir pagando 800€ y tratarnos como lomo al corte.

-Partner y demás no es necesario acumular personas con títulos emitidos por los gordos para no poder expresar cosas como estas “Todo no es genial”. 

-Gordos decir claramente que lo que estáis mostrando no es más que una demo y que no se utilice. Es decir menos templates en Visual Studio con agujeros y mejor documentación.

Menos clases internal y más código libre, quizá atraigas a más personas.

Plantéate que no si existe la posibilidad que desde nuget se pueda descargar el código fuente de las librerías y lo mismo la gente aporta cosas interesantes, con tanto ocultismo solo estás a expensas de que alguien lo diga. Piensa en la cantidad de gente que no habla, sabe de estos errores y los está aprovechando.

En definitiva que no utilices templates generadas que te pueden traer disgustos y que sirven para eso, para guiarte, pero nunca como el medio perfecto de hacer las cosas.

Y por último gorditos que no hagáis protocolos para tener cuentas e información en vuestro poder, que tanta política de privacidad me abruma.

Con lo cual flaquito por tu afán de hacer las cosas rápido, ahora estás paseando por la calle y los demás repuestos, pero se cometieron muchos errores que por otra parte te los pusieron a huevoSonrisa .

 

 

 

 

 

 

Me gustan las migas con tropezones

Al igual que cuando como migas mi cuerpo me sabe a fiesta, cuando veo una patada a un acceso a datos mi cuerpo se descompone e intento luchar por dejar claro que debes hacer y que es lo que nunca deberías de hacer.

Y ayer en twitter se produjo una conversación que pertenece a la segunda opción “nunca que accedas a datos con EF tienes que utilizar esto”.

https://twitter.com/panicoenlaxbox/status/402507006063300608

Este Viernes tuve la suerte de estar en una charla de Enrique Catalá en #gusenet.

 Debate: de ADO.NET a EF6 con SQL Server y compartir momentos con gente preocupada por el acceso a datos.

Eladio Rincón

Sergio Navarro

Javi

Raúl Serrano

Fran

Y mis dos inseparables Pin y Pon, junto con algunos asistentes más, que no les sigo la pista por twitter.

No muchos la verdad:( para lo importante que puede llegar a ser esto en nuestra profesión.

De todo lo que pudimos hablar que no fue poco y que nos duro un par de días más, que pesaos pero que envidia Paco Monfort, me quedo con esta frase de Eladio

problema no son ORMs. problema es "desprecio" a capa d persistencia.

Sí Eladio, tienes toda la razón, pero tampoco nos lo pone fácil el framework, más bien nos pone tropezones que en las migas están muy buenos pero que no son más que eso, zancadillas y facilidad para cometer errores y no haber pensado las cosas bien desde el principio.

Cuando ayer Sergio León escribió esto

https://twitter.com/panicoenlaxbox/status/402508906066214912

Me salto el chispazo,  algo estaba mal. Y como siempre acostumbro cuando eso pasa a pulsar F12 en el método en cuestión y esperar que me dice el bueno de Visual Studio.

Algo está mal

Es fácil ver que unas son peligrosas y las otras no. Las dos primeras son dos autenticas bombas ejecutadas en EF y lo triste es que se pueden ejecutar:(.

Vamos con un ejemplo sencillo que puedes ejecutar en un proyecto mvc copiando simplemente el código.

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Web;

   4: using System.Linq;

   5: using System.Web.Mvc;

   6: using System.Data.Entity;

   7: namespace MvcApplication12.Controllers

   8: {

   9:     public class HomeController : Controller

  10:     {

  11:         public ActionResult Index()

  12:         {

  13:             using (MyContext ct = new MyContext())

  14:             {

  15:                 ct.MyClass.Add(new MyClass() { Name = "Una locura utilizarlo" });

  16:                 ct.MyClass.Add(new MyClass() { Name = "No utilizarlo en EF es estar cuerdo" });

  17:                 ct.SaveChanges();

  18:             }

  19:  

  20:             using (MyContext ct = new MyContext())

  21:             {

  22:  

  23:                 var result = ct.MyClass.Where(Filter);

  24:                 foreach (var item in result)

  25:                 {

  26:                     Console.Write(item);

  27:                 }

  28:             }

  29:  

  30:             

  31:  

  32:             ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

  33:  

  34:             return View();

  35:         }

  36:  

  37:         public static bool Filter(MyClass myclass)

  38:         {

  39:             return myclass.Name.Contains("locura");

  40:         }

  41:  

  42:           

  43:         public ActionResult About()

  44:         {

  45:             ViewBag.Message = "Your app description page.";

  46:  

  47:             return View();

  48:         }

  49:  

  50:         public ActionResult Contact()

  51:         {

  52:             ViewBag.Message = "Your contact page.";

  53:  

  54:             return View();

  55:         }

  56:     }

  57:  

  58:     public class MyContext : DbContext

  59:     {

  60:         public MyContext() : base("DefaultConnection") { }

  61:         public DbSet<MyClass> MyClass { get; set; }

  62:     }

  63:  

  64:     public class MyClass

  65:     {

  66:         public int Id { get; set; }

  67:         public string Name { get; set; }

  68:     }

  69:     

  70: }

 

Quiero que nos centremos en este fragmento de código.

   1: using (MyContext ct = new MyContext())

   2:             {

   3:                 ct.MyClass.Add(new MyClass() { Name = "Una locura utilizarlo" });

   4:                 ct.MyClass.Add(new MyClass() { Name = "No utilizarlo en EF es estar cuerdo" });

   5:                 ct.SaveChanges();

   6:             }

   7:  

   8:             using (MyContext ct = new MyContext())

   9:             {

  10:  

  11:                 var result = ct.MyClass.Where(Filter);

  12:                 foreach (var item in result)

  13:                 {

  14:                     Console.Write(item);

  15:                 }

  16:             }

Estamos insertando dos registros en nuestra base de datos y creyendo que estamos filtrando de esta aquellos registros cuyo campo “Name” contiene “locura”.

¿Tu crees?, pues la respuesta es sencilla, No. Estás ejecutando contra tu motor de base de datos esta sentencia sql.

“select id,name from MyClass”

Y pensando que lo que querías ejecutar es esto otro.

select id,name from MyClass where name like ‘%’ + @name + ‘%’

Y esto, ¿que es? pues si tengo dos registros como es el caso nada pero si tengo cientos,miles o millones lo que estás haciendo es traerte toda la tabla al cliente y después filtrar en el cliente con LINQ TO OBJECT, vamos que a la postre es como si hubiésemos escrito esto.

   1: using (MyContext ct = new MyContext())

   2: {

   3:     foreach (var item in ct.MyClass)

   4:     {

   5:         if (item.Name.Contains("Locura"))

   6:             Console.WriteLine(item);

   7:     }                

   8: }

Ahora amigo Sergio si lo ves claro y tu también Quique🙂 ,es que no se puedes trabajar tanto y hay que escribir los repositorios por las mañanas, cuando uno está fresco y no fiarse siempre de lo que dice Resharper:).

Cuando me quejaba del Framework no es que a mí me guste patalear sino que cuando algo está mal me gusta decirlo claro y conciso, vamos que soy poco diplomático “está mal” y en este caso para mi es un error definir los métodos extensores de IEnumerable<T> Linq to Object en el mismo namespace que los extensores de IQueryable<T>.

¿Por qué no se creo un namespace llamado System.Linq.Query por ejemplo?

De esta forma Resharper no se equivoca puesto que en vez de utilizar using System.Linq usas System.Linq.Query y esos métodos no aparecen:).

Consejos.

1. Si no sabes de bb.dd y estás definiendo consultas linq con EF, tienes un serio problema. El mayor cuello de botella de cualquier aplicación es la bb.dd, así que aprende. Por mucho que te hayan contado que eso ya no se lleva.

2.Utiliza siempre que trabajes con EF “query syntax” en vez de “method sintax”, por muy friki que te parezca. Se asemeja más a la realidad de una bb.dd aunque el resultado puede ser el mismo en algunos casos, pero no en este.

En este ejemplo en ejecución hubiese saltado una exception del tipo System.NotSupportedException si se hubiese escrito con query syntax.

Method Syntax.

var result = ct.MyClass.Where(Filter) // Funciona pero con resultados desagradables.

Query Syntax.

var result = from item in ct.MyClass

                   where Filter(item)

                   select item. //Compila pero en ejecución lanza una exception del tipo System.NotSupportedException.

3. Ten presente que cualquier cosa que en Entity Framework no devuelva un IQueryable<T> huele mal ,excepto FirstOrDefault, First,Count,Sum etc,etc.

Referencias.

Enumerable.Where Method 

No lo utilices nunca en Entity Framework.

Queryable.Where Method

Utilizalo suiempre con Entity Framework.

Linq to Object

LINQ to Entities

Sintaxis de consultas y sintaxis de métodos en LINQ (C#)

Query syntax vs. Method syntax

En el proximo #yonodetuxaml de #gusenet (4,5,6 de Abril de 2014)vamos a regalar 30 Litros de cerveza que le acabo de ganar a Pon y Migas con tropezones Sonrisa. Apuntalo Pin que paga Pooooon

PD. Sergio últimamente me haces trabajar mucho…..

Yo soy tu guardían

El martes doce de Septiembre de este mismo año mi gatito(@eiximenis) a eso de las de las 2:21 PM publico esto  en twitter Uso de modelos dynamic en #aspnetmvc, solamente con leer el titulo era evidente que expectación iba a crear. Con lo cual sin  leerlo le di al botón de Retwittear y un comentario diversión garantizada.

Era evidente que todo el mundo estaba deseando una solución al engorro de los dto’s, automapper y demás hierbas que no hacen más que hacernos menos productivos y convertir nuestro trabajo en aburrido, o lo que es peor en vagos que devuelven toda la entidad para pintar en una vista dos campos, pero claro a eso últimamente le llaman “Optimización temprana” un antipatrón que a mi me gusta y que lo he convertido yo en el patrón “Engañabobos” básicamente dice “que le des al usuario lo que sus ojos pueden ver y no más”. Será que llamas antipatrón a no saber como funciona una base de datos.

Siguiendo un poco con la historia, mi otro gran amigo Sergio León hizo un comentario en el post Modelos de vista dinámicos en asp.net mvc invocando a un tal “Pedro” con esta frase.

“Por cierto, estoy esperando a Pedro por aquí, me encantaría saber también su opinión xD”

Ese Pedro no es otro que un servidor y le respondí, con una pincelada de lo que va a ser este post y algún que otro comentario en twiter.

Ya tienes mi respuesta.

Una pincelada de mis investigaciones

Mi frustración al darme cuenta

Es evidente que mi amigo Sergio estaba durmiendo la siesta por las horasSonrisa  y hoy o cuando pueda empezará a leer, pero como he dicho que es un gran amigo le voy a ayudar a ver cual era el sentido de mis comentarios.

Hace aproximadamente 20 años que estoy trabajando con herramientas Microsoft y me parecen excelentes, ahora me parece bastante menos excelente que se generen cosas por mí y menos que le pongas el modificador de acceso Internal.

YO ME QUIERO EQUIVOCAR Y NO QUIERO GUARDIANES

Sí señores este párrafo de Eduard tiene su miga.

Pero… no es oro todo lo que reluce. Si cambias la directiva a @model dynamic y vuelves al código donde usábamos un objeto anónimo… Obtendrás de nuevo el error ‘object’ does not contain a definition for ‘Name’. Es decir, a pesar de que declaras el tipo de modelo como dinámico en la vista, parece que para ASP.NET MVC el modelo sigue siendo un object.

Eduard la respuesta es no y si, puesto que en definitiva todo es “object”  el problema es que cuando se crearon los tipos anónimos en c# a algún iluminado no se le ocurrió otra cosa que marcar la clase como “internal”, os acompaño una muestra de código IL.

   1: .class private auto ansi sealed beforefieldinit '<>f__AnonymousType0`2'<'<Id>j__TPar','<Name>j__TPar'>

   2:        extends [mscorlib]System.Object

   3: {

   4:   .custom instance void [mscorlib]System.Diagnostics.DebuggerDisplayAttribute::.ctor(string) = ( 01 00 1D 5C 7B 20 49 64 20 3D 20 7B 49 64 7D 2C   // ...{ Id = {Id},

   5:                                                                                                  20 4E 61 6D 65 20 3D 20 7B 4E 61 6D 65 7D 20 7D   //  Name = {Name} }

   6:                                                                                                  01 00 54 0E 04 54 79 70 65 10 3C 41 6E 6F 6E 79   // ..T..Type.<Anony

   7:                                                                                                  6D 6F 75 73 20 54 79 70 65 3E )                   // mous Type>

   8:   .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 

   9: } // end of class '<>f__AnonymousType0`2'

Después unos pocos meses y años después otros Illuminati al hacer el compilador de las vistas de MVC(si ese que transforma Razor a código).

No se le ocurrió pensar que desde el 2005 “Framework 2.0” existía la posibilidad de marcar un ensamblado con este atributo InternalsVisibleToAttribute, esto lo que hace es que desde nuestra dll podamos exponer las clases internal como public o por lo menos que podamos acceder desde otra dll, si esto existe porque no me dejas marcar cuando voy a compilar el proyecto donde defino mis consultas linq que devuelven tipos anonimos y dinamicos “generar anonimos como public”, si tan sencillo como hacer esto.

GENERAR PUBLICOS

Simplemente con eso no necesito ni ExpandoObject ni la solución que voy a aportar como alternativa a Eduard DynamicObject, que a la postre no es más que eso, otra alternativa que evita tener que iterar dos veces por los datos, pero eso para el finalSonrisa.

Otra cosa que quiero transmitir es lo que realmente hacen nuestras vistas razor cuando se compilan, para ello te invito a crear un proyecto MVC4 y antes borra el contenido de esta carpeta.

C:WindowsMicrosoft.NETFrameworkv4.0.30319Temporary ASP.NET Filesroot

Sí, es en esa carpeta donde se generan todas las dll’s de las vistas que a la postre son las que no pueden acceder al tipo internal de otra dll y causantes del error y posteriores inventos.

En la versión anterior(creo recordar) que  se creaba una dll por cada vista, en la versión actual han hecho un esfuerzo y se crea una por cada carpeta.

Vamos con las vistas y el resultado final del código generado.

 

   1: @model dynamic

   2: @foreach (dynamic item in Model)

   3: {   

   4:     <br />

   5:     <span>@item.Id</span>

   6:     <br />

   7:     <span>@item.Name</span>

   8: }

Está sencilla vista genera un archivo csharp con el siguiente código.

   1: #pragma checksum "C:JWTMvcApplication6MvcApplication6ViewsHomeContact.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "8C64704206437FFED454D2F32D27976E1F09CE01"

   2: //------------------------------------------------------------------------------

   3: // <auto-generated>

   4: //     Este código fue generado por una herramienta.

   5: //     Versión de runtime:4.0.30319.18052

   6: //

   7: //     Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si

   8: //     se vuelve a generar el código.

   9: // </auto-generated>

  10: //------------------------------------------------------------------------------

  11:  

  12: namespace ASP {

  13:     using System;

  14:     using System.Collections.Generic;

  15:     using System.IO;

  16:     using System.Linq;

  17:     using System.Net;

  18:     using System.Web;

  19:     using System.Web.Helpers;

  20:     using System.Web.Security;

  21:     using System.Web.UI;

  22:     using System.Web.WebPages;

  23:     using System.Web.Mvc;

  24:     using System.Web.Mvc.Ajax;

  25:     using System.Web.Mvc.Html;

  26:     using System.Web.Optimization;

  27:     using System.Web.Routing;

  28:     using WebMatrix.Data;

  29:     using WebMatrix.WebData;

  30:     using Microsoft.Web.WebPages.OAuth;

  31:     using DotNetOpenAuth.AspNet;

  32:     

  33:     

  34:     public class _Page_Views_Home_Contact_cshtml : System.Web.Mvc.WebViewPage<dynamic> {

  35:         

  36: #line hidden

  37:         

  38:         public _Page_Views_Home_Contact_cshtml() {

  39:         }

  40:         

  41:         protected ASP.global_asax ApplicationInstance {

  42:             get {

  43:                 return ((ASP.global_asax)(Context.ApplicationInstance));

  44:             }

  45:         }

  46:         

  47:         public override void Execute() {

  48:             

  49:             #line 2 "C:JWTMvcApplication6MvcApplication6ViewsHomeContact.cshtml"

  50:  foreach (dynamic item in Model)

  51: {   

  52:  

  53:             

  54:             #line default

  55:             #line hidden

  56: BeginContext("~/Views/Home/Contact.cshtml", 56, 12, true);

  57:  

  58: WriteLiteral("    <br />rn");

  59:  

  60: EndContext("~/Views/Home/Contact.cshtml", 56, 12, true);

  61:  

  62: BeginContext("~/Views/Home/Contact.cshtml", 68, 10, true);

  63:  

  64: WriteLiteral("    <span>");

  65:  

  66: EndContext("~/Views/Home/Contact.cshtml", 68, 10, true);

  67:  

  68: BeginContext("~/Views/Home/Contact.cshtml", 79, 7, false);

  69:  

  70:             

  71:             #line 5 "C:JWTMvcApplication6MvcApplication6ViewsHomeContact.cshtml"

  72:      Write(item.Id);

  73:  

  74:             

  75:             #line default

  76:             #line hidden

  77: EndContext("~/Views/Home/Contact.cshtml", 79, 7, false);

  78:  

  79: BeginContext("~/Views/Home/Contact.cshtml", 86, 9, true);

  80:  

  81: WriteLiteral("</span>rn");

  82:  

  83: EndContext("~/Views/Home/Contact.cshtml", 86, 9, true);

  84:  

  85: BeginContext("~/Views/Home/Contact.cshtml", 95, 12, true);

  86:  

  87: WriteLiteral("    <br />rn");

  88:  

  89: EndContext("~/Views/Home/Contact.cshtml", 95, 12, true);

  90:  

  91: BeginContext("~/Views/Home/Contact.cshtml", 107, 10, true);

  92:  

  93: WriteLiteral("    <span>");

  94:  

  95: EndContext("~/Views/Home/Contact.cshtml", 107, 10, true);

  96:  

  97: BeginContext("~/Views/Home/Contact.cshtml", 118, 9, false);

  98:  

  99:             

 100:             #line 7 "C:JWTMvcApplication6MvcApplication6ViewsHomeContact.cshtml"

 101:      Write(item.Name);

 102:  

 103:             

 104:             #line default

 105:             #line hidden

 106: EndContext("~/Views/Home/Contact.cshtml", 118, 9, false);

 107:  

 108: BeginContext("~/Views/Home/Contact.cshtml", 127, 9, true);

 109:  

 110: WriteLiteral("</span>rn");

 111:  

 112: EndContext("~/Views/Home/Contact.cshtml", 127, 9, true);

 113:  

 114:             

 115:             #line 8 "C:JWTMvcApplication6MvcApplication6ViewsHomeContact.cshtml"

 116: }

 117:             

 118:             #line default

 119:             #line hidden

 120:         }

 121:     }

 122: }

Dónde podemos ver que heredamos de WebViewPage<TModel> y el parametro TModel es dynamic, pero aún no hemos acabado puesto que ese dynamic vamos a ver como se convierte object, creo que al igual que cualquier dinamico y a la postre dar la razón a EduardSonrisa.

Para ello con una herramienta como ILSpy .NET Decompiler, vamos a abrir las dll’s que se generan por cada carpeta de vistas de nuestro proyecto MVC4 y localizar la vista en mi caso _Page_Views_Home_Contact_cshtm y centrarnos en dos puntos.

1. Nuestro TModel ya no es dynamic es object(Eduard que listo eres).

2. Es digno de observar el método execute y todo lo que un dynamic hace por nosotros.

   1: using Microsoft.CSharp.RuntimeBinder;

   2: using System;

   3: using System.Collections;

   4: using System.Runtime.CompilerServices;

   5: using System.Web.Mvc;

   6: namespace ASP

   7: {

   8:     [Dynamic(new bool[]

   9:     {

  10:         false,

  11:         true

  12:     })]

  13:     public class _Page_Views_Home_Contact_cshtml : WebViewPage<object>

  14:     {

  15:         [CompilerGenerated]

  16:         private static class <Execute>o__SiteContainer0

  17:         {

  18:             public static CallSite<Func<CallSite, object, IEnumerable>> <>p__Site1;

  19:             public static CallSite<Action<CallSite, _Page_Views_Home_Contact_cshtml, object>> <>p__Site2;

  20:             public static CallSite<Func<CallSite, object, object>> <>p__Site3;

  21:             public static CallSite<Action<CallSite, _Page_Views_Home_Contact_cshtml, object>> <>p__Site4;

  22:             public static CallSite<Func<CallSite, object, object>> <>p__Site5;

  23:         }

  24:         protected global_asax ApplicationInstance

  25:         {

  26:             get

  27:             {

  28:                 return (global_asax)this.Context.ApplicationInstance;

  29:             }

  30:         }

  31:         public override void Execute()

  32:         {

  33:             if (_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site1 == null)

  34:             {

  35:                 _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, IEnumerable>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(IEnumerable), typeof(_Page_Views_Home_Contact_cshtml)));

  36:             }

  37:             foreach (object item in _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site1.Target(_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site1, base.Model))

  38:             {

  39:                 base.BeginContext("~/Views/Home/Contact.cshtml", 56, 12, true);

  40:                 this.WriteLiteral("    <br />rn");

  41:                 base.EndContext("~/Views/Home/Contact.cshtml", 56, 12, true);

  42:                 base.BeginContext("~/Views/Home/Contact.cshtml", 68, 10, true);

  43:                 this.WriteLiteral("    <span>");

  44:                 base.EndContext("~/Views/Home/Contact.cshtml", 68, 10, true);

  45:                 base.BeginContext("~/Views/Home/Contact.cshtml", 79, 7, false);

  46:                 if (_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site2 == null)

  47:                 {

  48:                     _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, _Page_Views_Home_Contact_cshtml, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.InvokeSimpleName | CSharpBinderFlags.ResultDiscarded, "Write", null, typeof(_Page_Views_Home_Contact_cshtml), new CSharpArgumentInfo[]

  49:                     {

  50:                         CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),

  51:                         CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)

  52:                     }));

  53:                 }

  54:                 Action<CallSite, _Page_Views_Home_Contact_cshtml, object> arg_170_0 = _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site2.Target;

  55:                 CallSite arg_170_1 = _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site2;

  56:                 if (_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site3 == null)

  57:                 {

  58:                     _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site3 = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Id", typeof(_Page_Views_Home_Contact_cshtml), new CSharpArgumentInfo[]

  59:                     {

  60:                         CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)

  61:                     }));

  62:                 }

  63:                 arg_170_0(arg_170_1, this, _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site3.Target(_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site3, item));

  64:                 base.EndContext("~/Views/Home/Contact.cshtml", 79, 7, false);

  65:                 base.BeginContext("~/Views/Home/Contact.cshtml", 86, 9, true);

  66:                 this.WriteLiteral("</span>rn");

  67:                 base.EndContext("~/Views/Home/Contact.cshtml", 86, 9, true);

  68:                 base.BeginContext("~/Views/Home/Contact.cshtml", 95, 12, true);

  69:                 this.WriteLiteral("    <br />rn");

  70:                 base.EndContext("~/Views/Home/Contact.cshtml", 95, 12, true);

  71:                 base.BeginContext("~/Views/Home/Contact.cshtml", 107, 10, true);

  72:                 this.WriteLiteral("    <span>");

  73:                 base.EndContext("~/Views/Home/Contact.cshtml", 107, 10, true);

  74:                 base.BeginContext("~/Views/Home/Contact.cshtml", 118, 9, false);

  75:                 if (_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site4 == null)

  76:                 {

  77:                     _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site4 = CallSite<Action<CallSite, _Page_Views_Home_Contact_cshtml, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.InvokeSimpleName | CSharpBinderFlags.ResultDiscarded, "Write", null, typeof(_Page_Views_Home_Contact_cshtml), new CSharpArgumentInfo[]

  78:                     {

  79:                         CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null),

  80:                         CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)

  81:                     }));

  82:                 }

  83:                 Action<CallSite, _Page_Views_Home_Contact_cshtml, object> arg_2C9_0 = _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site4.Target;

  84:                 CallSite arg_2C9_1 = _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site4;

  85:                 if (_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site5 == null)

  86:                 {

  87:                     _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site5 = CallSite<Func<CallSite, object, object>>.Create(Binder.GetMember(CSharpBinderFlags.None, "Name", typeof(_Page_Views_Home_Contact_cshtml), new CSharpArgumentInfo[]

  88:                     {

  89:                         CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)

  90:                     }));

  91:                 }

  92:                 arg_2C9_0(arg_2C9_1, this, _Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site5.Target(_Page_Views_Home_Contact_cshtml.<Execute>o__SiteContainer0.<>p__Site5, item));

  93:                 base.EndContext("~/Views/Home/Contact.cshtml", 118, 9, false);

  94:                 base.BeginContext("~/Views/Home/Contact.cshtml", 127, 9, true);

  95:                 this.WriteLiteral("</span>rn");

  96:                 base.EndContext("~/Views/Home/Contact.cshtml", 127, 9, true);

  97:             }

  98:         }

  99:     }

 100: }

En pocas palabras ya tenemos al descubierto la magia de Razor y de Dynamic, vamos todo en unoSonrisa.

Como era algo que estaba en mente desde mis inicios públicos El respositorio genérico.Un Derrochador en épocas de crisis y últimamente mis dos grandes compañeros y amigos @XaviPaper y Carlos(Carrillo pa los amigos:), se me quejaban todos los días del follón de los dto’s a mí también me lo parecían, pero como los escriben ellos xDDDD.

Venga vamos a darle la solución y hacer de Eduard mi gatitoSonrisa.

La solución de Eduard es buenísima y denota eso, lo que es un maestro, pero le veo un problema dos iteraciones por los datos y a mi me gusta abusar el patrón “Engañabobos”.

Para ello no voy a crear un ExpandoObject sino una clase que hereda de DynamicObject y en esta reemplazar el método TryGetMember.

   1: public class Midinamico : DynamicObject

   2:    {

   3:        

   4:        public Midinamico()

   5:        {

   6:            

   7:        }

   8:        public Object Value { private get; set; }

   9:        public override bool TryGetMember(GetMemberBinder binder, out object result)

  10:        {

  11:            result = Value.GetType().InvokeMember(

  12:                binder.Name,

  13:                BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,

  14:                null,

  15:                Value,

  16:                null);

  17:  

  18:            // Always return true, since InvokeMember would have thrown if something went wrong

  19:            return true;

  20:        }

  21:    }

Y donde está la magia en el tercer parámetro de la llamada al método “InvoqueMember” ese flag de Reflection de toda la vida que hace que por mucho que declares algo como privado siempre habrá alguien que pueda acceder desde otra dll, cosas de .NetSonrisa.

BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic

Madre ese Public|NonPublic que peligro tieneSonrisa.

Y al final la solución completa.

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Data.Entity;

   4: using System.Dynamic;

   5: using System.Linq;

   6: using System.Reflection;

   7: using System.Text;

   8: using System.Threading.Tasks;

   9:  

  10: namespace Datos

  11: {

  12:     public class Context : DbContext

  13:     {

  14:         public Context()

  15:             : base("DefaultConnection")

  16:         {

  17:  

  18:         }

  19:         public DbSet<ClaseLarga> ClaseLarga { get; set; }

  20:  

  21:         public dynamic GetData()

  22:         {

  23:             return this.ClaseLarga.Select(x => new Midinamico() { Value = new { Id = x.Id, Name = x.Name } });

  24:         }

  25:     }

  26:     public class Midinamico : DynamicObject

  27:     {

  28:         

  29:         public Midinamico()

  30:         {

  31:             

  32:         }

  33:         public Object Value { private get; set; }

  34:         public override bool TryGetMember(GetMemberBinder binder, out object result)

  35:         {

  36:             result = Value.GetType().InvokeMember(

  37:                 binder.Name,

  38:                 BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,

  39:                 null,

  40:                 Value,

  41:                 null);           

  42:             return true;

  43:         }

  44:     }

  45:     public class ClaseLarga

  46:     {

  47:         public int Id { get; set; }

  48:         public string Name { get; set; }

  49:         public string MasCamposQueMeCanso { get; set; }

  50:     }

  51: }

Y nuestro controlador quedaría de esta forma.

   1: public ActionResult Contact()

   2: {

   3:    var ctx = new Context();          

   4:    return View(ctx.GetData());

   5:              

   6: }

P.D. El código genera un error en ejecución pero muy sencillo de solucionar y evidentemente nunca escribiría un código así sobre todo en el “Context”, pero eso os lo dejo a vosotros.

Gracias Sergio por destapar las ganas de volver a escribir y moraleja “No escribo un dto más en mi vida para pintar una vista”. Xavi Carri vosotros sí Sonrisa.

Eduard gracias!!! Como siempre un maestro que me ha hecho calentarme la cabeza para buscar otra alternativa.

Pero alguien pensaba que esto finalizaba. No me queda una parte.Toda la inspiración vino por este post Passing anonymous objects to MVC views and accessing them using dynamic, si nos fijamos en el final este señor utiliza la directiva @page de asp para decir a su vista de que clase hereda Inherits. ¿Tanto cuesta implementar esto en Razor?, porque hasta donde yo se todo depende de esa otra joyita que es el web.config de la carpeta donde están las vistas “View”

   1: <system.web.webPages.razor>

   2:    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

   3:    <pages pageBaseType="System.Web.Mvc.WebViewPage">

   4:      <namespaces>

   5:        <add namespace="System.Web.Mvc" />

   6:        <add namespace="System.Web.Mvc.Ajax" />

   7:        <add namespace="System.Web.Mvc.Html" />

   8:        <add namespace="System.Web.Optimization"/>

   9:        <add namespace="System.Web.Routing" />

  10:      </namespaces>

  11:    </pages>

  12:  </system.web.webPages.razor>

En definitiva si esto existiese en Razor yo podría escribir mis consultas linq limpias sin extensores como en el caso de Eduard ni como en mi caso creando un objeto que hereda de de DynamicObject.

Conclusiones

Que me ha quedado una duda, esto servirá para algo. Puesto que lo que estoy reclamando a gritos son dos pequeñas modificaciones una en MVC y otra en Visual Studio(Olvídate de los internal o déjame que los míos los genere yo, aunque sean anónimos, les pongo EditorBrowsableAttribute para que no los vea nadie en el intellisense), pero  como yo los quiera “my guardian” y los quiero “public” que me ahorran tiempo y me quitan la desesperación 🙂

Avanzamos de Sql Inyection a SSJS(server Side javascript-inyection)

De esos días que a uno le quedan ganas de pasarse por el foro de c# veo esta pregunta y sus respectivas respuestas y ante eso reacciono e intenta evitar situaciones comprometidas.

Resultado  como se puede ver ni puto caso:). Vamos a ver si lo convencemos.

http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/55061187-d5ca-4d01-b5f6-6b6c9bd7ab86

Pero para ello, no lo voy a hacer con Sql Server, Oracle, My Sql,etc. Lo vamos a hacer con  Mongo y pronto podréis observar  como bien dicen que no todo lo que reluce es oro.

Lo digo por estas cosas que se pueden leer acerca de Mongo y bb.dd no Sql.

-No Sql Inyection

-No Join

-No schemas

Efectivamente toda una maravilla, excepto que como a todos siempre se nos olvida algo. Y es entonces cuando crean una bonita sentencia que no es otra $exists. Lo mismo la crearon por aquello de no tener shemas y podamos hacer query de aquellos documentos que no contiene tal campo, pero claro también podemos hacer query de todos los campos de un formulario donde $exists es verdadero y es aquí donde empieza la fiesta.

Pasitos.

Vamos a ser modernos y además de mongo hoy nos toca todo con Node. Con .Net y c# no tienes este problema, excepto … y ya lo contaré al final.

1. Creamos una carpeta llamada “nodemongo”. Tenemos node y mongodb instalado a jugar si no es así ya sabes Node y Mongo

2. instalamos express para ello sencillo “npm install express”

3. instalamos jade. Una joyita y de las buenas(Me parece buenísimo). “npm install jade”

4. instalamos mongo para node js “npm install mongodb”

5. ejecutamos la siguiente sentencia desde nuestra carpeta “express”

6. Esto nos va a crear una estructura de directorios con vistas,controladores(routes) y public(css y javascript). Es decir todo el esqueleto básico de una app con express.

7. Como somos tan chulos y no hacemos caso a aquello de utiliza estándares nos creamos una tabla(collection) en mongo db para almacenar nuestros login.

desde mongo ejecutamos lo siguiente.

db.login.insert({user:’admin’,password:’123456’})

Si posteriormente ejecutamos db.login.find() podemos ver que nos hemos ahorrado un montón de cosas.

-Create Table

-Insert

-etc,etc

Es decir lo acabas de comprar. Todo en uno y barato:)

Bueno llegado a este punto ya tenemos la suficiente infraestructura para modificar todo aquello que nos aporta express y no es más que modificar una vista para que solicite el login a nuestro sitio y otra página que nos dirá “estás conectado”  o “me quieres engañar”.

8. En las vista vamos a index.jade y la dejamos de la siguiente forma.

extends layout

block content
h1
p Welcome
form(action="/users",method='post')
input(type="text",name="user")
br
input(type="text",name="password")
br
input(type="submit",value="submit")

9. Modificamos el archivo app.js creado en los puntos 5 y 6 y lo dejamos de esta forma.


/**
* Module dependencies.
*/

var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path');

var app = express();

app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
app.use(express.errorHandler());
});

app.get('/', routes.index);
app.post('/users', user.list);

http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});

Nuestra app responde a “/” para mostrar nuestro formulario y un post en “/users” para recoger los datos de autenticación.

10. Modificamos en la carpeta “routes” el archivo “users.js” y lo dejamos de la siguiente forma. Ahora entra mongo a realizar sus buenas labores(salvarnos de los join, será que no sabes como funcionanSonrisa.


/*
* GET users listing.
*/

exports.list = function(req, res){
var query = {'user':req.body.user,password:req.body.password};

var mongo = require('../node_modules/mongodb/index.js'),
Server = mongo.Server,
Db = mongo.Db;

var server = new Server('localhost',27017,{safe:false});
var db = new Db('test', server);

db.open(function(err, db) {
if(!err) {
db.collection('login', function(err, collection) {
collection.find(query).toArray(function(err, items) {
if(items.length)
res.send("conectado");
else
res.send("me estás engañando");
}
);

});
}
});
};

Bueno con estos pasos tenemos nuestro sistema preparado para hacer login, fácil no?

11. Ahora con esa maravilla que es node vamos a ejecutar lo siguiente.

node app.js

12. Desde el explorador tecleamos la siguiente url.

localhost:3000

13. Este es el número que me gustaSonrisa.  Jo tenemos un formulario para hacer login y una bb.dd con dos patadas. Esto es una maravilla!!!.

Bueno ahora toca ser malo y vamos a hacer lo siguiente.

1. Lo primero que vemos es una página fea, pero no os preocupéis que después llega el diseñador y todo arreglado.

login

2. Lo primero es conocer que hace esto . Sencillo tecleamos en usuario “admin” y en password lo mismo por si tenemos suerte. El resultado es evidente.

“me estás engañando”. Bien!!!!

3. Ahora vamos a ver que ha pasado entre bastidores, para ello F12 y developer tool->Network.

Network

 

El form data deja claro que hay dos campos uno llamado “user” y el otro “password”, nada fuera de lo normal.

4. Vamos a putear….

Lo más sencillo del mundo no comprobar que nuestro usuario y password es el mismo que nos pasan, es mas optimo hacer count(*) Sonrisa.

if(items.length)
res.send("conectado");
else
res.send("me estás engañando");
5. Vamos a cambiar de ventana en las dev tools y nos vamos a elements el objetivo no es otro que dejar nuestro html de la siguiente forma.
 
html
 
Algo sencillo cambiar el name del input user por user[$exists] y lo mismo con password para dejarlo como password[$exists]. Seguro que si lo dejo de esta forma alguno puede llegar hasta este punto y pensar que le estoy tomando el pelo, Nooooo!!!.
 
Es bastante más sencillo que teclear todo lo que nos dice esta pagina de las 1000 que hay por internet http://www.unixwiz.net/techtips/sql-injection.html y además se ha pasado de moda:). Es más sencillo teclear lo siguiente, sabes que? no te lo imaginas a estas alturas:). De lo más sencillo.
 
– En el campo user tecleamos “true”
– En el campo password tecleamos “true”.
 
Si al final lo que queremos es que exista el campo “user”  y el campo “password” en nuestra collection(tabla), menuda maravilla. La respuesta es clara acabas de pasar de impostor a ser el dueño del mambo.
true
 
Quieres ser el señor, pulsa en "submit”.
Conclusiones.
 
Si alguien está libre que tire la primera piedra a todas estas preguntas.
 
1. Quien no utiliza los mismos nombres de campos en sus formularios que en la bb.dd, yo diría que todos.
 
2. Quien no ha montado alguna vez un sistema de login así. Yo diria que todos.
 
3. Quien no se deja una entrada sin validar. Yo diría que alguna vez todos.
 
4. Quien ha creado para mongo un anti$exists. Ninguno.
 
5.  Como respuesta a los de c# estáis libres de pecado puesto que vuestro user y password es string (TIPADO) y mongo ante esta query
 
db.login.finc({user:'{$exists:true}’,password:'{$exists:true}’})
 
Responde con 0 registros( que sacrilegio registros, ¿no deberían ser documentos? y en vez de ORM ODM Sonrisa). Señores por una vez no somos los inseguros. Excepto que utilices el magnifico $where
 
6. Quien no ha validado basándose en Count y no hacer comprobaciones posteriores.
 
7. Podemos ver como cada día que nuestra profesión va en continua evolución hasta encontrar eso “la panacea”.
 
 
 
 

Node debug. De la desilusión al conformismo

Últimamente se han puesto de moda una serie de tendencias que no se donde nos van a conducir, pero porque no probar.

La verdad que me inspiraron estos post que me encontré del amigo Juanma (@gulnor).

http://blog.koalite.com/2011/12/mas-sobre-el-tutorial-node-js-express-jquery/

Así como ver esto en el build 2012.

Para ello no me plantee grandes cosas sino algo muy concreto quiero depurar el ejemplo que hay en la página de Node el típico “hello world”

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Worldn');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
He dicho claro no quiero hacer el “Hello World” quiero depurarlo. Ya veréis en que se convierte esto.
 
Primer Intento (Depurar con node).
Antes de empezar os recomiendo esta lectura
 

1. Si no tenéis node instalado lo podéis hacer desde

http://nodejs.org/download/

2. Abrimos el Notepad.

3. Copiamos el código y lo guardamos en una carpeta yo para el ejemplo he creado una con nombre “debugnode”. La extensión del archivo es indiferente un .txt también funciona aunque lo normal es que lo guardes con extensión .js. Te lo dejo a tu elección.

4. Ejecutamos el command prompt de node.

 

Command

No esperéis grandes cosas, alguno puede intuir que se le va a abrir una bonita pantalla negra como la vida mismoSonrisa. Y preparados para teclear muchas veces “Ctrl+C” o si te agobias más fácil la cruz roja.

5. Tecleamos el siguiente comando “node debug server.js” y el resultado es la siguiente ventana.

debug1

Anda no te quejes que algo de color hay. La verdad que da miedo y la sensación es como de impotencia. Primer “Ctrl+C”. bueno como no vas a romper nada vuelve a entrar si haces lo mismo que yo hice y piensa un poco. Tienes dos caminos uno imprimir la página de la documentación y tenerla siempre a tu derecha. La otra es tener un poco de imaginación e intentar pulsar algo,  que te parece “help” o “quit” o de nuevo “Ctrl+c” o bien eso la cruz Roja.

Si llegado a este punto no has cerrado y te has ido a tomar una caña al pulsar “help” te aparece lo siguiente.

debug2

Es decir comandos bastante intuitivos y con una gran similitud con el debug de Visual Studio.

Node Visual Studio
cont o c F5
next n F10
step p F11
SetBreakPoint sb (nº linea) F9

 

Al resto le podeís buscar vosotros su pareja en visual studio.

4. Vamos a jugar con algunos comandos

a) pulsamos list() muestra las 6 primeras lineas de código.

b) una vez que vemos todo el código este concretamente tiene 6, imaginemos uno con 2000, menudo sudor frio te puede dar. tecleamos sb(3) y ya tenemos nuestro primer breakpoint, como siempre intuitivo y por eso nos muestra un “*” a la izquierda de linea 3.

c) pulsamos c para que se pueda ejecutar todo el código y podamos invocar nuestro “hello world” desde el explorador.

d) Abrimos en el explorador la siguiente url “http://localhost:1337” y vemos que el explorador se queda dando vueltas y sin responder, no os preocupéis. Estamos en el breakpoint.

e) ahora quiero ver el valor de req. Request para los amigos. sencillo pulsas repl y ya tienes un magnifico entorno para evaluar expresiones.

    Aqui te invito a que ejecutes los siguientes comandos.

    req.headers.

    req.method.

    req.url.

El resultado obtenido es el esperado.

debug4 

Estamos viendo las cabeceras http el metodo y la url que nos invoca, como podéis observar todo un logro.

Seguro que alguno a estas altura piensa lo mismo que yo y se siente desilusionado con tanto bombo para tan poca cosa o tiene las mismas sensaciones que yo una buena y otra mala.

La buena,  por un momento me sentí con 20 años menos en plenos 90 , rejuvenecido con ganas y con una herramienta mejor que la utilizaba entonces que no era otra que poner banderas y compilar, porque ni depurador había. Ves hemos progresado eres más joven y más listo. Ya tienes depurador.

La mala es que me levante de la silla y me fui corriendo al espejo a ver si de verdad tenía 20 años menos y efectivamente estaba en 2012 utilizando algo que ya había utilizado en los 90. De hay mi gran desilusión.

Pero como se puede hablar bien de esto, esa debería haber sido mi siguiente reacción y un helper a la cruz roja, pero como ha tozudo no me gana nadie, me di otra oportunidad.

Segundo Intento (node-inpector).

Buscando por la web me encontré con esta otra herramienta https://github.com/dannycoates/node-inspector.

Bueno piensa que si la gente habla bien de node algo tiene que tener. Vamos a hacer lo siguiente e instalamos node-inspector.

1. Ejecutamos el siguiente comando desde el directorio npm install node-inspector.

npm es un gestor de paquetes de node parecido a nuget.

2. Ejecutamos el siguiente comando.

node –debug server.js y obtenemos el siguiente resultado.

nodeinspector1

3. Abrimos otra ventana con el command prompt y ejecutamos lo siguiente comando después de lógicamente entrar en el directorio “nodedebug” o en el que hayas creado.

“.node_modules.binnode-inspector &”

El resultado es el siguiente

nodeinspector2

Bueno y ahora como depuro, pues sencillo lee los mensajes de ambas pantallas y tira de tu imaginación o de alguna que otra búsqueda por internet. Piensa un poco tienes primero un mensaje diciendo que tu servidor esta corriendo en el puerto 1337 por otra parte un mensaje de alguien escuchando en el puerto 5858 y en la última ventana un mensaje que me dice que vaya a la dirección tal y que empezare a hacer debugging.

Pues vamos a hacerle caso.

Abrimos chrome o cualquier explorador webkit(yo solo lo he probado en chrome) y hacemos caso a la pantalla negra que para eso es vieja y sabia. El resultado sería el siguiente.

nodeinspector3

La primera exclamación es “jo que bueno” si son las developer tool de chrome. No te confundas es una buena pagina web que hace lo mismo o parecido a las devtools. Vamos a verlo para ello podemos pulamos F12 y nos aparecen las verdaderas devtools y ahora para picaros un poquito más quiero que pongáis un breakpoint no en el primer texto que vemos sino en la linea 13 del archivo overrides.js tal y como os muestro.

socket

y ahora un breakpoint en el impostor el resultado es que el legitimo se para en la linea 13 y podemos ver que recibe un mensaje al servidor con el siguiente formato.

«{«id»:25,»result»:{«breakpointId»:»1″,»locations»:[{«lineNumber»:2,»columnNumber»:6,»scriptId»:»23″}]}}»

Ya sabeís a jugar o mejor a estudiar si queréis haceros vuestro propio depurador .

DebuggerProtocol.

El código fuente de esta pagina web no devtool lo tenéis en la carpeta “node_modulesnode-inspectorfront-end”

Bueno parece que esto mejora, pero si analizamos  necesito 4 ventanas para depurar dos negras y otras dos con el explorador chorme que lo mismo no lo quiero instalar y otra con el explorador que utilizas para hacer peticiones. He aprovechado para conciliar a dos rivales IE10 y Chrome todo ello para depurar una app desarrollada en node. Todo un logro por mi parte sentar en la misma mesa a los dosSonrisa.

nodeinspector4

Desilusionado pues igual que yo pero un poco menos.

Tercer intento(Microsoft viene a salvarnos).

Ante estas dos frustrantes actuaciones y recordando un poco me vino a la cabeza que webMatrix soportaba node. Pues vamos a ello.

1. Descarga WebMatrix.

2. Desístala iisnode si lo tienes instalado, creo que lo instala webMatrix en algún momento, no estoy seguro.

3. Instala la versión de iisnode desde esta url

https://github.com/tjanczuk/iisnode/downloads

Yo concretamente instale esta para una máquina de 32

https://github.com/downloads/tjanczuk/iisnode/iisnode-iis7express-v0.1.13-x86.msi

Y comprueba si quieres depurar node  con el siguiente comando

%localappdata%iisnode existe la carpeta iisnode en la ruta y que en vez de encontrarte con un archivo js te encuentras con una dll llamada iisnode-inspector.dll.

4. Creo que tienes que hacer algún cambio en el archivo applicationhost.config.

%userprofile%documentsIISExpressconfigapplicationhost.config

concretamente este

<section name=»handlers» overrideModeDefault=»Allow» /> su default value es “Deny”

5. Por último tienes que cambiar el webConfig de las plantillas que trae webMatrix por defecto con lo siguiente.

<!-- 
This configuration file is required if iisnode is used to run node processes behind
IIS or IIS Express. For more information, visit:

https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->
<configuration>
<system.webServer>
<handlers>
<!-- indicates that the server.js file is a node.js application to be handled by the iisnode module -->
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Don't interfere with requests for logs -->
<rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^[a-zA-Z0-9_-]+.js.logs/d+.txt$"/>
</rule>
<!-- Don't interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js/debug[/]?"/>
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<!-- All other URLs are mapped to the Node.js application entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
<iisnode>
debuggerPortRange="5058-6058"
debuggerPathSegment="debug"
maxNamedPipeConnectionRetry="3"
namedPipeConnectionRetryDelay="2000"
</iisnode>
</system.webServer>
<system.web>
</configuration>

A partir de este momento ya podemos ejecutar y depurar nuestra app node con webMatrix. para ejecutarla simplemente ir al sitio de webMatrix tal y como muestro.

 
webmatrix

Desde otra ventana del depurador podeís ejecutar la siguiente url y a depurar “http://localhost:33732/server.js/debug/”.

Como podéis ver algo que no te lleva unas pocas horas y es por eso por lo que uno se encuentra al final ya no desilusionado sino conformista, porque aun habiéndolo logrado esto no es ninguna maravilla hemos pasado de 4 a 3 ventanas y un entorno más amigable, pero con deficiencias en Intellisense y demás…

Otras opciones que podéis probar son.

Cloud 9 ide

WebStorm

o incluso el propio eclipse https://github.com/joyent/node/wiki/Using-Eclipse-as-Node-Applications-Debugger.

Pero vamos nada parecido a lo que Microsoft podría hacer con Node y que sería integrarlo en Visual Studio.

Conclusiones.

Que tristemente Microsoft apuesta por node porque realmente esta Azure e interesa. Pero veo que el esfuerzo realizado no compensa lo que node me da frente a lo que me da MVC como desarrollador .net aunque es algo que no tenemos que dejar pasar y seguir muy de cerca.

Espero dos cosas.

1. Que Microsoft lo integre en Visual Studio.

2. Que deje node donde esta que como servicio gratuito ya ha hecho creo yo lo que le corresponde. Dar herramientas medianamente decentes. Y que apueste por TypeScript y haga su propio motor con “chakra”.

Estoy soñando o me lo estoy creyendoSonrisa