Este artículo proviene de un motor de traducción automática.
Pronóstico: nublado
Mezcla Node.js en su Windows Azure solución
Node.js ha estado recibiendo mucha prensa últimamente, y es altamente promocionado por su modelo asincrónico de I/O, que libera el subproceso principal para hacer otro trabajo mientras esperan respuestas de I/O. La regla general de Node.js es que I/O es caro, y que intenta mitigar el gasto forzando un modelo asincrónico de I/O. He estado pensando acerca de cómo se podría integrar en un marco ya existente. Si usted está empezando desde cero, es relativamente fácil de diseñar las opciones de tecnología y tomar una decisión, incluso si la decisión se basa bastante cool factor por sí solo. Sin embargo, si el objetivo es realizar una actualización de la tecnología en una parte de una solución, el truco es escoger algo que es actual, tiene un futuro, no viene con un gran costo adicional y encaja perfectamente con el paisaje existente de la solución.
Eso es exactamente lo que voy a demostrar en esta columna. Me quedo con una solución existente que permite la visualización de documentos en almacenamiento de información, pero requiere una firma de acceso compartido a descargarlos. A esa solución agregaré una sencilla interfaz de usuario utilizando Node.js. Para ayudar a que la aplicación, me quedo con ventaja de algunos marcos utilizados para Node.js. La solución, por tanto, incluirá:
- Motor principal de node.js—the
- Express — un Model-View-Controller (MVC)-marco de estilo
- Jade: un motor de renderizado y plantillas
Juntos estas tres herramientas proporcionará un marco Rico desde el cual construir la interfaz de usuario, como usando ASP.NET MVC 3 y Razor.
Para comenzar
Si eres nuevo en Node.js, es probablemente lo mejor es empezar con los tutoriales disponibles de Microsoft en windowsazure.com/develop/nodejs. También deberás instalar el SDK de Node.js para Windows Azure. Además, usted probablemente querrá pasar un poco de tiempo metiese alrededor Express (expressjs.com) y Jade (jade-lang.com). Si eres nuevo en estas herramientas, encontrará algunos conceptos familiares y una mezcla de sintaxis familiar y desconocida.
En este caso, mis servicios existentes hará el trabajo en el lado de Windows Azure, y el sitio alojado en Windows Azure basada en Node.js llamará esos servicios para representar una lista de documentos para el acceso. Generalmente, es una práctica útil para poner una capa de direccionamiento indirecto entre el cliente y los servicios de back-end. Esto aísla los servicios de cambios de interfaz, pero el valor real es a menudo en la flexibilidad extra funcional y la forma en que puede incluir y excluir a los proveedores de servicios de back-end.
En la solución existente, como representado en figura 1, el objetivo era dar un acceso de usuario sólo si él fue autenticado, lo que genera una firma de acceso compartido (SAS). La idea era que conceda acceso de lectura a quienes autenticado y luego posteriormente otorgar plena creación, lectura, actualización, acceso de eliminar (CRUD) a un documento particular basado en papel y pertenencia a nivel. Aquí me centraré exclusivamente en los permisos de lectura.
Figura 1 la secuencia de solicitud
Creación de los servicios
Voy a la burlarse de un servicio de autenticación para devolver un ID. La llamada de servicio posterior recupera una lista de archivos. El recipiente de almacenamiento de Windows Azure que estoy usando ("documentos") tiene los permisos públicos restringidos. Quiero ofrecer una lista de documentos, incluso si el usuario no está autenticado, pero no desea que un usuario no autenticado para poder abrir los archivos. Mis dos firmas llamadas de la API que he creado son:
http://[host]/admin/GetAccess?User=[user] & pwd = [contraseña]
http://[host]/admin/Files?accessId=[authId]
Por supuesto, querrá un servicio auth más realista que no utilice el querystring y SSL; Yo no cubre ese pedazo de la solución aquí.
En primer lugar, necesito un método para crear una SAS (ver figura 2). Necesitaré este método poco cuando se crea el método que se basa la lista de documentos.
Método de la figura 2 para obtener la firma de acceso compartido
public string GetSharedAccessSignature()
{
string sas = "";
sas = (string) System.Web.HttpContext.Current.Cache.Get("sas");
// If no SAS then get one
if (sas == null)
{
// TODO: hardcoded container, move to config
CloudBlobContainer container =
blobClient.GetContainerReference("documents");
// Ask the container for SAS passing in the a newly initialized policy
sas = container.GetSharedAccessSignature(new SharedAccessPolicy()
{
SharedAccessStartTime = DateTime.Now,
SharedAccessExpiryTime = DateTime.Now.AddMinutes(MaxMinutes),
Permissions =
SharedAccessPermissions.Read | SharedAccessPermissions.List
});
// Add to cache for reuse because this isn't a per-user SAS
System.Web.HttpContext.Current.Cache.Add("sas", sas, null,
DateTime.Now.AddMinutes(MaxMinutes), new TimeSpan(0,0,5,0,0),
CacheItemPriority.High, null);
}
return sas
}
En su mayor parte, esto es bastante típico código de Windows Azure Storage hasta la llamada a GetSharedAccessSignature. No hay una política de acceso compartido aquí, así que necesito para pasar información acerca de cuándo iniciar o detener permitiendo el acceso y el tipo de permisos. Todo lo que quiero ofrecer a través de la SAS es la capacidad para leer y mostrar archivos. También, porque el SAS se utilizará teóricamente por quien se autentica, agregarlo a la caché para su reutilización para evitar colisiones y reducir la cantidad de tasas de deserción en la generación de claves de acceso.
La interfaz del servicio se configurará como un WebMethod:
[OperationContract]
[WebGet(UriTemplate = "Files?accessId={accessId}")]
List<BlobInfo> GetFiles(string accessId);
Observe el uso de la clase personalizada BlobInfo — una vez más, estoy usando direccionamiento indirecto. Tengo campos específicos que quiero volver y IListBlobItem no representan necesariamente. Así, te Mariscal la información devuelta desde IListBlobItems en una lista de mi tipo, tal como se muestra en la figura 3.
Figura 3 GetFiles aplicación
public List<BlobInfo> GetFiles(string accessId)
{
List<BlobInfo> blobs = new List<BlobInfo>();
CloudBlobClient sasBlobClient = default(CloudBlobClient);
CloudStorageAccount storageAccount =
CloudStorageAccount.FromConfigurationSetting(
"StorageAccountConnectionString");
string sas = default(string);
if(accessId.Length > 0)
{
// For the mock just make a simple check
if(VerifyId(accessId))
{
sas = GetSharedAccessSignature();
// Create the blob client directly, using the SAS
sasBlobClient = new CloudBlobClient(storageAccount.BlobEndpoint,
new StorageCredentialsSharedAccessSignature(sas));
}
}
else
{
sasBlobClient = storageAccount.CreateCloudBlobClient();
}
CloudBlobContainer blobContainer =
sasBlobClient.GetContainerReference("documents");
foreach (IListBlobItem blob in blobContainer.ListBlobs())
{
BlobInfo info = new BlobInfo();
info.Name = blob.Uri.LocalPath;
info.Uri = blob.Uri.AbsoluteUri;
info.Sas = sas;
info.CombinedUri = blob.Uri.AbsoluteUri + sas;
blobs.Add(info);
}
return blobs;
}
Es importante tener en cuenta en figura 3 que estoy usando el SAS si se autentica el usuario con el fin de devolver una lista que respeta la política de acceso en el contenedor.
Con el servicio de transferencia de estado representacional (REST) en su lugar puedo ejecutar una prueba rápida a través de una ventana del explorador. Al configurar la interfaz de servicio de esta manera, se hace más fácil para mí para burlarse de autenticación utilizando un valor conocido hasta que tengo el bucle generando la lista y el SAS funcionando correctamente. Una vez hecho esto, el VerifyId(string) simplemente comprueba si tengo una credencial en caché con una clave igual a la accessId. Figura 4 muestra una lista devuelta sin ser autenticado. Porque no era autentica la lista devuelta por el servicio, el valor SAS se establece a cero. Así, puedo usar los datos para hacer la lista, pero no puedo dar un vínculo de trabajo para el usuario, porque no hay ningún SAS.
4 Figura una lista no autenticada
Figura 5 muestra la lista autenticada, que incluyen el SAS.
Figura 5: una lista autenticada con el SAS
Será el trabajo del cliente Node.js para ordenar a través de lo que devuelve el servicio desde una llamada autenticada y representar los hipervínculos con el SAS fijarse al URI. Para ayudar con eso, que he proporcionado un elemento CombinedUri para que el cliente deba acceder sólo que un elemento. Por último, mientras que el XML es genial, porque estoy trabajando en Node.js, tiene sentido cambiar la atribución de la interfaz que devolver JSON para que la respuesta del servicio puede consumirse directamente como un objeto:
[WebGet(UriTemplate = "Files?accessId={accessId}",
ResponseFormat=WebMessageFormat.Json)]
Aquí está lo que la salida JSON como luce:
[{"CombinedUri":"https:\/\/footlocker.blob.core.windows.
net\/documents\/AzureKitchen-onesheet.docx?st=2012-03-05T05%3A22%3A22Z&se=2012-03-05T05%3A27%3A22Z&sr=c&sp=rl&sig=Fh41ZuV2y2z5ZPHi9OIyGMfFK%2F4zudLU0x5bg25iJas%3D","Name":"\/documents\/AzureKitchen-onesheet.docx","Sas":"?st=2012-03-05T05%3A22%3A22Z&se=2012-03-05T05%3A27%3A22Z&sr=c&sp=rl&sig=Fh41ZuV2y2z5ZPHi9OIyGMfFK%2F4zudLU0x5bg25iJas%3D","Uri":"https:\/\/footlocker.blob.core.windows.
net\/documents\/AzureKitchen-onesheet.docx"}]
Como se señaló, JSON es lo que en última instancia queremos aquí, ya que es directamente consumible en Express y Jade.
Interfaz de usuario de node.js
Ya he configurado Node.js, Express y Jade, así que estoy listo para crear la interfaz de usuario. Me he pasado por el proceso de implementación de roles Node.js obtener y ejecutar en Visual Studio, pero es un proceso bastante detallado y completo manual. Así, porque no hay ninguna integración de herramientas para la parte de Node.js de esta, usaré Sublime 2 texto cromado para depurar y hacer mis tareas de edición (como se describe en el blog de Tomasz Janczuk en bit.ly/uvufEM).
Debo mencionar algunos artículos de limpieza. Para los no iniciados, los marcos que estoy empleando proporcionan algunos contenedores easy-to-use cierta funcionalidad, MVC y un motor de representación de la plantilla:
- Restler para llamadas de resto fáciles (que simplificado WebClient)
- Express para el marco general de aplicación MVC-estilo
- Jade para procesamiento de plantilla, relacionado con el motor de Razor en ASP.NET MVC
Estos son todos los módulos considerados Node.js (como archivos DLL en. net) y generalmente se instalan desde GitHub via nodo paquete Manager (MNP). Por ejemplo, para instalar Restler, utilice el comando "restler Instale el MNP" desde dentro de la carpeta del proyecto. Esto toma el lugar de instalación de un módulo y agregar una referencia a él en el proyecto manualmente.
Un último bit de información para el desconocido. Usted notará que muchas funciones anónimas anidado en otras funciones. Mi mejor Consejo es simplemente volver a formatear el código suficiente para ver la anidación mientras trabaja con ella hasta que se lo analizar naturalmente sin tener que formatearlo. Voy intentar anidar mis muestras para mejorar la legibilidad, así como utilizar imágenes de Sublime, que son de color bien y ayudar con la legibilidad.
Usé los comandos New-AzureService y AzureWeb de nuevapapel para crear una aplicación denominada AzureNodeExpress. También he hecho un par de otras modificaciones. En server.js se agregó rutas para llegar a la página de índice; el ASP.NET analógico es el método de MapRoutes utilizado en proyectos MVC.
Server.js modificaciones
Mucho como instrucciones using en C#, necesito decirle Node.js qué bibliotecas utilizará. En Node.js, configuran estas referencias asignando a una variable el valor de la devolución de la requieren ('[lib]') función nombre. Una vez establecidas las referencias, hago un poco de configuración para establecer algunas de las variables del motor (por ejemplo, establecer "ver el motor" "jade"). De particular interés son el "motor de vista", router, bodyParser, cookieParser y sesión.
Voy a saltar algunos de los elementos más mundanos, pero quiero configurar mi ruta. Para el verbo Get en mi página de índice voy simplemente representar la vista directamente:
app.get('/index',
function(req, res){
res.render('index.jade', {title: 'index'});
}
);
Sin embargo, para el verbo Post quiero pasar el manejo sobre el modelo de índice. Para lograrlo tengo que "obligar" a un método definido del modelo:
app.post('/index', index.GetAccessKey.bind(index));
Con el enrutamiento en lugar que necesito configurar la vista y el modelo.
La View—Index.jade
En cierto sentido, estoy saltando desde el principio hasta el final yendo desde el controlador a la vista, pero cuando se trabaja en un estilo MVC quiero crear una vista simplificada para trabajar contra el primero. Sintaxis de Jade es básicamente HTML sin la decoración de los soportes. Mi plantilla de Jade todo se muestra en la figura 6.
Figura 6 plantilla de Jade
html
head
title Default
body
h1 File List Home Page
br
label Welcome #{username}
form(method='post', action='/index')
label Username:
input(name='username', type='text')
br
label Password:
input(name='password', type='password')
br
button(type='submit') Login
h2 Files
form
table(border="1")
tr
td Name
td Uri
each doc in docList
tr
td #{doc.Name}
td
a(href=#{doc.CombinedUri}) #{doc.Name}
Aquí destacan el uso de # {[var]} para hacer referencia a variables y la plantilla de la tabla con el lazo, que es una especie de foreach abreviado. Yo he nombrado arbitrariamente la lista de artículos que quiero iterar sobre docList. Esto es importante porque en la página de index.js donde pido Jade para representar esta vista, que necesito pasar el valor para docList. Las cosas son muy básicas aquí, porque solo estoy creando un desarrollador IU — simple y llano.
La Model—Index.js
Habiendo establecido la infraestructura de tiempo de ejecución en server.js y la plantilla de vista final en index.jade, me quedo con la carne de la ejecución, lo que sucede en index.js. Recuerde que configurar un enlace para app.Publicar en la página de índice. Que enlace se cargue y ejecute el prototipo que he creado en index.js. Para ello, agregaré funciones con el prototipo del índice, como se muestra en la figura 7. En esencia, estoy creando una función con nombre (por ejemplo, GetAccessKey) y definir una función anónima como su órgano de ejecución. En cada una de estas funciones usaré el módulo Restler para simplificar las llamadas resto que necesito hacer.
Figura 7 Index.js funciones
La primera llamada una vez que el Post pasa de bind es GetAccessKey, que simplemente toma el usuario y contraseña que he presentado a través del post de formulario, les anexa a la URI como parte de la cadena de consulta y usa Restler para hacer un Get. Recuerde, en Node.js toda la comunicación pasa de forma asincrónica, que es una de las razones que se verá la proliferación de funciones anónimas altamente anidadas. Mantenerse fiel a ese patrón en la llamada a rest.get, definir una función anónima que se ejecuta una vez finalizada la solicitud. Sin el código de control de errores, la línea se simplifica a:
rest.get (uri).on('complete',
function(result){
accesskey = result;
this.ShowDocs (req, res);}
)
Ojala, que volver a formatear ayudarán a darle un sentido de lo que está pasando. Una vez que me he metido la clave de mi servicio, voy postfix al URI en el método para obtener la lista de documentos. Ahora las cosas se ponen un poco diferentes a la habitual. La función anónima manejo el retorno de la llamada de resto para obtener la lista de documentos, pido a Jade para procesar los resultados para mí:
res.render ('index', {title: "Doc List",
layout: false,
docList: result,
username: req.BODY.username});
Señaló que en la plantilla que he creado el nombre de la variable "docList." Ahora necesito para asegurarse de que estoy usando ese nombre correcto. La llamada a res.render dice el marco Express para procesar el recurso de "índice" y luego pasa parámetros a través de una lista de pares de nombre: valor de dos puntos y comas.
Runtime
Si intento buscar a uno de los archivos para descargar, me presenté con nada. No se encuentra la página Web. Se podría esperar un error no autorizado de Windows Azure Storage, pero si se intenta acceder a algo que es marcados como privados, lo que vuelve es que no existe el recurso. Esto es así por diseño, y es deseable porque algo que es "privado" no debería existir para el público, incluso en el concepto. Si en cambio se devuelve un error 401, indicaría que algo está realmente allí, violando la política de seguridad representada por privado.
Porque he fijado la ubicación de almacenamiento, no se permite ningún acceso directo. Sin embargo, una vez que ejecuto el código de ejemplo, la historia es un poco diferente. I publicar la aplicación utilizando el comando de Windows PowerShell publicar-AzureService, vaya a la página y escriba mis credenciales; Entonces estoy presenta una lista de vínculos a los archivos (véase figura 8).
Figura 8 vínculos a los archivos
Porque mi servicio es intermediación las llamadas para almacenamiento, fui capaz de listar los archivos, aunque no puedo enumerarlas directamente. También, porque cada vínculo es fijarse con el SAS, cuando hago clic en él yo soy pedirá abrir o guardar el documento de destino.
En resumen
Si estás interesado en las tecnologías nuevas o tendencias como una manera de evolucionar su aplicación de Windows Azure, y eres un verdadero creyente de lo que está sucediendo en el dominio de Node.js, Windows Azure le ha cubierto — no sólo con la solución de alojamiento, pero también en el lado de desarrollo con opciones tales como una biblioteca de cliente para Node.js, golpeando la API REST directamente o a través de direccionamiento indirecto como he demostrado aquí. Desarrollo sin duda sería mucho mejor y más fácil si Node.js contó con apoyo de herramientas adecuadas, y estoy seguro de que finalmente veremos a algún tipo de integración con Visual Studio si la popularidad de Node.js sigue creciendo.
Joseph Fultz es un arquitecto de software en Hewlett-Packard Co., que trabaja como parte del grupo HP.com Global IT. Anteriormente fue un arquitecto de software de Microsoft, trabaja con su empresa de primer nivel y clientes de ISV para definir soluciones de arquitectura y diseño.
Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Bruno Terkaly