martes, 29 de noviembre de 2016

Probando Aplicaciones Windows con WinAppDriver

La conferencia de desarrolladores de Microsoft de este año, la Connect(); //2016 ha estado llena de anuncios como la entrada de Microsoft en la Funcación Linux como miembro platino, la unión de Google y Samsung al grupo de dirección técnica de .Net Foundation, o la presentación del Visual Studio para Mac lo que más me llamó la atención fue una pequeña demo donde probaban aplicaciones de Windows.

Los más veteranos del lugar recuerdan Selienum, se utilizaba para hacer pruebas de interfaz de usuario web, elegías el navegador desde el que se quería probar y se ejecutaba la pruebas. Pues WinAppDriver utiliza Appium, una herramienta inspirada en Selenium que monta un servidor web  que es quien realmente ejecuta la aplicación y que expone un API Web con el que nos podemos comunicar. De tal manera que podemos usarlo con prácticamente cualquier tecnología (.Net, Java, etc)

En este post voy a utilizar xUnit y voy a hacer una prueba contra el notepad.
Antes de abrir el Visual Studio hay que instalar WinAppDriver, que se encuentra en  https://github.com/Microsoft/WinAppDriver/releases.
Una vez listo, creo un proyecto en Visual Studio de librería y añado la dependencia a xUnit y otra a Appium.

Para el test quiero probar que si se pulsan las teclas aparecen en pantalla. Lo primero que tengo que hacer en el test configurar appium y decirle que inicie la aplicación de notepad. Esto lo hago asi:

	//Ejecutar notepad
	DesiredCapabilities appCapabilities = new DesiredCapabilities();
	appCapabilities.SetCapability("app", @"C:\Windows\System32\notepad.exe");
	var NotepadSession = new IOSDriver(new Uri("http://127.0.0.1:4723"), appCapabilities);

¿Pero hacemos referencia a la zona editable del Notepadd. En Selenium se hacía referencia al DOM del HTML y teníamos métodos para identificar controles por nombre o id, aquí también los tenemos ¿pero como sabemos los nombres de los ids? La respuesta es inspect.exe que viene con el Windows SDK. Si tienes instalado el Visual Studio 2015 posiblemente lo tengas instalado ya que se instala por defecto y lo puedes encontrar en C:\Program Files (x86)\Windows Kits\10\bin\x86. Ejecutamos el notepad y después el inspect y veremos como cuando pulsamos en cualquier elemento del la pantalla, en el inspect aparecen las propiedades de dicho elemento. Por ejemplo si pulsamos en la zona editable del documento del notepadd aparecerá esto: image

Si nos fijamos vemos que nos muestra como ClassName un “Edit” asi que vamos buscar ese elemento por ClassName. Asi que hacemos las acciones que queremos realizar en el test y anotamos los controles y como los podemos identificar. Por ejemplo el test que hice quedó así:

    public class WindowsAppsTest
    {
        [Fact]
        public void LaunchNotepad()
        {
            const string texto = "SHAZAM!";
            //Ejecutar notepad
            DesiredCapabilities appCapabilities = new DesiredCapabilities();
            appCapabilities.SetCapability("app", @"C:\Windows\System32\notepad.exe");
            var NotepadSession = new IOSDriver(new Uri("http://127.0.0.1:4723"), appCapabilities);
            
            //Escribir algo 
            IOSElement documentoNotepad = NotepadSession.FindElementByClassName("Edit");
            documentoNotepad.SendKeys(texto);
            Assert.Equal(texto, documentoNotepad.Text);

            NotepadSession.FindElementByName("Cerrar").Click();

            NotepadSession.FindElementByName("No guardar").Click();
        }
        
    }

  • Abrimos la aplicación, identificamos la zona del documento en notepad con FindElementByClassName("Edit")
  • después escribimos el texto de prueba
  • comprobamos que el texto que enviamos está en pantalla con Assert.Equal(texto, documentoNotepad.Text);
  • hacemos click en el boton cerrar
  • hacemos click en el botón no guardar


Ya tenemos nuestro test listo, ahora lo que tenemos que hacer antes de lanzar nuestro test es iniciar nuestro servidor de appium, que arrancamos ejecutando WinAppDriver.exe que instalamos anteriormente y ya estamos listos para lanzar la prueba.
Es un poco trabajoso tener que hacer la prueba “manual” e ir anotando los controles involucrados buscar como se identifican, pero parece un conocimiento que los expertos en pruebas deberían tener. Si trabajáis en una gran empresa no estaría mal que hubiese un departamento de testing y sería ese departamento el encargado de diseñar y programar estas pruebas. Como es sabido, no es bueno que los propios desarrolladores prueben sus desarrollos. No porque no deben de hacer (que deben) si no porque están viciados con su perspectiva.
Os dejo un par de enlaces de la documentación oficial con pruebas en java y en .net

