Inclusion Node.js in your solution for Microsoft Azure

Recently, Node.js has been widely written in the press, praising highly for the asynchronous I / O model, which frees the main thread from waiting for answers to I / O requests and allows it to do other work during this period. The main concept of Node.js is that I / O is an expensive operation, and therefore an attempt has been made to reduce these costs by forcing the introduction of an asynchronous I / O model. I was thinking about how this concept can be incorporated into an existing infrastructure. If you are starting from scratch, it’s relatively easy to paint technological options and make choices. However, if the goal is to update the technology for one part of the solution, the trick is to choose something modern that has a future,

That is what I intend to demonstrate in this article. I will take an existing solution that allows you to view documents in the repository, but requires a shared access signature to download them. I will add a simple UI to this solution using Node.js. To simplify this implementation, I will take advantage of some of the commonly used Node.js frameworks. Thus, the solution will include:
  • Node.js - the basic mechanism;
  • Express - infrastructure in the style of Model-View-Controller (MVC);
  • Jade - a mechanism for rendering and supporting templates.

Together, these three tools will provide a rich infrastructure for building a UI, much like a combination of ASP.NET MVC 3 and Razor.

Getting down

If you are new to Node.js, you'd better start by exploring the materials available on the Microsoft website at windowsazure.com/develop/nodejs . You will also need to install the Node.js SDK for Microsoft Azure. In addition, you will probably need to spend some time experimenting with Express expressjs.com and Jade jade-lang.com . You will find some familiar concepts in these tools, as well as a mixture of familiar and unfamiliar syntax.

In this scenario, my existing services will do work on the Microsoft Azure side, and the Node.js-based site hosted on Microsoft Azure will call these services to visualize the list of documents to access. In general, it is useful to create an abstraction layer between the client and the services. This isolates the services from any changes in the interface, but the true value of this separation is in additional functional flexibility and the way you can enable and exclude providers of these services.

In the existing solution, as presented, the goal was to give the user access only if he is authenticated, which leads to the generation of the Shared Access Signature (SAS). The idea was to provide read access to authenticated users, and subsequently issue full CRUD access (Create, Read, Update, Delete) to a specific document based on roles and level of group membership. Here I will focus solely on read permissions.

Query sequence

UserUser
Microsoft Azure Node.js SiteNode.js-based site in Microsoft Azure
Custom servicesOwn services
Microsoft Azure StorageMicrosoft Azure Storage
Browse PageView page
Render listList rendering
Send creedsSubmitting Identities
Click Link to Fetch DocumentClicking a link to receive a document
Get Document List Without SASGetting a list of documents without SAS
Loginentrance
Return access keyReturn Passkey
Build list with sasListing with SAS
Hyperlinked listHyperlink list
Get Document ListGetting a list of documents
Get sasGetting SAS
Return leaseRental return


Service Creation

I imitate an authentication service that returns some identifier. A subsequent service call returns a list of files. The Microsoft Azure Storage container ("documents") I use has limited open access permissions. I want to provide a list of documents, even if the user is not authenticated, but prevent unauthenticated users from opening files. Two call signatures for the API I created look like this:

http: // [host] / admin / GetAccess? User = [user] & pwd = [password]

http: // [host] / admin / files? AccessId = [authId]

Of course , you will need a more realistic authentication service that uses SSL and does not operate with a query string; I will not describe this part of the solution here.

