📜 ⬆️ ⬇️

Include Node.js in your solution for Microsoft Azure

Recently, much has been written about Node.js in the press, praising highly the asynchronous I / O model, which frees the main thread from waiting for responses to I / O requests and allows it to do other work during this period. The main concept of Node.js is that input-output is an expensive operation, and therefore an attempt has been made to reduce these costs by forcing the introduction of an asynchronous input-output model. I thought about how this concept could be incorporated into an existing infrastructure. If you start from scratch, it is relatively easy to paint technological options and make a choice. However, if the goal is to update the technology for one of the parts of the solution, the whole trick is to choose something modern that has a future that does not entail a lot of additional costs and that can be easily integrated into the existing solution.

This is what I am going to demonstrate in this article. I'll take an existing solution that allows you to view documents in the repository, but requires a shared access signature to download them. In this solution, I will add a simple UI using Node.js. To simplify this implementation, I will take advantage of some of the infrastructures often used for Node.js. Thus, the solution will include:

Together, these three tools will provide a rich infrastructure for building a UI, in many ways similar to the combination of ASP.NET MVC 3 and Razor.

Getting started

If you are new to Node.js, you'd better start by studying the materials available on Microsoft 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 in these tools some familiar concepts, as well as a mixture of familiar and unfamiliar syntax.

In this scenario, my existing services will do the work on the side of Microsoft Azure, and a site based on Node.js hosted by Microsoft Azure will call these services to visualize the list of documents to be accessed. In general, it is useful to create a level of abstraction between the client and the services. This isolates services from any changes in the interface, but the true value of such a separation is in additional functional flexibility and the way you can include and exclude providers of these services.
')
In the existing solution, as presented, the goal was to grant access to the user only if it is authenticated, which leads to the generation of Shared Access Signature (SAS). The idea was to provide read access to authenticated users, and subsequently issue a full CRUD access (Create, Read, Update, Delete) to a specific document based on roles and the level of group membership. Here I will focus solely on permissions for reading.

Query sequence

UserUser
Microsoft Azure Node.js SiteSite based on Node.js in Microsoft Azure
Custom servicesOwn services
Microsoft Azure StorageMicrosoft Azure Storage
Browse PagePage view
Render listList rendering
Send CredsSending credentials
Click Link to Fetch DocumentClick the link to get the document
Get Document List Without SASGetting a list of documents without SAS
Loginentrance
Return access keyReturn key access
Build List with SASList formation with SAS
Hyperlinked listHyperlink list
Get Document ListGetting a list of documents
Get SASGetting SAS
Return leaseRental return


Creation of services

I simulate an authentication service that returns an identifier. A subsequent service call returns a list of files. The Microsoft Azure Storage container (“documents”) that I use has limited open access permissions. I want to provide a list of documents, even if the user is not authenticated, but prohibit unauthenticated users from opening files. The 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 to get SAS - you will need it when creating the method that forms the 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.

If I have a REST service, I can run a quick test through a browser window. After this configuration of the service interface, you can fairly easily imitate authentication using some well-known value until a for loop is written that generates the list and until SAS starts to work properly. VerifyId (string) simply checks if I have an identity cached with a key whose value is equal to accessId. Below is a list returned without authentication. And since it is returned by the service without authentication, the SAS value is set to nil. Thus, it is possible to use the data for the list visualization, but it is impossible to provide a working link to the user, since there is no SAS.

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

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

This is 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, since it can be directly used 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 launched them in Visual Studio, but this is quite a painstaking and completely manual process. Since there are no integration tools for this part of Node.js, I will be editing using Sublime Text 2, and debugging via Chrome (as described in Tomasz Janczuk's blog at bit.ly/uvufEM ).

I have to mention some aids. For those who have not yet passed the initiation rite, the infrastructures I use provide a number of easy-to-use wrappers that encapsulate certain functionality, MVC and the template rendering mechanism:

All of these modules are components of Node.js (like DLLs in .NET) and are usually installed from GitHub via 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 lot of anonymous functions nested in other functions. My advice is to simply reformat the code so that you can see the attachment in the process of working with this code, until you get used to understanding it without reformatting. I tried to make my examples as readable as possible, and also to use screen shots from Sublime, the code for 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 analogue 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 largely by analogy with using expressions in C #. In Node.js, such references are assigned by assigning a variable to the value returned by the require ('[libname]') function. After setting the links, I set up some configuration variables (for example, I set the view engine to “jade”). Of particular interest are the view engine, router, bodyParser, cookieParser and session.

I omit some more prosaic elements, but it’s necessary to set up routes. For the Get command in my Index page, I’ll just 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 on the processing of the index model (index model). To do this, you need to "bind" a certain method of the model:
 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 jump from beginning to end, moving from controller to presentation, but when working in MVC style, I prefer to create a simplified presentation at first. Jade syntax is basically HTML, 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 abbreviated form of foreach. I arbitrarily called the list of enumerated elements docList. This is important, since on the index.js page, where I ask Jade to visualize this view, I will need to pass the value to docList. The rest is clear and without explanation, since I create a UI for the developer - very simple and without any decorations.

Model: index.js

Having configured the infrastructure of the runtime environment in server.js and the template of the final view in index.jade, it remains to write application code that runs 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. In essence, I create a named function (for example, GetAccessKey) and define the 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 is first called GetAccessKey, which simply takes the username and password passed through the form, appends them to the URI as part of the querystring, and uses Restler to perform the 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 faithful to this template in the rest.get call, I define an anonymous function that is executed when the request is completed. Without an error code, it all 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 from 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}); 

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

Executing environment

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

As I protect my repository, direct access is prohibited. However, as soon as I run the example code, the situation changes somewhat. I publish the application with the Publish-AzureService command from Windows PowerShell, go to the page and enter my credentials; after that I am provided with a list of links to files.

Since my service is an intermediary when making calls to the repository, I can enumerate files, despite the fact that this is not directly possible. In addition, since each link is supplemented with SAS, after clicking on it, I am asked 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 are watching 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 you are developing various options, such as the client library for Node.js, direct or indirect (as shown in this article) access to the REST API. Development would certainly be much more efficient and simpler if Node.js were provided with proper instrumental support, but I’m sure that we will eventually see some form of integration with Visual Studio if the popularity of Node.js continues grow.

PS: Thank you, XaocCPS , for the reminder.
Original source: http://msdn.microsoft.com/ru-ru/magazine/jj190801.aspx

Source: https://habr.com/ru/post/227769/


All Articles