¿Qué opináis? ¿merece la pena?

Despues de leer el post y antes de publicarlo, decidí hacer un video para mostrar la ejecución del test que programamos

domingo, 27 de noviembre de 2016

Spring Boot Rules!!!

imageHace unos meses cuando publicamos el hola mundo de Asp.Net Core Pedro me comentó que era muy parecido a Spring Boot.
Recientemente hemos publicado un hola mundo en Java con Maven, asi que como continuación vamos a hacer un hola mundo con una aplicación web utilizando Sping Boot.

Antes de empezar y para los que no lo conozcan ¿qué es Spring y qué es Spring Boot?
Cuando comencé a oír de Spring se utilizaba como inyección de dependencias, alrededor de este fueron creciendo utilidades y diversificando las técnicas utilizadas (programación orientada a aspectos, cachés, transaccionalidad, etc) con el tiempo la comunidad fue madurando y ofreciendo frameworks para desarrollar aplicaciones web, servicios, etc. Sping Boot es una forma concreta de hacer aplicaciones web, con todo el soporte de Spring basado en convenciones y reduciendo en la medida de lo posible las configuraciones (Convention over configuration)

Hagamos una aplicación web. Aunque Spring Boot ofrece un inicializador de proyectos en su web, lo vamos a hacer a mano, así que empezaremos por el POM, donde incluireos el proyecto padre spring-boot-starter-web, la dependencia con spring-boot-starter-web y el plugin spring-boot-maven-plugin

<?xml version="1.0" encoding="UTF-8"?>
<project 
    xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.nocompila</groupId>
    <artifactId>spring-boot-rules</artifactId>
    <version>0.1.0</version>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.2.RELEASE</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
    <properties>
        <java.version>1.8</java.version>
    </properties>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

A diferencia del proyecto anterior, no hemos configurado el tipo de empaquetado, ni un plugin con la clase de contiene el public static void main. El plugin Spring Boot Maven ya se encarga de todo eso sin configurarlo, creando un jar que contiene todos los jars encontrados en el classpath y marcando el jar como ejecutable detectando.

Los siguiente que haremos es crear el directorio del código md src\main\java\rules directorio donde crearemos la siguiente clase:

package rules;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;

@EnableAutoConfiguration
public class SpringBootRules{
    public static void main (String [] args) throws Exception{
        SpringApplication.run(SpringBootRules.class, args);
    }
}

Con la anotación @EnableAutoConfiguration le decimos a Spring que configure la aplicación, como detecta la dependencia spring-boot-starter-web asume que estamos haciendo una aplicación web y configura un Tomcat y Spring MVC. Lo cierto es que al añadir spring-boot-starter-web se añaden muchas dependencias de las que no somos conscientes. Si ejecutamos mvn dependency:tree veremos que tenemos todas estas dependencias:

[INFO] com.nocompila:spring-boot-rules:jar:0.1.0
[INFO] \- org.springframework.boot:spring-boot-starter-web:jar:1.4.2.RELEASE:compile
[INFO]    +- org.springframework.boot:spring-boot-starter:jar:1.4.2.RELEASE:compile
[INFO]    |  +- org.springframework.boot:spring-boot:jar:1.4.2.RELEASE:compile
[INFO]    |  +- org.springframework.boot:spring-boot-autoconfigure:jar:1.4.2.RELEASE:compile
[INFO]    |  +- org.springframework.boot:spring-boot-starter-logging:jar:1.4.2.RELEASE:compile
[INFO]    |  |  +- ch.qos.logback:logback-classic:jar:1.1.7:compile
[INFO]    |  |  |  +- ch.qos.logback:logback-core:jar:1.1.7:compile
[INFO]    |  |  |  \- org.slf4j:slf4j-api:jar:1.7.21:compile
[INFO]    |  |  +- org.slf4j:jcl-over-slf4j:jar:1.7.21:compile
[INFO]    |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.21:compile
[INFO]    |  |  \- org.slf4j:log4j-over-slf4j:jar:1.7.21:compile
[INFO]    |  +- org.springframework:spring-core:jar:4.3.4.RELEASE:compile
[INFO]    |  \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO]    +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.4.2.RELEASE:compile
[INFO]    |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.6:compile
[INFO]    |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.6:compile
[INFO]    |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.6:compile
[INFO]    +- org.hibernate:hibernate-validator:jar:5.2.4.Final:compile
[INFO]    |  +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO]    |  +- org.jboss.logging:jboss-logging:jar:3.3.0.Final:compile
[INFO]    |  \- com.fasterxml:classmate:jar:1.3.3:compile
[INFO]    +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.4:compile
[INFO]    |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.4:compile
[INFO]    |  \- com.fasterxml.jackson.core:jackson-core:jar:2.8.4:compile
[INFO]    +- org.springframework:spring-web:jar:4.3.4.RELEASE:compile
[INFO]    |  +- org.springframework:spring-aop:jar:4.3.4.RELEASE:compile
[INFO]    |  +- org.springframework:spring-beans:jar:4.3.4.RELEASE:compile
[INFO]    |  \- org.springframework:spring-context:jar:4.3.4.RELEASE:compile
[INFO]    \- org.springframework:spring-webmvc:jar:4.3.4.RELEASE:compile
[INFO]       \- org.springframework:spring-expression:jar:4.3.4.RELEASE:compile

Esta vez para compilar y ejecutar vamos a utilizar mvn spring-boot:run. Podemos hacer eso porque hemos añadido como padre de la aplicación spring-boot-starter-parent en el POM. Al arrancar la aplicación veremos que al iniciar el programa se inicia un Tomcat en el puerto 8080 de nuestra máquina. Pero si pedimos una página nos dará un error el siguiente error porque no tenemos nada para responder:
image

Lo único que nos falta por hacer es poner una respuesta en algún sitio, como este es un primer ejemplo, vamos a intentar hacer todo lo más pequeño posible, así que lo vamos a hacer todo en la misma clase para que se vea bien (pero no lo hagáis en vuestro código de producción)

package rules;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class SpringBootRules{
    public static void main (String [] args) throws Exception{
        SpringApplication.run(SpringBootRules.class, args);
    }

    @RequestMapping("/**")
    String allSite (){
        return "Spring Boot Rules!!!";
    }
}

Como hemos dicho anteriormente, Sping Boot nos ha añadido SpringMVC, así que vamos a utilizar este framework. Marcamos la clase no la anotación @RestController (una meta anotación compuesta por @Controller y @ResponseBod) y añadimos un método que devuelve un String anotado con @RequestMapping(“/**”) con lo que le estamos diciendo a Spring que todas las peticiones web serán respondidas por este método.
Si volvemos a ejecutar la aplicación ahora y pedimos una página tendremos una respuesta mejor:
image

Como mencionamos anteriormente, al usar spring-boot-maven-plugin, no tenemos que configurar nada, y en el directorio target tendremos dos ficheros un jar súpervitaminado (“uber-jar”) que contiene todas las dependencias del classpath y otro finalizado jar.original que no contiene las dependencias.
El .jar lo podemos ejecutar con java –jar, por ejemplo desde el directorio raíz con java -jar target\spring-boot-rules-0.1.0.jar

En resumen, hemos hecho una aplicación de consola con un public static void main que arranca una aplicación web iniciando un Tomcat… ¿No suena esto parecido al hola mundo que hicimos con Asp.Net Core? ¿Vosotros qué creéis?
Ahh se me olvidaba, el código completo lo tenéis en GitHub

viernes, 11 de noviembre de 2016

Java Maven: Hello World

No solo de .Net vive el blog, así que tal como estamos haciendo tutoriales básicos sobre .Net Core y Asp.Net Core, vamos a hacer pequeños post introductorios al mundo Java.
Al igual que hacemos con los de .Net evitaremos en la medida de lo posible usar IDEs y utilizar editores de texto para comprender mejor lo que sucede entre bambalinas.

Así que empezaremos con lo básico, el típico hola mundo de Java con Maven. Para los desarrolladores .net que no sepan que es Maven decirles que es una herramienta que sirve para compilar y empaquetar (una especie de MsBuild) unido a un gestor de paquetes (como NuGet) y que es extensible mediante plugins, creación de distintos tipos de proyectos mediante arquetipos, etc. Lo mejor es echarle un vistazo al sitio del proyecto y ver sus características.

Maven se basa en una serie de convenciones (que se pueden cambiar mediante configuración).
La primera convención de Maven es la estructura de directorios del proyecto: El código fuente del proyecto se encuentra en src/main/java/ Así que comenzamos el proyecto creando directorios: md src\main\java\hello.

Lo siguiente que vamos a hacer es crear un fichero POM, que es el fichero de configuración de Maven. En este fichero se configuran las dependencias, los empaquetados, plugins, etc. Un fichero POM sencillo sería así:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion> <!-- Versión del POM-->
    <groupId>hello</groupId><!--Grupo al que pertenece el proyeto, como el namespace del proyecto-->
    <artifactId>helloWorld</artifactId><!--Nombre que va a recibir el empaquetado del proyecto-->
    <packaging>jar</packaging><!--Tipo de empaquetado, en este casjo JAR-->
    <version>0.1.0</version><!-- Versión del proyecto-->
</project>

Ahora vamos al código, entramos en el directorio hello y creamos un fichero con el típico hola mundo de java:

package hello;

public class HelloWorld {
    public static void main (String [] args){
        System.out.println("Hello World!!");
    }
}

Para compilar, solo tenemos que escribir mvn compile (nos olvidamos del javac) compila el proyecto. Maven deja la salida de la compilación en el directorio target\classes. Así que para ejecutar entramos en ese directorio y escribimos java hello.HelloWorld
Para crear un jar solo tenemos que ejecutar desde el directorio raíz del proyecto mvn package y tendremos en el directorio target el fichero helloWorld-0.1.0.jar (aircraftId-version)
Si intentamos ejecutar el jar nos dará un error:

C:\Desarrollo\HolaMundo>java -jar target/helloWorld-0.1.0.jar
no hay ningún atributo de manifiesto principal en target/helloWorld-0.1.0.jar

Para poder generar un jar ejecutable con Maven podemos usar el plugin Maven Shade. Esto se hace añadiendo el plugin en el POM

<build>
 <plugins>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-shade-plugin</artifactId>
   <version>2.1</version>
   <executions>
    <execution>
     <phase>package</phase>
     <goals>
      <goal>shade</goal>
     </goals>
     <configuration>
      <transformers>
       <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
        <mainClass>hello.HelloWorld</mainClass>
       </transformer>
      </transformers>
     </configuration>
    </execution>
   </executions>
  </plugin>
 </plugins>
</build>

En el plugin debemos indicar la clase que contiene el método public void main. Si ahora repetimos el empaquetado y la ejecución veremos que ya funciona.
Pero veamos como se añade una referencia, por ejemplo noda joda time (en java SE 8 ya no se recomienda usar joda, pero es un ejemplo de uso de una dependencia)

<dependencies>
 <dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.9.6</version>
 </dependency>
</dependencies>

Y ya con esto podemos utilizar la librería en nuestro código, con lo que nuestra clase HelloWorld quedaría así:

package hello;

import org.joda.time.LocalTime;

public class HelloWorld {
    public static void main (String [] args){
        LocalTime now = new LocalTime();
        System.out.println("Now the local time is " + now);
        System.out.println("Hello World!!");
    }
}

Con esto finalizamos el hola mundo de un proyecto java con Maven, donde hemos compilado, añadido dependencias, añadido un plugin de Maven y empaquetado el código en un jar ejecutable. El código fuente lo tenéis disponible en GitHub.
No se si el acercamiento de este tema ha sido condicionado por mi experiencia con .net. Se agradecen comentarios al respecto.

Actualización: 19/11/2016

Pedro me comenta para hacer un Jar ejecutable se suele utilizar el assembly-plugin. Elimino el plugin anterior y añado este. Y lo primero que descubro al revisar la documentación en maven es que no es necesario que ponga el groupId del plugin (ni en este caso, ni en el anterior) ya que por defecto los plugins pertenecen al grupo org.apache.maven.plugins. La sección de plugisn del POM queda ahora, tal que así:

        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>hello.HelloWorld</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <!-- this is used for inheritance merges -->
                        <phase>package</phase>
                        <!-- bind to the packaging phase -->
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

La configuración de este plugin es diferente al anterior, seguimos teniendo que indicar la clase que tiene el public static void main (línea 11), pero además hay que indicar un Assemblie Descriptor que es lo que utiliza este plugin para crear los paquetes que se indican dentro del nodo de configuración.
Afortunadamente el plugin cuenta con algunos predefinidos, en este caso es lo que vamos a usar (jar-with-dependencies).
Ahora, al ejecutar mvn package tendremos dos jars de salida: helloWorld-0.1.0.jar y helloWorld-0.1.0-jar-with-dependencies. El ejecutable es el segundo.
Y como comentábamos anteriormente el código está disponible en GitHub.

martes, 25 de octubre de 2016

Listando los grupos a los que pertenece tu usuario

El otro día en la oficina no podía conectarme a una base de datos. La conexión era integrada de Windows. Un compañero nuevo en la oficina sí que tenía permisos, pero no sabía a qué grupos pertenecía.

En vez de liarnos con ventanas buscando los grupos, le pasé una sentencia para ejecutar en la consola y ver qué grupos ¿qué mejor que una sentencia por consola para ver a qué grupos que contienen “SQL” perteneces? así que le mandé el siguiente comando:

whoami -groups | find /i "SQL"

De esta manera vimos los grupos candidatos a los que debería pedir que incluyera mi usuario el administrados. Aunque no se use mucho, algunas veces whoami es muy útil

image

sábado, 22 de octubre de 2016

Copiar al portapapeles la salida de un comando en la consola cmd

Esta opción es muy útil y casi nadie la sabe. Cuando quieres copiar la salida de un comando al portapapeles en la consola de Windows lo único que hay que hacer es poner es “| clip”. Lo mejor es ir a la documentación (ejecuto clip /?| clip y pego en el post )

CLIP

Descripción:
    Redirecciona el resultado de las herramientas de la línea de comandos
    al Portapapeles de Windows. Esta salida de texto se puede pegar en
    otros programas.

Lista de parámetros:
    /?                  Muestra este mensaje de ayuda.

Ejemplos:
    DIR | CLIP          Coloca un copia de la lista de directorios actual
                        en el Portapapeles de Windows.

    CLIP < README.TXT   Coloca una copia del texto de readme.txt
                        en el Portapapeles de Windows.

imageLo cierto es que esto es muy cómodo y lo puedes mezclar con filtros (find o findstr). Por ejemplo: quiero sacar todas las conexiones abiertas ahora mismo en el pc al portapapeles: netstat -ano | find /i "Established" |clip

lunes, 3 de octubre de 2016

Diferencia entre Git init y Git init --bare

Hace años ya que la guerra entre Git y Mercurial terminó y con el éxito de Github, este software de control de versiones se ha convertido en el más usado. Descentralizado y sin la necesidad de ejecutar ningún software en el servidor es muy cómodo para compartir código.

Si por el motivo que sea necesitar compartir código por el sistema de ficheros, nunca crees el repositorio central con Git init a secas, porque podrás hacer un clone del repositorio, pero cuando en intentes hacer un push aparecerá un mensaje como este:

remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.

Aunque como indica el mensaje, puedes saltare la advertencia configurando receive.denyCurrentBranch, lo recomendado es utilizar un repositorio bare.

El respositorio desnudo

Cuando hacemos Git init en un directorio se crea un directorio oculto .git que es donde se registran los cambios (commits, ramas, etiquetas, ...). Mientras que cuando hacemos Git init --bare, no se crea un directorio de trabajo, es decir, el directorio donde se ejecuta el comando contiene lo mismo que en un repositorio normal estaría en el directorio .git. Así que el repositorio que se vaya a utilizar como repositorio central siempre se debería crear con Git init –bare.

Lo cierto es que esta es una forma muy cómoda y rápida para compartir código con compañeros, tan solo hay que elegir un directorio que esté accesible por ellos, ya sea una carpeta compartida, una ruta de red o un una carpeta de dropbox (o similar). Todo simple, incluso la seguridad, ya que es a nivel de ficheros. Así que si pueden leer el directorio, pueden hacer clone, y si pueden escribir podrán hacer push.

domingo, 18 de septiembre de 2016

Asp.Net Core: Sirviendo ficheros estáticos

Anteriormente en NoCompila hemos visto como se gestiona una petición web mediante middlewares encadenados en Asp.Net Core.
Si quisiésemos devolver el contenido de un fichero, tendríamos que escribir un middleware que analizase el httpRequest, viese qué fichero se está demandando, buscarlo en el sistema de ficheros local y servirlo.
Afortunadamente eso no hace falta programarlo, uno de los middlewares que trae de serie Asp.Net Core es UseStaticFiles que por defecto sirve los ficheros que están ubicados en el directorio wwwroot de la aplicación web.
Para que el Host de la aplicación sepa la ruta local que está manejando debemos indicárselo

public static void Main(string[] args)
{
	var host = new WebHostBuilder()
		.UseKestrel()
		.UseContentRoot(Directory.GetCurrentDirectory())
		.UseIISIntegration()
		.UseStartup<Startup>()
		.Build();

	host.Run();
}

En la línea 5 se establece el ContentRootPath en el webHost, que indica la dirección en el sistema de ficheros local donde se está la aplicación. Una vez establecido el ContentRootPath, otra propiedad se estable por defecto: el WebRootPath, que es el directorio local desde el que se van a servir los ficheros estáticos (por defecto el directorio wwwroot).
Una vez que nuestro Host conoce la ruta de los ficheros, configuramos el pipeline para que sirva ficheros estáticos.

public void Configure(IApplicationBuilder app)
{
	app.UseStaticFiles();
}

UseStaticFiles es el middleware para servir los ficheros estáticos. Pero importante, una de las carencias de Asp.Net Core es que no se puede securizar el contenido de el ContentWebRoot, es decir, de los ficheros estáticos del directorio wwwroot. Todo lo contenido en ese directorio es público sí o sí.

Sirviendo ficheros por defecto

Para servir ficheros por defecto, se utiliza otro middleware del framework: UseDefaultFiles. Este componente no sirve ficheros, lo que hace es un URL rewrite (cambia la ruta de la petición) buscando un fichero cuando solo se le indica un directorio. Si tenemos en cuenta el orden de ejecución de los middlewares importa debemos sabes que hay que usar UseDefaultFiles antes que UseStaticFiles.

public void Configure(IApplicationBuilder app)
{
	app.UseDefaultFiles();
	app.UseStaticFiles();
}

Por defecto los ficheros que busca son:

  1. default.htm
  2. default.html
  3. index.htm
  4. index.html

Habilitando la exploración de directorios

La exploración de directorios está deshabilitada por defecto por motivos de seguridad, pero si se quiere habilitar hay que hacer dos cosas:

  1. Usar el método AddDirectoryBrowser que registra los servicios de exploración de directorios en el contenedor IoC
  2. Añadir al pipeline de la aplicación el middelware UseDirectoryBrowser
public class Startup
{
	// This method gets called by the runtime. Use this method to add services to the container.
	// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddDirectoryBrowser();
	}

	// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
	public void Configure(IApplicationBuilder app)
	{
		app.UseStaticFiles();
		app.UseDirectoryBrowser();
	}
}

Sirviendo ficheros estáticos con ficheros por defecto y habilitando la exploración de directorios a la vez

Alguien debió pensar que para configurar una aplicación web que únicamente sirve ficheros no se debería tener que escribir tanto, asi que tenemos un método extensor que nos permite delegar la configuración de todos los middlewares en una única línea: UseFileServer

public class Startup
{
	// This method gets called by the runtime. Use this method to add services to the container.
	// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddDirectoryBrowser();
	}

	// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
	public void Configure(IApplicationBuilder app)
	{
		app.UseFileServer(enableDirectoryBrowsing: true);
	}
}

domingo, 11 de septiembre de 2016

Asp.Net Core: Startup

Anteriormente en NoCompila hicimos el hola mundo más pequeño posible en Asp.Net Core, más tarde explicamos qué es un middleware en Asp.Net Core y como enlazando middlewares se construye el pipeline de la aplicación.

El código de estos ejemplos es pequeño y se basa únicamente en el método de entrada de la aplicación (public static void Main). Pero a medida que crece el código y ahora que tenemos claro como funciona, vamos a comenzar a separar en clases la lógica de la aplicación.

Si creamos una aplicación Asp.Net web vacía desde el Visual Studio nos encontraremos con el siguiente método Main:

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
	}
    }

Podemos observar como el equipo de Asp.Net aplica el principio de responsabilidad única (una clase solo tendría que tener una razón para cambiar) dejando la responsabilidad de crear el WebHost en la el método Main de la clase Program, mientras que será responsabilidad de la clase Startup configurar la aplicación web. Si nos fijamos en la línea 9, mediante el método UseStartup se le indica al WebHost que utilice la clase Startup que contiene el siguiente código:

public class Startup
{
	// This method gets called by the runtime. Use this method to add services to the container.
	// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
	public void ConfigureServices(IServiceCollection services)
	{
	}

	// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
	public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
	{
		loggerFactory.AddConsole();

		if (env.IsDevelopment())
		{
			app.UseDeveloperExceptionPage();
		}

		app.Run(async (context) =>
		{
			await context.Response.WriteAsync("Hello World!");
		});
	}
}

De la misma manera que en los post anteriores utilizábamos el delegado Configure del WebHostBuilder para configurar el pipeline de la aplicación web, aquí se utiliza método Configure de la clase Startup. En el código anterior tenemos un pipeline con un único middleware que se configura con Run.

Además, el método Configure recibe un IHostenvironment que contiene información sobre el entorno en el que estamos trabajando y un ILoggerFactory que nos permite configurar el motor de logs que trae integrado Asp.Net Core.
Otra nueva característica incluida en el Asp.Net Core es un contenedor de IoC. El método ConfigureServices de Startup se utiliza para registrar las dependencias a inyectar.

Tanto del contenedor, como los entornos y el motor de logs espero poder hablar en futuros post.

lunes, 29 de agosto de 2016

Asp.Net Core: el pipeline de peticiones de la aplicación

Anteriormente en NoCompila vimos como se hacía un Hola Mundo muy sencillo en Asp.Net Core.

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace WebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
           var host = new WebHostBuilder()
            .UseKestrel()
            .Configure(app => {
                app.Run(async (context)=>await context.Response.WriteAsync("Hola"));
            })
            .Build();
            host.Run();
        }
    }
}

En Asp.Net Core se entiende como middleware un delegado que recibe por parámetro un httpContext que permite manejar la petición y la respuesta web. Cada middleware decide si se debe invocar al siguiente delegado, pudiendo ejecutar código antes y después de la invocación.
El pipeline de peticiones debería estar compuesto por al menos un middleware.

Middelware03

En el ejemplo de Hola Mundo, vemos como en la línea 14 es donde se establece el pipeline de peticiones de la aplicación (no se me ocurre ninguna traducción que suene bien para este termino), es decir donde se añade al pipeline de peticiones de la aplicación el único middleware usando el método Run.
Para construir el pipeline tenemos tres métodos:

  • Run: Añade el último delegado a ejecutar en el pipeline, si solo tenemos uno, será este método el que usemos. Es el que hemos usado en el HolaMundo
  • Map y MapWhen: Deciden en base a base a la ruta de la petición web si se ejecuta un middleware
  • Use: Es la manera estándar de añadir un middleware al pipeline.

Los componentes de middleware se ejecutan en el orden en el que se añaden al pipeline.
Microsoft recomienda no modificar el HttpResponse después de hacer la invocación del siguiente middleware.
En el ejemplo que vamos a poner a continuación hacemos caso omiso de las recomendaciones de Microsoft para enseñar el orden de ejecución de los componentes, no es código real de una aplicación de producción:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace WebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
           var host = new WebHostBuilder()
            .UseKestrel()
            .Configure(app => {
                app.Use(async (context, next)=>{
                    context.Response.WriteAsync("Inicio Use1 \n");
                    await next.Invoke();
                    context.Response.WriteAsync("\nFin Use1");
                });
                app.Use(async (context, next)=>{
                    context.Response.WriteAsync("Inicio Use2 \n");
                    await next.Invoke();
                    context.Response.WriteAsync("\nFin Use2");
                });
                app.Run(async (context)=>await context.Response.WriteAsync("Hola"));
            })
            .Build();
            host.Run();
        }
    }
}

En el ejemplo vemos como al ejemplo anterior le hemos añadido dos métodos Use. Si ejecutamos ese código veremos que primero se ejecuta la primera línea del primer use, al ejecutar next.Invoke pasa al siguiente middleware y después ejecuta la tercera línea. Es decir que la salida que tendremos en el navegador es:

Inicio Use1 
Inicio Use2 
Web Asombrosa
Fin Use2
Fin Use1

En el código podemos ver como al usar Use tenemos que recordar ejecutar el siguiente middleware con la llamada next.Invoke, a no ser que nos interese interrumpir el flujo. De hecho un Use sin next.Invoke es igual a un Run.

Con Map se permite tomar decisiones sobre qué se ejecuta en base a la ruta de la petición web. El la clase HttpRequest contiene una propiedad Path que contiene la ruta de la petición web. Cuando se ejecuta Map, la parte de la ruta que coincide con el criterio que estamos poniendo se quita de la propiedad y se añade a la propiedad PathBase. Veamos un ejemplo:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace WebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
           var host = new WebHostBuilder()
            .UseKestrel()
            .Configure(app => {
                app.Map("/manelmola",(app2)=> app2.Run(
                    async (context)=>await context.Response.WriteAsync(
                        string.Format ("Manel Mola\nPath={0}\nPathBase={1}", 
                            context.Request.Path, 
                            context.Request.PathBase
                        )
                    )
                ));
                app.Run(async (context)=>await context.Response.WriteAsync(
                    string.Format ("Hola\nPath={0}\nPathBase={1}", 
                        context.Request.Path, 
                        context.Request.PathBase
                    )
                ));
            })
            .Build();
            host.Run();
        }
    }
}

En este caso al Usar Map si ponemos la ruta coincide con ManelMola escribe “Manel Mola” en caso contrario escribe Hola. Como se puede ver en las capturas:
MapLocalhostMapBlablaMapManelMola

Usar MapWhen es muy parecido, pero en vez de poner un patrón de ruta, el primer parámetro es un delegado que devuelve un boolean, si el primer parámetro devuelve true, se ejecutará el segundo delegado.

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace WebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
           var host = new WebHostBuilder()
            .UseKestrel()
            .Configure(app => {
                app.MapWhen(context => context.Request.Query.ContainsKey("mola"),
                    (app2)=> app2.Run(
                        async (context)=>await context.Response.WriteAsync("MOLA")
                ));
                app.Run(async (context)=>await context.Response.WriteAsync("Hola"));
            })
            .Build();
            host.Run();
        }
    }
}

MapWhenNoMolaMapWhenMola

Cuando la lógica de un middleware es compleja Microsoft recomienda que se exponga mediante un método extensor ocultando la complejidad del componente. Vamos a ver un ejemplo sencillo donde programamos un cronómetro para medir la velocidad del resto de ejecuciones del pipeline:

using Microsoft.AspNetCore.Http;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;

namespace WebApplication{
    public static class StopwatchMiddelwareExtensions{
         public static IApplicationBuilder UseStopWatchMiddelware(this IApplicationBuilder builder){
            builder.Use(async (context, next)=>{
				var cronometro = new Stopwatch();
				cronometro.Start();
				await next.Invoke();
				cronometro.Stop();
				await context.Response.WriteAsync(string.Format("Se ha ejecutado en {0} milisegundos", cronometro.ElapsedMilliseconds));
			});
            return builder;
        }
    }  
}

Al usar el método extensor, el código queda mucho más limpio (línea 16):

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using System;
using System.Threading;

namespace WebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
           var rnd = new Random();
           var host = new WebHostBuilder()
            .UseKestrel()
            .Configure(app => {
                app.UseStopWatchMiddelware();
                app.Run(async (context)=>{
                    Thread.Sleep(rnd.Next(0,1000));
                });
            })
            .Build();
            host.Run();
        }
    }
}

Hay formas más sofisticadas para la creación y encapsulamiento de middlewares, pero como punto introductorio con esto están cubiertos los conceptos básicos. Si buscas un ejemplo más avanzado de como crear encapsular un middleware lo puedes encontrar aquí.

Asp.Net Core trae una serie de middlewares ya programados:

  • Authentication: Da soporte a la autenticación.
  • CORS: Configura el uso cruzado de recursos (cuando se solicita un recurso de un dominio distinto al que se pertenece).
  • Routing: Define en enrutamiento.
  • Session: Permite el uso de sesiones de usuario.
  • Static Files: Ofrece soporte para el uso de ficheros estáticos y exploración de directorios.

lunes, 22 de agosto de 2016

AspNet Core: Hola Mundo

Las aplicaciones Asp.Net Core son aplicaciones de consola que configuran y ejecutan un WebHost.
En base a esto el ejemplo mínimo de un Hola Mundo en Asp.Net Core sería:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace WebApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
           var host = new WebHostBuilder()
            .UseKestrel()
            .Configure(app => {
                app.Run(async (context)=>await context.Response.WriteAsync("Hola"));
            })
            .Build();
            host.Run();
        }
    }
}

El web Host no gestiona directamente las peticiones http. Eso es responsabilidad del servidor web. Asp.Net Core incluye un servidor multiplataforma llamado Krestel.
En el ejemplo anterior vemos que con .UseKestrel() indicamos configuramos el host para que use Krestel como servidor web.

Nuestro WebHost recibirá las peticiones web envueltas en un objeto de tipo httpContext.
En en el delegado async (context)=>await context.Response.WriteAsync("Hola") vemos qué hace la aplicación cuando recibe una petición web: Se recibe un objeto context del tipo httpContext. En este objeto escribimos en la respuesta el texto “Hola”.
En ningún momento se toma una decisión en base a la petición web (httpContext.Request) así que ante cualquier petición siempre hará lo mismo: responder con el texto “Hola”.

A la hora de pasar a producción una aplicación Asp.Net Core no deberíamos dejar al pequeño servidor web Krestel expuesto. Lo que recomienda Microsoft tenerlo detrás de otros servidores web más robustos, como puede ser el IIS en Windows o un nginx en Linux. Lo que me extraña es que ni mencionen apache, que también podríamos ponerlo por delante.

Con este ejemplo podemos ver las grandes diferencias que hay entre el modelo de Asp.Net Core y el modelo Asp.Net. Y es que Asp.Net Core ya no se basa en la librería System.Web.dll y eso cambia mucho todas las reglas de juego. Y en la medida de lo posible deberíamos evitar tener aplicaciones con pies de barro.