First of all, you need to write a method for obtaining SAS - you will need it when creating a method that forms a list of documents.
    public string GetSharedAccessSignature()
    {
      string sas = "";
      sas = (string) System.Web.HttpContext.Current.Cache.
        Get("sas");
      // Если SAS нет, создаем его
      if (sas == null)
      {
        // TODO: контейнер "зашит" в код,
        // переместить в конфигурацию
        CloudBlobContainer container =
          blobClient.GetContainerReference("documents");
        // Запрашиваем у контейнера передачу SAS
        // в только что инициализированной политике
        sas = container.GetSharedAccessSignature(
          new SharedAccessPolicy()
        {
          SharedAccessStartTime = DateTime.Now,
          SharedAccessExpiryTime =
            DateTime.Now.AddMinutes(MaxMinutes),
          Permissions = SharedAccessPermissions.Read |
            SharedAccessPermissions.List
        });
        // Добавляем в кеш для повторного использования,
        // поскольку это 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
    }

It is important to note that in the code I use SAS if the user is authenticated to return a list that matches the access policy for the container.

With a REST service, I can run a quick test through a browser window. After such a configuration of the service interface, you can easily simulate authentication using some well-known value until a for loop is generated that generates a list and until SAS starts to work properly. VerifyId (string) just checks if I have an identity cached with a key whose value is equal to accessId. The following is a list returned without authentication. And since it is returned by the service without authentication, the SAS value is set to nil. Thus, you can use the data to visualize the list, but you cannot provide a working link to the user, since there is no SAS.

In fig. 5 shows an authenticated list that includes SAS.

The analysis of what the service returns when an authenticated call rests with the Node.js client; in addition, it must render hyperlinks from the SAS, which is written to the end of the URI. To simplify this task, I provided a CombinedUri element, and the client will need to access only this element. Finally, although XML is a good thing, I still work in Node.js, and therefore it makes sense to change the attributes of the interface so that it returns JSON. Thanks to this, the response of the service can be directly used as an object:
    [WebGet(UriTemplate = "Files?accessId={accessId}",
      ResponseFormat=WebMessageFormat.Json)]

Here's what the JSON output looks like:
    [{"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=Fh41ZuV2y2z
    5ZPHi9OIyGMfFK%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=Fh41ZuV2y2z
    5ZPHi9OIyGMfFK%2F4zudLU0x5bg25iJas%3D","Uri":"https:\/\/
    footlocker.blob.core.windows.net\/documents\/AzureKitchen-
    onesheet.docx"}]

As noted, ultimately we need JSON here, as it can be used directly in Express and Jade.

Node.js UI

I have already installed Node.js, Express and Jade, so I'm ready to create a UI. I deployed Node.js roles and ran them in Visual Studio, but this is a rather painstaking and completely manual process. Since there is no integration tool for this part of Node.js, I will be editing using Sublime Text 2, and debugging through Chrome (as described in the blog by Tomasz Janczuk at bit.ly/uvufEM ).

I have to mention some aids. For those who have not yet passed the initiation rite, the infrastructures used by me provide a number of easy-to-use shells encapsulating certain functionality, MVC and a template rendering mechanism:
  • Restler - facilitates REST calls (consider it a simplified WebClient);
  • Express - a universal application infrastructure in the style of MVC;
  • Jade is a template rendering engine similar to Razor, which is used in ASP.NET MVC.

All of these modules are components of Node.js (like DLLs in .NET) and are usually installed from GitHub through the Node Package Manager (NPM). For example, to install Restler, enter the command “npm install restler” in the project folder. This command will install the module and add a link to it in the project.

And one more thing for the uninitiated. You will see a ton of anonymous functions nested in other functions. My advice is simply to reformat the code so that you can see the attachment while working with this code, until you get used to understanding it without reformatting. I tried to make my examples as readable as possible, as well as use screen shots from Sublime, the code on which is highlighted in different colors (this also helps the perception of the code).

When creating the AzureNodeExpress application, I used the New-AzureService and New-AzureWeb¬Role commands. I also made a few other changes. In server.js, I added routes to the Index page; An analog in ASP.NET is the MapRoutes method used in MVC projects.

Changes in server.js

I need to tell Node.js which libraries I will use; this is done in much the same way as using statements in C #. In Node.js, such links are set by assigning the variable the value returned by the require ('[library_name]') function. After setting the links, I set up some configuration variables (for example, set the view engine to "jade"). Of particular interest are view engine, router, bodyParser, cookieParser, and session.

I omit some more prosaic elements, but routes need to be configured. For the Get command in my Index page, I will simply visualize the view:
    app.get('/index',
      function(req, res){
        res.render('index.jade', {title: 'index'});
      }
    );

But in the case of the Post command, I want to pass the processing of the index model. To do this, “link” a specific model method:
app.post('/index', index.GetAccessKey.bind(index));

After that, you can proceed to customize both the view and the model.

View: index.jade

In a sense, I make a jump from start to finish, moving from the controller to the view, but when working in the MVC style, I prefer to create a simplified view at first. The Jade syntax is mostly HTML compliant, but it does not use square brackets. My whole Jade template is shown below.
    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}

Notice that # {[var]} is used here for variable references and the table template with a loop inside, which is a kind of shortened form of foreach. I arbitrarily named the list of iterable elements of docList. This is important because on the index.js page, where I ask Jade to visualize this view, I need to pass the value for docList. The rest is clear without explanation, since I create a UI for the developer - very simple and without any decorations.

Model: index.js

Having configured the runtime infrastructure in server.js and the final presentation template in index.jade, it remains to write the application code executed in index.js. Recall that I linked app.Post to the Index page. This binding will load and run the prototype I created in index.js. To do this, I will add functions to the prototype index, as shown below. Essentially, I create a named function (e.g. GetAccessKey) and define an anonymous function as its body. In each of these functions, I will use the Restler module to simplify the REST calls I need.
After binding Post, GetAccessKey is called first, which simply accepts the username and password passed through the form, appends them to the URI as part of querystring, and uses Getler to execute Get. Recall that in Node.js, all interactions occur asynchronously, and this is one of the reasons for the abundance of nested anonymous functions. Keeping this pattern true in the call to rest.get, I define an anonymous function that is executed when the request is processed. Without error handling code, it comes down to the following:
    rest.get (uri).on('complete',
      function(result){
        accesskey = result;
    this.ShowDocs (req, res);}
      )

Fortunately, this reformatting helps to understand what is being done here. As soon as I get the key to my service, I add it to the end of the URI in this method to get a list of documents. And now the order of things begins to differ from the usual. In an anonymous function that processes the data returned by a REST call to get a list of documents, I ask Jade to visualize the results:
    res.render ('index', {title: "Doc List",
      layout: false,
      docList: result,
      username: req.BODY.username});

I previously noted that in the template I created a variable called docList. Now I need to make sure that I use this name. The res.render call tells the Express framework to visualize the index resource, and then pass parameters through a list of name: value pairs, separated by colons and commas.

Runtime

If you try to go to one of the files to download it, nothing appears on the page. This web page was not found. You may have expected Microsoft Azure Storage to report an unauthorized access error, but if you try to access a private resource, the error "resource does not exist" is returned. That is what is intended, and this behavior is preferable because a private resource should not exist in a public space. If error 401 returned instead, it would indicate that such a resource actually exists, and thus would reveal the fact of its existence.

Since I am protecting my repository, direct access is denied. However, as soon as I run the sample code, the situation changes somewhat. I publish the application with the Publish-AzureService team from Windows PowerShell, go to the page and enter my identities; after that I am given a list of file links.

Since my service is an intermediary for calls to the repository, I can list files, although it is not possible to do this directly. In addition, since each link is supplemented by SAS, after clicking it, I am invited to open or save the target document.

Conclusion

If you are interested in new technologies for the development of your Microsoft Azure application and you follow what is being done in the field related to Node.js, then Microsoft Azure is exactly what you need: it not only provides hosting for your solution, but also provides when developing various options, for example, the client library for Node.js, direct or indirect (shown in this article) access to the REST API. Development, of course, would be much more efficient and easier if Node.js were provided with proper instrumental support, but I'm sure that in the end we will see some form of integration with Visual Studio, if the popularity of Node.js continues to be grow.

PS: Thanks, XaocCPS , for the reminder.
Source:http://msdn.microsoft.com/en-us/magazine/jj190801.aspx

Also popular now: