Поделиться через


Подключение к базе данных SQL Azure и выполнение запросов с использованием пакета Node.js и mssql npm

Применимо к: База данных SQL Azure

В этом кратком руководстве описывается подключение приложения к базе данных в База данных SQL Azure и выполнение запросов с помощью Node.js и mssql. В этом кратком руководстве описан рекомендуемый подход без пароля для подключения к базе данных.

Безпарольные подключения для разработчиков

Подключения без пароля обеспечивают более безопасный механизм доступа к ресурсам Azure. Следующие шаги на высоком уровне используются для подключения к базе данных SQL Azure с помощью подключений без пароля в этой статье:

  • Подготовьте среду для проверки подлинности без пароля.
    • Для локальной среды используется ваша личная идентификация. Этот идентификатор можно извлечь из интегрированной среды разработки, CLI или других локальных средств разработки.
    • Для облачной среды используется управляемое удостоверение .
  • Аутентификация в среде с использованием DefaultAzureCredential из библиотеки удостоверений Azure для получения проверенных учетных данных.
  • Используйте проверенные учетные данные для создания клиентских объектов Azure SDK для доступа к ресурсам.

Дополнительные сведения о безпарольных подключениях можно узнать на хабе безпарольных подключений.

Предварительные условия

Настройка сервера базы данных

Безопасные подключения без паролей к базе данных SQL Azure требуют определенных конфигураций базы данных. Проверьте следующие параметры на логическом сервере в Azure, чтобы правильно подключиться к База данных SQL Azure в локальных и размещенных средах:

  1. Для локальных подключений разработки убедитесь, что ваш логический сервер настроен так, чтобы разрешить подключение IP-адреса вашего локального компьютера и других служб Azure:

    • Перейдите на страницу "Сеть " сервера.

    • Переключите радиокнопку "Выбранные сети", чтобы отобразить дополнительные параметры конфигурации.

    • Выберите "Добавить IPv4-адрес клиента (xx.xx.xx.xx)", чтобы добавить правило брандмауэра, которое будет разрешать подключения с вашего локального IPv4-адреса. Кроме того, можно выбрать + Добавить правило брандмауэра, чтобы ввести конкретный IP-адрес.

    • Убедитесь, что установлен флажок разрешить службам и ресурсам Azure доступ к этому серверу .

      Снимок экрана: настройка правил брандмауэра.

      Предупреждение

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

      Дополнительные сведения о конфигурациях безопасности базы данных см. в следующих ресурсах:

  2. На сервере также должна быть включена проверка подлинности Microsoft Entra и назначена учетная запись администратора Microsoft Entra. Для локальных подключений к разработке учетная запись администратора Microsoft Entra должна быть такой, в которую вы также можете войти в Visual Studio или Azure CLI на локальном компьютере. Вы можете проверить, включена ли аутентификация Microsoft Entra на странице Microsoft Entra ID вашего логического сервера.

    Снимок экрана: включение проверки подлинности Microsoft Entra.

  3. Если вы используете личную учетную запись Azure, убедитесь, что Microsoft Entra настроен и конфигурирован для базы данных Azure SQL, чтобы назначить вашу учетную запись администратором сервера. Если вы используете корпоративную учетную запись, Microsoft Entra ID уже будет настроен для вас.

Создание проекта

Действия, описанные в этом разделе, создают Node.js REST API.

  1. Создайте новый каталог для проекта и перейдите в него.

  2. Инициализировать проект, выполнив следующую команду в терминале:

    npm init -y
    
  3. Установите необходимые пакеты, используемые в примере кода в этой статье:

    npm install mssql express swagger-ui-express yamljs dotenv
    
  4. Откройте проект в Visual Studio Code.

    code .
    
  5. Откройте файл package.json и добавьте следующее свойство и значение после имени , чтобы настроить проект для модулей ESM.

    "type": "module",
    

Создание кода приложения Express.js

Чтобы создать приложение OpenAPI Express.js, создайте несколько файлов:

