Статьи

Использование Node.js с базой данных Windows Azure SQL

Node.js часто используется для создания сильно асинхронных конечных точек для веб-приложений. Хотя приложения, созданные с помощью Node.js, часто являются совершенно новыми приложениями, использующими новые базы данных или службы, существует большое количество существующих приложений, которые можно создавать с помощью Node.js и которые могут использовать преимущества существующих баз данных SQL Server.

Каноническое приложение Hello World, написанное в Node.js для Windows Azure , показано ниже:

var http = require('http')
var port = process.env.port || 1337;
http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World\n');
}).listen(port);

HTTP-сервер создан, и приложение прослушивает определенный порт для входящих запросов. Анонимная функция определена для обработки входящих запросов. Когда запрос получен, обработчик просто записывает заголовок ответа HTTP 200, за которым следует текст ответа.

В качестве примера того, как использовать базу данных SQL Windows Azure с Node.js , мы создадим простое веб-приложение, которое отслеживает простой список задач. Помимо Node.js, приложение будет использовать два популярных модуля сообщества: Express и Jade.

Создание базы данных списка задач в Windows Azure

Для начала мы создадим небольшую базу данных для хранения нашего списка задач. Вам потребуется учетная запись Windows Azure, чтобы создать базу данных или развернуть пример приложения для этой статьи. Чтобы создать учетную запись, перейдите на http://www.windowsazure.com . Нажмите на ссылку Бесплатная пробная версия на домашней странице и следуйте инструкциям, чтобы зарегистрировать учетную запись.

После создания учетной записи перейдите по адресу http://manage.windowsazure.com, чтобы посетить портал управления. Войдите, используя свои учетные данные; портал управления будет отображаться, как показано на рисунке ниже.

Портал управления предоставляет доступ к функциям Windows Azure, которые вы используете в рамках своей подписки. Нажмите на вкладку База данных SQL, чтобы управлять экземплярами базы данных SQL. Вы увидите что-то вроде экрана ниже.

Нажмите на ссылку «Создать базу данных SQL», чтобы перейти на страницу, позволяющую создать новую базу данных, как показано ниже.

Используйте следующие значения для новой базы данных:

Имя: TaskList

Издание: Интернет

Максимальный размер: 1 ГБ

Сервер: новый сервер базы данных SQL

Нажмите стрелку вправо, чтобы перейти на следующую страницу, которая позволяет выбрать имя пользователя, пароль и местоположение для базы данных. Укажите соответствующие значения для этих параметров и установите флажок, чтобы создать базу данных. После создания базы данных будет отображена панель управления базой данных, как показано на рисунке ниже.

Ваша база данных будет иметь имя, подобное этому:

7ok3age98.database.windows.net

Это имя присваивается Windows Azure и не может быть изменено. База данных защищена правилами брандмауэра, и по умолчанию только серверы Windows Azure могут подключаться к базе данных. Когда вы используете портал для доступа к базе данных, вам будет предложено добавить ваш текущий IP-адрес в правило брандмауэра, чтобы разрешить доступ. Если вы не разрешите доступ, вы не сможете управлять базой данных.

База данных TaskList имеет таблицу с именем Tasks, которая используется для хранения задач, которыми в данный момент управляет приложение. Есть два способа добавить таблицу и исходные данные:

  • Вы можете подключиться к базе данных с помощью SQL Server Management Studio (SSMS)
  • Вы можете использовать портал управления для добавления таблиц

В этой статье мы будем использовать SSMS. Если в настоящее время у вас не установлена ​​SSMS для SQL Server 2012, вы можете загрузить ее как часть SQL Express.

Используя SSMS, откройте соединение с базой данных и базой данных TaskList. Используйте полное имя сервера (в нашем предыдущем примере 7ok3age98.database.windows.net), а также ваше имя пользователя и пароль. Откройте новое окно запроса и выполните команды в листинге 1, чтобы добавить таблицу Tasks в базу данных TaskList.

Листинг 1. Команды, используемые для инициализации базы данных TaskList.

CREATE TABLE [dbo].[Tasks](
	[ID] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
	[Description] [nvarchar](200) NOT NULL,
	[Priority] [nvarchar](50) NOT NULL,
	[Status] [nvarchar](50) NOT NULL
)
GO
INSERT INTO [dbo].[Tasks]
           ([Description], [Priority], [Status])
     SELECT 'Shop for dinner', 'high', 'done'
     UNION ALL
     SELECT 'Get car washed', 'med', 'new'
     UNION ALL
     SELECT 'Cook dinner', 'high', 'in-progress'
GO

После создания базы данных TaskList следующим шагом будет локальная установка SDK Node.js и начало работы с базой данных.

Установка SDK

Чтобы начать разработку с Node.js и Windows Azure, загрузите Windows Azure SDK для Node.js, который доступен в центре разработчиков Windows Azure по адресу http://www.windowsazure.com/en-us/develop/nodejs/. , SDK включает в себя инструменты, необходимые для создания приложений Windows Azure с Node.js.

Установка модуля node-sqlserver

Начните с создания нового каталога для вашей работы с Node. Хотя я буду использовать C: \ NodeWork \ TaskList, не стесняйтесь использовать любое имя каталога — просто учтите, что мои примеры путей могут отличаться от ваших.

Первым шагом к использованию SQL Server в приложении Node.js является установка модуля node-sqlserver. Хотя большинство модулей для приложения Node.js устанавливаются с помощью Node Package Manager (NPM), модуль SQL-Node в настоящее время находится в режиме предварительного просмотра.

На момент написания этой статьи для самой простой установки требуется вручную установить модуль SQL-Node, выполнив следующие действия:

1. Загрузите исполняемую версию модуля (в настоящее время node-sqlserver.0.1.0.EXE) с https://github.com/WindowsAzure/node-sqlserver/ во временный каталог.

2. Запустите node-sqlserver.0.1.0.EXE

3. При появлении запроса введите путь к временному каталогу, который будет использоваться для расширения модуля.

4. После распаковки файлов запустите node-sqlserver-install.cmd, чтобы сгенерировать необходимые каталоги.

5. Скопируйте каталог node-sqlserver в каталог node-modules вашего приложения (создайте node_modules, если он еще не существует)

После выполнения этих шагов рабочий каталог настраивается для использования модуля node-sqlserver для подключения к локальному экземпляру SQL Server или базе данных SQL Azure Windows.

Подключение к базе данных

При использовании Node.js с Windows Azure и базой данных SQL вы выполняете те же три основных шага, которые вы используете, когда SQL Server используется с другими платформами и системами:

  • Подключитесь к базе данных, используя соответствующие учетные данные
  • Оформите запрос к базе данных
  • Опционально отображать информацию пользователю

В качестве начального примера использования Node.js и модуля node-sqlserver мы подключимся к базе данных SQL Azure Windows, которую мы создали выше, с отображением содержимого таблицы TaskList.

Первый исходный файл представлен ниже в листинге 2. Все остальные листинги в этой статье содержат три заполнителя в переменной conn_str, которые должны быть обновлены значимыми значениями для вашего проекта:

—        [databasename] , которое должно быть именем вашего сервера баз данных (что-то вроде 5rt89g4, а не TaskList)

—        [имя пользователя] , которое должно быть именем пользователя с правами доступа к базе данных. При создании базы данных ранее было присвоено как минимум одно имя

—        [пароль] , который является паролем, связанным с именем пользователя

Например, если имя вашей базы данных 5rt89g4, имя пользователя — tasklist-user, а пароль — 88Noodles, строка подключения будет выглядеть следующим образом:

var conn_str = "Driver={SQL Server Native Client 11.0};" +
               "Server=tcp:5rt89g4.database.windows.net,1433;" +
               "Database=TaskList;Uid=tasklist-user;" +
               "Pwd=88Noodles;Encrypt=yes;Connection Timeout=30";


Сохраните содержимое листинга 2 как server.js в рабочем каталоге вашего проекта (если вы используете мои имена каталогов, сохраните в C: \ NodeWork \ TaskList.)

Листинг 2. Подключение к базе данных SQL с помощью Node.js

var http = require('http');
var port = process.env.port || 1337;
var sql = require('node-sqlserver');

var conn_str = "Driver={SQL Server Native Client 11.0};" +
               "Server=tcp:[databasename].database.windows.net,1433;" +
               "Database=TaskList;Uid=[username];" +
               "Pwd=[password];Encrypt=yes;Connection Timeout=30";

var query = "SELECT description, priority, status FROM dbo.Tasks";

http.createServer(function (req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    sql.open(conn_str, function (err, conn) {
        if (err) {
            res.end("Error opening the connection!");
            return;
        }
        conn.queryRaw(query, function (err, results) {
            if (err) {
                res.write("Error running query!");
                return;
            }
            console.log("row count = " + results.rows.length + "\n");
            for (var i = 0; i < results.rows.length; i++) {
                res.write("Description: " + results.rows[i][0] +
                          " Priority: " + results.rows[i][1] + 
                          " Status: " + results.rows[i][2] + "\n");
            }
            res.end('Done --\n');
        }); 
    }); // sql.open
}).listen(port);

После сохранения содержимого листинга 2 в качестве server.js откройте командное окно и перейдите в рабочий каталог (например, C: \ NodeWork \ TaskList.). Запустите Node.js, используя следующую команду:

node server.js

Хотя в командном окне нет немедленной обратной связи, поскольку Node.js выполняет его, он загружает файл server.js и открывает конечную точку по адресу http: // localhost: 1337 . Откройте браузер и перейдите по этому адресу, чтобы увидеть текущее содержимое таблицы TaskList, как показано на рисунке ниже.

В командном окне будут отображаться любые исключения, возникающие во время обработки, а также сообщения, которые явно зарегистрированы. Код в листинге 2 бревна количества возвращаемых строк для каждого набора результатов.

В листинге 2 все взаимодействие с базой данных происходит через модуль node-sqlserver. Ссылка на модуль инициализируется в верхней части списка со следующей строкой:

var sql = require('node-sqlserver');

Соединение с базой данных открывается с помощью вызова sql.open (). В соответствии с ожидаемым шаблоном в Node.js, анонимная функция передается в качестве аргумента для обработки результата:

sql.open(conn_str, function (err, conn) {
    if (err) {
       ...
    }
    ...
});

Если не удается открыть соединение, отображается сообщение об ошибке. Если попытка подключения успешна, ссылка на соединение передается обработчику, и выполняется запрос к базе данных с помощью вызова conn.rawQuery ():

conn.queryRaw(query, function (err, results) {
    if (err) {
        ...
    }
   ...
});

Если запрос выполнен успешно, обработчику передается ссылка на результат, содержащая данные запроса. Цикл for во фрагменте ниже перебирает результат и отображает данные в браузере:

conn.queryRaw(query, function (err, results) {
    if (err) {
        ...
    }
    for (var i = 0; i < results.rows.length; i++) {
        res.write("Description: " + results.rows[i][0] +
                    " Priority: " + results.rows[i][1] +
                    " Status: " + results.rows[i][2] + "\n");
    }
    res.end('Done --\n');
});

Хотя эта версия примера демонстрирует, как подключать и извлекать данные, для того, чтобы быть полезной, нам нужно добавить некоторый код для обработки вставки новых элементов и немного веб-интерфейса, чтобы мы могли взаимодействовать с пользователем.

Используя Экспресс

Создание даже простого веб-сайта было бы утомительно без рамок или других инструментов. Чтобы избежать существенного количества разметки HTML в нашем JavaScript, мы будем использовать два популярных модуля Node.js для управления презентацией:

·       Express — это инфраструктура маршрутизации, которая позволяет создавать поддерживаемые приложения, использующие асинхронные функции Node.js.

·       Jade — это шаблонизатор для Express и Node.js, который будет использоваться для упрощения программирования пользовательского интерфейса, необходимого для примера проекта.

Express позволяет вам определять функции, которые обрабатывают запросы на основе отображений (известных как маршруты), которые вы определяете. Для начала установите Express с помощью Node Package Manager (NPM) с помощью следующей команды из рабочего каталога вашего проекта:

NPM install express

NPM найдет экспресс-модуль и установит его в подкаталог проекта node_modules.

Express заменяет модуль HTTP, использованный в первом примере, и вместо того, чтобы обрабатывать все запросы с помощью одной функции, мы можем определить обработчики для конкретных команд HTTP и входящих путей. Например, чтобы обработать запрос к http: // sitename / users, вы должны использовать такую ​​конструкцию при использовании Express:

app.get('/users', function (req, res) {
    res.write(...);
    res.end(...);
});

Переменная res, передаваемая в обработчик, является ссылкой на объект ответа, который используется для отправки ответов обратно в браузер.

Версия server.js в листинге 3 похожа на более раннюю версию, за исключением того, что теперь мы используем Express для управления маршрутами. В этом случае мы управляем только одним базовым URL-маршрутом. Замените текущую версию server.js этим кодом, чтобы начать использовать Express. Как и в предыдущем примере, замените значения для сервера базы данных, имени пользователя и пароля в строке подключения.

Листинг 3. Использование Express с node-sqlserver для работы с данными.

var express = require('express');
var app = express.createServer();
var sql = require('node-sqlserver');

var conn_str = "Driver={SQL Server Native Client 11.0};" +
               "Server=tcp:[databasename].database.windows.net,1433;" +
               "Database=TaskList;Uid=[username];" +
               "Pwd=[password];Encrypt=yes;Connection Timeout=30";
var query = "SELECT description, priority, status FROM dbo.Tasks";
var port = process.env.port || 1337;

app.get('/', function (req, res) {
    var taskSet = [];
    sql.open(conn_str, function (err, conn) {
        if (err) {
            res.end("Error opening the connection!");
            return;
        }

        conn.queryRaw(query, function (err, results) {
            if (err) {
                res.end("Error running the task query");
                return;
            }
            console.log("row count = " + results.rows.length + "\n");
            for (var i = 0; i < results.rows.length; i++) {
                res.write("Description: " + results.rows[i][0] +
                          " Priority: " + results.rows[i][1] + 
                          " Status: " + results.rows[i][2] + "\n");
            }
            res.end('Done --\n');
        });

    }); // sql.open
});
app.listen(port);

После сохранения содержимого листинга 3 в качестве новой версии server.js откройте командное окно и перейдите в рабочий каталог. Запустите Node.js, используя следующую команду:

node server.js

Как и в предыдущем примере, Node.js загружает файл server.js и открывает конечную точку по адресу http: // localhost: 1337 . Откройте браузер и перейдите по этому адресу, чтобы увидеть текущее содержимое таблицы TaskList.

Although this version of server.js is similar to the earlier version, there is a key difference in how the request is handled. Rather than handling all possible requests with one handler, HTTP-GET requests to the root URL are explicitly handled by the one route defined in this version of the application.

Creating the Jade Templates

Use NPM to install Jade with the following command from your project’s working directory:

NPM install jade

 

As with the earlier installation of the Express module, NPM will download and install Jade for you in the application’s node_modules subdirectory.

As discussed earlier, leveraging Jade frees us from embedding HTML generation in our server-side JavaScript code. For this application, we need three fairly simple page templates that the Jade engine will render as HTML when output is sent to the browser:

  • Index.js is the main page that displays a table of our tasks
  • TaskInput.js is the new task input page
  • Error.js is the generic error page for the application

To get started with some Jade templates, first create a subdirectory named Views in the application working folder. If you’re using my example structure, create a C:\NodeWork\TaskList\Views\ directory.

The first template that we need is for the initial view that displays all of the tasks. Save the contents of Listing 4 as Index.js in the Views subdirectory.

Listing 4.  A Jade template for the TaskList index page

!!! 5
html(lang="en")
  head
    title =pageTitle
  body
    h1 Task List
    - if(tasks.length)
      table
        tr
          th Description
          th Priority
          th Status
            each task in tasks
              tr
                td #{task.description}
                td #{task.priority}
                td #{task.status}
    - else
      p Task list is empty
    p
      a(href="/taskInput") Create Task

 

Note that the file starts with a carriage return. The second line, containing !!! 5, is a directive to Jade to use HTML 5 for rendering the output. Indentation is significant in Jade, and you must use either spaces or tabs (but not both.) The relative indentation is just to indicate hierarchy in the element rendering, and rather than nested tags, a simple declaration is used. For example, a single p rather than a pair of tags (<p>…</p>) is used to indicate a paragraph.

Most of the template is fairly straight-forward if you’re familiar with HTML with the exception of the table. The server.js file will pass the table contents to the template as an array named tasks. This line:

each task in tasks

 

marks the beginning of a loop that renders three columns for each item in the tasks array.

The next template that we need is a simple form to enable a user to enter the description, priority, and status for a task. Save the contents of Listing 5 as TaskInput.js in the Views subdirectory.

Listing 5.  A Jade template for the task input page

!!! 5
html(lang="en")
  head
    title =pageTitle
   body 
      form(method="POST" 
           action="/createTask") 
        h1 Task Input
        p
          p 
            label Description
            br
            input(type="text"
                name="description"
                id="description"
                size="25")
          p 
            label Priority
            br
            input(type="text"
                name="priority" 
                id="priority"
                size="25") 
          p 
            label Status
            br
            input(type="text"
                name="status" 
                id="status"
                size="25")
          br
          input(type="submit"
                name="submit" 
                id="submit"
                value="Submit")

 

In Listing 5, we have a Jade template for a simple form that accepts the three text values required for a new task. The next version of server.js will accept HTTP-POST verbs for the form action /createTask, and will insert tasks into the database.

Finally, the last template is used to provide basic structured error messages to the user. Although we’ve used the built-in console logging functionality included in Node.js, a simple error page will make is easier to debug any issues that you might encounter. Save the contents of Listing 6 as Error.js in the Views subdirectory.

Listing 6.  A Jade template for the error page

!!! 5
html(lang="en")
  head
    title =pageTitle
  body
    h1 Error
    p= error
    p
      a(href="/") Return to task list

Inserting Data into Windows Azure SQL Database

There’s more to working with the database than simply reading data. When you need to insert or update data in the database, you should take steps to avoid injection attacks. For that reason, as a best practice you should parameterize your queries so that malformed input can’t lead to unexpected results. Parameter positions are represented by question marks in a query string, like this:

INSERT INTO Tasks (description, priority, status) VALUES (?, ?, ?)

You’re expected to package the parameter arguments in an array, and pass them immediately after the parameterized query string, like this:

var query = "INSERT INTO Tasks (description, priority, status)" +
            " VALUES (?, ?, ?)";
var params = ["Wash the car", "Low", "New"];
sql.open(conn_str, function (err, conn) {
    ...
    conn.queryRaw(query, params, function (err, results) {
    ...
});

The complete version of server.js that handles inserting and reading tasks is provided in Listing 7. Save the contents of Listing 7 as the final version of server.js.

Listing 7. The complete version of server.js

var express = require('express');
var app = express.createServer();
var sql = require('node-sqlserver');
var conn_str = "Driver={SQL Server Native Client 11.0};" +
               "Server=tcp:[databasename].database.windows.net,1433;" +
               "Database=TaskList;Uid=[username];" +
               "Pwd=[password];Encrypt=yes;Connection Timeout=30";
var query = "SELECT description, priority, status FROM dbo.Tasks";
var port = process.env.port || 1337;
app.use(express.bodyParser());

app.get('/', function (req, res) {
    var taskSet = [];
    sql.open(conn_str, function (err, conn) {
        if (err) {
            res.render('error.jade',
                {
                    pageTitle: 'Error opening the connection!',
                    layout: false,
                    error: 'Could not open the database connection'
                });
            return;
        }

        conn.queryRaw(query, function (err, results) {
            if (err) {
                res.render('error.jade',
                    {
                        pageTitle: 'Error running query!',
                        layout: false,
                        error: 'Error running the task query'
                    });
                return;
            }

            for (var i = 0; i < results.rows.length; i++) {
                taskSet[i] = {
                    description: results.rows[i][0],
                    priority: results.rows[i][1],
                    status: results.rows[i][2]
                };
            }
            res.render('index.jade',
               {
                   pageTitle: 'My tasks',
                   layout: false,
                   tasks: taskSet
               });
        });
    }); // sql.open
});

app.get('/taskInput', function (req, res) {
    res.render('taskInput.jade',
        {
            pageTitle: 'Add a task',
            layout: false
        });
});

app.post('/createTask', function (req, res) {

    if (req.body) {
        var query = "INSERT INTO Tasks (description, priority, status)" +
                 " VALUES (?, ?, ?)";
        var params = [req.body.description,
                      req.body.priority,
                      req.body.status];

        sql.open(conn_str, function (err, conn) {
            if (err) {
                res.render('error.jade',
                    {
                        pageTitle: 'Error running query!',
                        layout: false,
                        error: 'Could not open database connection'
                    });
                return;
            }
            else {
                res.redirect('/');
            }
            conn.queryRaw(query, params, function (err, results) {
                if (err) {
                    res.render('error.jade',
                    {
                        pageTitle: 'Error running query',
                        layout: false,
                        error: 'Database insert failed'
                    });
                    return;
                }
            });
        }); // sql.open
    }
    else {
        res.render('error.jade',
            {
                pageTitle: 'Error posting task',
                layout: false,
                error: 'Posted task not found'
            });
    }
});
app.listen(port);

Run this final version of the application by using the following command:

node server.js

Open a browser and navigate to http://localhost:1337 to see the current contents of the TaskList table. Unlike the earlier versions of the application, the table will be rendered in HTML rather than text, as shown in the figure below.

In addition to displaying the tasks in a table, you can also follow the link to add new tasks to the database.

This is a complete version of the application, running against an instance of Windows Azure SQL Database. In the next section, we’ll deploy the Node.js web application to Windows Azure.

Deploying to Windows Azure

In this section, we’ll create a Windows Azure website, and use Git to publish the application to Azure. Start by going to the Window Azure management portal at  http://manage.windowsazure.com (this is the same site that was used earlier to create the Windows Azure SQL database.) Click on the Web Sites tab to display your current list of web sites, as shown in the figure below.

Click the link to Create a Web Site to enable you to enter a URL for the site. Enter a value that’s meaningful to you; I’ve used TaskList92653, as shown below.

 

The green checkbox is your indication that the URL is available.

Click the Create Web Site link to create the site. After the site is created, it will be added to your list of available websites, as shown in the figure below.

Click the name of the website, in my case TaskList92653, to display the web site management dashboard, shown below.

 

The dashboard displays information about site usage as well as links to commonly performed tasks.

Click the link to set up Git publishing for the web site. If this is the first time that you’ve used Git to deploy to Windows Azure, you’ll be prompted to create a user name and password. A Git repository will be created for you, and details about the Git configuration and necessary commands for deployment will be presented.

The first task for you is to install Git if it’s not on your machine.

After installing Git, open a command prompt, and go to the application’s root directory. In my example, I’m using C:\NodeWork\TaskList. Commit the current version of the application by entering these three commands:

git init
git add .
git commit –m “initial commit”

After your files have been committed, publish the application by typing these two commands, substituting <Git URL> with the values provided on the page:

git remote add azure <Git URL>
git push azure master

Browse to the Windows Azure endpoint for your application. It’s listed on the dashboard page, in my case, http://tasklist92653.azurewebsites.net/. If the deployment was successful, the functionality in Windows Azure will match the local deployment.

Summary

This article has walked through the steps required to use Node with the Windows Azure SQL Database. We’ve also looked at using Node.js with the Express and Jade frameworks, and deployed the application to Windows Azure by publishing our application with Git.