Файл Описание
.env.development Файл среды только для локальной разработки.
index.js Основной файл приложения, который запускает приложение Express.js через порт 3000.
person.js Express.js файл API маршрута /person для обработки операций CRUD.
openapi.js Маршрут Express.js /api-docs для интерфейса пользователя обозревателя OpenAPI. Корень перенаправляет на этот маршрут.
openApiSchema.yml Файл схемы OpenAPI 3.0, определяющий API Person.
config.js Файл конфигурации для чтения переменных среды и создания соответствующего объекта подключения mssql.
database.js Класс базы данных для обработки операций CRUD SQL Azure с помощью пакета mssql npm.
./vscode/settings.json Игнорировать файлы по шаблону glob во время развертывания.
  1. Создайте файл index.js и добавьте следующий код:

    import express from 'express';
    
    // Import App routes
    import person from './person.js';
    import openapi from './openapi.js';
    
    const port = process.env.PORT || 3000;
    
    const app = express();
    
    // Connect App routes
    app.use('/api-docs', openapi);
    app.use('/persons', person);
    app.use('*', (_, res) => {
      res.redirect('/api-docs');
    });
    
    // Start the server
    app.listen(port, () => {
      console.log(`Server started on port ${port}`);
    });
    
  2. Создайте файл маршрута person.js и добавьте следующий код:

    import express from 'express';
    import { 
      passwordConfig as SQLAuthentication, 
      noPasswordConfig as PasswordlessConfig 
    } from './config.js';
    import { createDatabaseConnection } from './database.js';
    
    const router = express.Router();
    router.use(express.json());
    
    const database = await createDatabaseConnection(SQLAuthentication);
    
    router.get('/', async (req, res) => {
      try {
        // Return a list of persons
    
        const persons = await database.readAll();
        console.log(`persons: ${JSON.stringify(persons)}`);
        res.status(200).json(persons);
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.post('/', async (req, res) => {
      try {
        // add a person
        const person = req.body;
        console.log(`person: ${JSON.stringify(person)}`);
        const rowsAffected = await database.create(person);
        res.status(201).json({ rowsAffected });
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.get('/:id', async (req, res) => {
      try {
        // Get the person with the specified ID
        const personId = req.params.id;
        console.log(`personId: ${personId}`);
        if (personId) {
          const result = await database.read(personId);
          console.log(`persons: ${JSON.stringify(result)}`);
          res.status(200).json(result);
        } else {
          res.status(404);
        }
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.put('/:id', async (req, res) => {
      try {
        // Update the person with the specified ID
        const personId = req.params.id;
        console.log(`personId: ${personId}`);
        const person = req.body;
    
        if (personId && person) {
          delete person.id;
          console.log(`person: ${JSON.stringify(person)}`);
          const rowsAffected = await database.update(personId, person);
          res.status(200).json({ rowsAffected });
        } else {
          res.status(404);
        }
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    router.delete('/:id', async (req, res) => {
      try {
        // Delete the person with the specified ID
        const personId = req.params.id;
        console.log(`personId: ${personId}`);
    
        if (!personId) {
          res.status(404);
        } else {
          const rowsAffected = await database.delete(personId);
          res.status(204).json({ rowsAffected });
        }
      } catch (err) {
        res.status(500).json({ error: err?.message });
      }
    });
    
    export default router;
    

    Для аутентификации без пароля измените параметр, переданный в createDatabaseConnection, с SQLAuthentication на PasswordlessConfig.

    const database = await createDatabaseConnection(PasswordlessConfig);
    
  3. Создайте файл маршрута openapi.js и добавьте следующий код для обозревателя пользовательского интерфейса OpenAPI:

    import express from 'express';
    import { join, dirname } from 'path';
    import swaggerUi from 'swagger-ui-express';
    import yaml from 'yamljs';
    import { fileURLToPath } from 'url';
    
    const __dirname = dirname(fileURLToPath(import.meta.url));
    
    const router = express.Router();
    router.use(express.json());
    
    const pathToSpec = join(__dirname, './openApiSchema.yml');
    const openApiSpec = yaml.load(pathToSpec);
    
    router.use('/', swaggerUi.serve, swaggerUi.setup(openApiSpec));
    
    export default router;
    
  4. Создайте файл openApiSchema.yml и добавьте следующий код, чтобы обозреватель пользовательского интерфейса OpenAPI знал, какие API и модели нужно отобразить:

    openapi: 3.0.0
    info:
      version: 1.0.0
      title: Persons API
    paths:
      /persons:
        get:
          summary: Get all persons
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    type: array
                    items:
                      $ref: '#/components/schemas/Person'
        post:
          summary: Create a new person
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Person'
          responses:
            '201':
              description: Created
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Person'
      /persons/{id}:
        parameters:
          - name: id
            in: path
            required: true
            schema:
              type: integer
        get:
          summary: Get a person by ID
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Person'
            '404':
              description: Person not found
        put:
          summary: Update a person by ID
          requestBody:
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Person'
          responses:
            '200':
              description: OK
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Person'
            '404':
              description: Person not found
        delete:
          summary: Delete a person by ID
          responses:
            '204':
              description: No Content
            '404':
              description: Person not found
    components:
      schemas:
        Person:
          type: object
          properties:
            id:
              type: integer
              readOnly: true
            firstName:
              type: string
            lastName:
              type: string
    

Настройка объекта подключения mssql

Пакет mssql реализует подключение к База данных SQL Azure путем предоставления параметра конфигурации для типа проверки подлинности.

  1. В Visual Studio Code создайте файл config.js и добавьте следующий код конфигурации mssql для проверки подлинности в базе данных SQL Azure.

    import * as dotenv from 'dotenv';
    
    if(process.env.NODE_ENV === 'development') {
      dotenv.config({ path: `.env.${process.env.NODE_ENV}`, debug: true });
    }
    
    // TIP: Port must be a number, not a string!
    const server = process.env.AZURE_SQL_SERVER;
    const database = process.env.AZURE_SQL_DATABASE;
    const port = +process.env.AZURE_SQL_PORT;
    const type = process.env.AZURE_SQL_AUTHENTICATIONTYPE;
    const user = process.env.AZURE_SQL_USER;
    const password = process.env.AZURE_SQL_PASSWORD;
    
    export const noPasswordConfig = {
      server,
      port,
      database,
      authentication: {
        type
      },
      options: {
        encrypt: true
      }
    };
    
    export const passwordConfig = {
      server,
      port,
      database,
      user,
      password,
      options: {
        encrypt: true
      }
    };
    

Создание файла переменной локальной среды

Создание файла .env.development для переменных локальной среды

Добавьте следующий текст и обновите значения для <YOURSERVERNAME> и <YOURDATABASENAME>.

AZURE_SQL_SERVER=<YOURSERVERNAME>.database.windows.net
AZURE_SQL_DATABASE=<YOURDATABASENAME>
AZURE_SQL_PORT=1433
AZURE_SQL_AUTHENTICATIONTYPE=azure-active-directory-default

Примечание.

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

Добавление кода для подключения к База данных SQL Azure

  1. Создайте файл database.js и добавьте следующий код:

    import sql from 'mssql';
    
    let database = null;
    
    export default class Database {
      config = {};
      poolconnection = null;
      connected = false;
    
      constructor(config) {
        this.config = config;
      }
    
      async connect() {
        try {
          this.poolconnection = await sql.connect(this.config);
          this.connected = true;
          console.log('Database connected successfully.');
          return this.poolconnection;
        } catch (error) {
          console.error('Error connecting to the database:', error);
          this.connected = false;
        }
      }
    
      async disconnect() {
        try {
          if (this.connected) {
            await this.poolconnection.close();
            this.connected = false;
            console.log('Database disconnected successfully.');
          }
        } catch (error) {
          console.error('Error disconnecting from the database:', error);
        }
      }
    
      async executeQuery(query) {
        const request = this.poolconnection.request();
        const result = await request.query(query);
    
        return result.rowsAffected[0];
      }
    
      async create(data) {
        const request = this.poolconnection.request();
    
        request.input('firstName', sql.NVarChar(255), data.firstName);
        request.input('lastName', sql.NVarChar(255), data.lastName);
    
        const result = await request.query(
          `INSERT INTO Person (firstName, lastName) VALUES (@firstName, @lastName)`
        );
    
        return result.rowsAffected[0];
      }
    
      async readAll() {
        const request = this.poolconnection.request();
        const result = await request.query(`SELECT * FROM Person`);
    
        return result.recordsets[0];
      }
    
      async read(id) {
        const request = this.poolconnection.request();
        const result = await request
          .input('id', sql.Int, +id)
          .query(`SELECT * FROM Person WHERE id = @id`);
    
        return result.recordset[0];
      }
    
      async update(id, data) {
        const request = this.poolconnection.request();
    
        request.input('id', sql.Int, +id);
        request.input('firstName', sql.NVarChar(255), data.firstName);
        request.input('lastName', sql.NVarChar(255), data.lastName);
    
        const result = await request.query(
          `UPDATE Person SET firstName=@firstName, lastName=@lastName WHERE id = @id`
        );
    
        return result.rowsAffected[0];
      }
    
      async delete(id) {
        const idAsNumber = Number(id);
    
        const request = this.poolconnection.request();
        const result = await request
          .input('id', sql.Int, idAsNumber)
          .query(`DELETE FROM Person WHERE id = @id`);
    
        return result.rowsAffected[0];
      }
    
      async createTable() {
        if (process.env.NODE_ENV === 'development') {
          this.executeQuery(
            `IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Person')
             BEGIN
               CREATE TABLE Person (
                 id int NOT NULL IDENTITY, 
                 firstName varchar(255), 
                 lastName varchar(255)
               );
             END`
          )
            .then(() => {
              console.log('Table created');
            })
            .catch((err) => {
              // Table may already exist
              console.error(`Error creating table: ${err}`);
            });
        }
      }
    }
    
    export const createDatabaseConnection = async (passwordConfig) => {
      database = new Database(passwordConfig);
      await database.connect();
      await database.createTable();
      return database;
    };
    

локальное тестирование приложения.

Приложение готово к локальному тестированию. Убедитесь, что вы вошли в облако Azure в Visual Studio Code с той же учетной записью, что и администратор базы данных.

  1. Запустите приложение с помощью следующей команды. Приложение начинается через порт 3000.

    NODE_ENV=development node index.js
    

    Таблица Person создается в базе данных при запуске этого приложения.

  2. В браузере перейдите в обозреватель OpenAPI по адресу http://localhost:3000.

  3. На странице пользовательского интерфейса Swagger разверните метод POST и выберите "Попробовать".

  4. Измените пример JSON, чтобы включить значения свойств. Свойство ID игнорируется.

    Снимок экрана, показывающий, как протестировать API.

  5. Выберите "Выполнить" , чтобы добавить новую запись в базу данных. API возвращает успешный ответ.

  6. Разверните метод GET на странице пользовательского интерфейса Swagger и выберите "Попробовать". Выберите "Выполнить", а пользователь, который вы только что создали, возвращается.

Настроить проект для развертывания zip-файла

  1. Создайте папку .vscode и создайте файл settings.json в папке.

  2. Добавьте следующее, чтобы игнорировать переменные среды и зависимости во время развертывания ZIP.

    {
        "appService.zipIgnorePattern": ["./.env*","node_modules{,/**}"]
    }
    

Развертывание в Службу приложений Azure

Приложение готово к развертыванию в Azure. Visual Studio Code может создать службу приложение Azure и развернуть приложение в одном рабочем процессе.

  1. Убедитесь, что приложение остановлено.

  2. Войдите в Azure, если вы еще не сделали этого, выбрав Azure: войти в облако Azure в палитре команд ( + +

  3. В окне Обозреватель Azure в Visual Studio Code щелкните правой кнопкой мыши на узле App Services и выберите Создать новое веб-приложение (расширенный).

  4. Чтобы создать Служба приложений, используйте следующую таблицу:

    Подсказка Значение
    Введите глобально уникальное имя для нового веб-приложения. Введите запрос, например azure-sql-passwordless. Добавьте в конце уникальную строку, например 123.
    Выберите группу ресурсов для новых ресурсов. Нажмите кнопку +Создать новую группу ресурсов, а затем выберите имя по умолчанию.
    Выберите стек сред выполнения Выберите версию LTS Node.js стека.
    Select an OS (Выберите ОС). Щелкните Linux.
    Выберите расположение для новых ресурсов. Выберите расположение рядом с вами.
    Выберите план службы приложений Linux. Выберите Создать новый план службы приложений. Затем выберите имя по умолчанию.
    Выберите ценовую категорию. Выберите "Бесплатный" (F1).
    Select an Application Insights resource for your app (Выберите ресурс Application Insights для приложения). выберите Пока пропустить.
  5. Подождите уведомления о создании вашего приложения, прежде чем переходить к дальнейшим действиям.

  6. В Обозревателе Azure разверните узел Службы приложений и щелкните правой кнопкой мыши ваше новое приложение.

  7. Выберите "Развернуть в веб-приложении".

    Снимок экрана: Visual Studio Code в обозревателе Azure с выделенным параметром

  8. Выберите корневую папку проекта JavaScript.

  9. Когда появится всплывающее окно Visual Studio Code, нажмите кнопку "Развернуть".

Когда развертывание завершится, приложение не работает правильно в Azure. Для получения данных необходимо настроить безопасное подключение между Служба приложений и базой данных SQL.

Подключение Службы приложений к базе данных SQL Azure

Для подключения экземпляра службы приложений к базе данных Azure SQL необходимо выполнить следующие действия.

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

Существует несколько средств, доступных для реализации следующих действий.

Соединитель служб — это средство, которое упрощает аутентификацию подключений между различными службами в Azure. Соединитель служб в настоящее время поддерживает подключение Службы приложений к базе данных Azure SQL через команду az webapp connection create sql с использованием Azure CLI. Эта одна команда выполняет три описанных выше шага.

Создайте управляемое удостоверение с помощью Service Connector

Выполните следующую команду в Cloud Shell на портале Azure. Cloud Shell имеет последнюю версию Azure CLI. Замените переменные <> собственными значениями.

az webapp connection create sql \
    -g <app-service-resource-group> \
    -n <app-service-name> \
    --tg <database-server-resource-group> \
    --server <database-server-name> \
    --database <database-name> \
    --system-identity

Проверка параметров приложения Служба приложений

Изменения, внесенные Service Connector, можно проверить в параметрах App Service.

  1. В Visual Studio Code в обозревателе Azure щелкните правой кнопкой мыши вашу службу приложений и выберите Открыть на портале.

  2. Перейдите на страницу Идентификация для Службы приложений. На вкладке "Системное назначение" статус должен быть"Вкл.". Это значение указывает на то, что системное управляемое удостоверение было активировано для вашего приложения.

  3. Перейдите на страницу конфигурации для службы приложений. На вкладке "Параметры приложения" вы увидите несколько переменных среды, которые уже находились в объекте конфигурации mssql .

    • AZURE_SQL_SERVER
    • AZURE_SQL_DATABASE
    • AZURE_SQL_PORT
    • AZURE_SQL_AUTHENTICATIONTYPE

    Не удаляйте или не изменяйте имена или значения свойств.

Тестирование развернутого приложения

Перейдите по URL-адресу приложения, чтобы проверить, работает ли подключение к База данных SQL Azure. URL-адрес вашего приложения можно найти на странице обзора службы приложений.

Лицо, которое вы создали локально, должно быть отображено в браузере. Поздравляем! Теперь приложение подключено к База данных SQL Azure как в локальных, так и в размещенных средах.

Совет

Если во время тестирования вы получаете ошибку внутреннего сервера 500, это может быть связано с конфигурациями сети базы данных. Убедитесь, что логический сервер настроен с параметрами, описанными в разделе "Настройка базы данных ".

Очистка ресурсов

После завершения работы с База данных SQL Azure удалите ресурс, чтобы избежать непредвиденных затрат.

  1. В строке поиска в портале Azure найдите Azure SQL и выберите соответствующий результат.

  2. Найдите и выберите базу данных в списке баз данных.

  3. На странице "Обзор" базы данных Azure SQL выберите Удалить.

  4. На странице Azure "Вы уверены, что хотите удалить...", которая открывается, введите имя вашей базы данных для подтверждения, а затем нажмите Удалить.

Пример кода

Доступен пример кода для этого приложения:

Следующие шаги