Вот забавный проект, если вы хотите узнать, как создавать функции Azure Functions. Этот проект будет немного глупым, но познавательным.
Мы создадим генератор QR-кодов, который будет работать на 100% в функции Azure. В интернете есть тысячи генераторов QR-кодов, так что это глупое упражнение, но я хотел расширить границы возможностей Azure Function, показать вам, насколько они круты, и вдохновить вас на создание крутых вещей с их помощью.
Если вы предпочитаете посмотреть видео, оно есть здесь:
- В этом руководстве вы узнаете:
- Что такое Azure Function?
- Требования
- Создание нового проекта
- Установите инструменты для генерации QRCode
- Создание функции генератора QR-кода
- Сбор информации из строки запроса.
- Генерация QR-кода в PNG
- Создание объекта возврата JSON
- Запуск функции локально
- Создание передней части
- Обслуживание индексной страницы
- Тестирование нашего фронт-энда
- Развертывание нашего приложения
- Запуск приложения на Azure
- Заключение
В этом руководстве вы узнаете:
- Как работают функции Azure Functions и насколько они просты.
- Как генерировать QR-коды с помощью библиотек .NET
- Как мало усилий требуется для создания чего-то подобного.
Это будет весело, и я надеюсь, что вы последуете за мной. Что мы будем делать в этом уроке:
- Создадим функцию Azure Function
- Реализуем QR-генератор
- Создадим для него грубый фронтенд
- развернем его в Azure с помощью Azure Functions Core Tools.
К концу этого урока у вас будет функциональный генератор QR-кодов, и это может вдохновить вас на создание новых интересных вещей с помощью Azure Functions. Они компактны, просты и легко создаются.
Полный исходный код этого проекта вы можете найти здесь.
Что такое Azure Function?
Azure Functions — это бессерверные приложения, которые выполняются в Azure. Это небольшие фрагменты кода, которые можно выполнять без сервера или сложного экземпляра. Большинство Azure Functions — это «микросервисы», которые выполняют одну небольшую задачу и делают это хорошо.
Функции Azure Functions также могут отвечать на HTTP-запросы, выступая в качестве своего рода «мини API». Они отвечают на HTTP-запросы точно так же, как веб-интерфейс API, но при использовании функций требуется гораздо меньше настроек.
Я начинаю говорить как реклама, но это потрясающе. Много лет назад для такого же проекта требовалось установить сервер или настроить ASP.NET Web API где-нибудь на сервере IIS. Теперь мы можем сделать это в несколько строк кода и перенести в облако. Функции Azure Functions позволяют сосредоточиться на создании классных вещей, а не на всей этой настройке.
Требования
Вот что вам понадобится для этого урока. Я буду создавать это в Windows 11, но мы не будем использовать Visual Studio. Я буду использовать драйвер dotnet, поэтому вы можете повторить это на Mac или OSX, если захотите. На вашей машине должно быть установлено это (бесплатное) программное обеспечение.
Вам понадобятся:
- Учетная запись Microsoft Azure
- .NET 6
- Основные инструменты Azure Functions
- Инструменты Azure CLI
- Visual Studio Code (или редактор по вашему выбору)
Итак, давайте начнем!
Создание нового проекта
Мы будем использовать Azure CLI для создания новой функции Azure для нас, и мы будем использовать драйвер dotnet для установки программного обеспечения.
Visual Studio может автоматизировать это для нас, но если сделать это таким образом, то мы получим лучшее представление о необходимых шагах, и это можно сделать на многих платформах.
Сначала мы инициализируем новую функцию. С помощью команды func
мы создадим новый проект с именем QRCodeGen. Мы укажем рабочую среду выполнения как «dotnet», чтобы выбрать C# в качестве используемого языка.
Выполните эту команду в папке проекта или репозиториев:
func init QRCodeGen --worker-runtime dotnet
Если вы посмотрите в папку, то здесь не так много:
Нам все еще нужно создать Azure Function внутри проекта. У Azure Functions есть набор шаблонов, в зависимости от типа приложения, которое вы создаете. Мы хотим создать HTTP-триггер — функцию, которая отвечает на HTTP-запрос.
func new --template "Http Trigger" --name QRCodeGen --authlevel anonymous
Если вы используете Visual Studio Code, вы можете увидеть это сообщение:
Выберите «Да» для установки расширений.
Теперь вы увидите пример функции, которую func
создал для вас:
Вы можете запустить ее локально, если хотите опробовать. Введите
func start
После этого вы увидите запуск функции, а консоль даст вам некоторые указания по использованию приложения:
Как мы видим, мы можем отправить GET или POST на http://localhost:7071/api/QRCodeGen
. Давайте загрузим его в браузер и посмотрим, что возвращает простой GET:
У нас появляется сообщение:
This HTTP triggered function was executed successfully. Pass a name in the query string or in the request body for a personalized response.
Отлично. Давайте покопаемся в коде и посмотрим, что означает это сообщение.
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
Как видно из приведенного выше кода, выполняется проверка на наличие значения в переменной name
. Если эта переменная пуста или равна нулю, будет выведено сообщение, которое мы только что видели.
Но как нам заполнить это значение? Несколькими строками выше мы видим параметр в запросе, который мы можем использовать:
string name = req.Query["name"];
Если параметр заполнен, он отобразит сообщение с указанным именем. Нам нужно отправить ему URL-адрес с name
в качестве параметра следующим образом:
http://localhost:7071/api/QRCodeGen?name=Jeremy
Так что давайте попробуем это:
И вот мое сообщение. Теперь мы знаем, что функция работает так, как ожидалось. Мы загружаем URL, получаем параметр и изменяем вывод.
Для простоты мы будем использовать эту модель для нашего генератора QR-кодов. Мы создадим наш URL для прослушивания запросов, передадим значение в команде GET, а затем вернем QR-код. Давайте начнем с генерации этого QR-кода.
Установите инструменты для генерации QRCode
Превращение текста в QR-код является относительно сложной задачей. К счастью, вы используете .NET. Вам не придется создавать генератор вручную. Эта проблема была решена уже давно. Мы воспользуемся библиотекой, которая сгенерирует для нас QR-код в формате .PNG. Нам останется только написать вокруг нее немного кода и создать инструмент, решающий нашу проблему.
Во-первых, давайте установим пакет QRCode Generator от Manuel BL. Мы сделаем это с помощью драйвера dotnet. Я укажу последнюю версию на момент написания этой статьи.
dotnet add package Net.Codecrete.QrCodeGenerator --version 2.0.1
Генератор QRCode работает отлично, но может генерировать только .svgs. Поскольку нам нужен формат .PNG (растровый), и мы хотим, чтобы он работал в разных средах, нам нужно установить пакет SkiaSharp:
dotnet add package SkiaSharp
SkiaSharp включает в себя возможность генерировать растровые изображения. Но вы должны скачать и добавить этот файл в свой проект:
https://github.com/manuelbl/QrCodeGenerator/blob/master/Demo-SkiaSharp/QrCodeBitmapExtensions.cs
Создайте файл с именем QrCodeBitmapExtensions.cs в папке вашего проекта и скопируйте в него содержимое этого файла. Это позволит вам работать с растровыми изображениями, а именно с файлами .PNG.
Теперь у нас есть инструменты генератора QR-кода, поэтому давайте заставим его работать в нашей функции.
Создание функции генератора QR-кода
Если вы откроете QRCodeGen.cs
, вы увидите сгенерированную для нас функцию Azure Function (метод). Она будет выглядеть следующим образом:
[FunctionName("QRCodeGen")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
Давайте удалим этот метод. Единственное, что вы должны увидеть в QRCodeGen.cs
, это следующее:
namespace QRCodeGen
{
public static class QRCodeGen
{
}
}
Далее давайте добавим новый метод для генерации QR-кодов.
Сначала добавьте декоратор name. Это даст методу имя и изменит URL для доступа к нему.
[FunctionName("GenerateQRCode")]
Теперь, когда мы запустим наше приложение, URL будет таким:
http://localhost:7071/api/GenerateQRCode
Это будет URL, который мы вызовем для генерации QR-кода, и мы вызовем его из JavaScript.
Далее создайте метод:
public static async Task<IActionResult> Generate(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ILogger log)
{
}
Мы создаем метод с именем Generate. Мы передаем некоторые параметры, чтобы сообщить приложению, что нам нужен HttpTrigger:
[HttpTrigger
И внутри этого триггера мы передаем некоторые параметры.
Мы хотим установить авторизацию на анонимную, чтобы любой мог получить к нему доступ:
AuthorizationLevel.Anonymous
Мы хотим принимать только GET
запросы к этому URL, поэтому следующий параметр указывает на это:
"get",
Затем мы установим наш маршрут в null. Если нам нужен другой URL или конкретный маршрут, мы можем указать его здесь. Для простоты мы оставим все как есть:
Route = null)]
Далее мы передадим объект HttpRequest. Он будет содержать данные из входящего запроса, чтобы мы могли получить из него параметры. Когда GET-запрос отправляется в нашу функцию, мы можем извлечь из него такие вещи, как заголовки, параметры и многое другое.
HttpRequest req
Наконец, мы передадим логгер, чтобы мы могли записывать сообщения.
ILogger log
Отлично, теперь у нас есть созданный метод. Давайте заполним его.
Сбор информации из строки запроса.
Пользователь должен отправить текст, который он хочет превратить в QR-код. Обычно это URL. Мы решили не усложнять задачу. Мы просто соберем эту информацию из строки запроса, отправленного API.
Помните HttpRequest, который мы передали в метод? Мы можем получить наш параметр Query из него. Мы можем создать строковую переменную, а затем взять параметр из строки запроса:
string qrtext = req.Query["qrtext"];
Добавьте следующее сообщение для регистрации нашего вывода. Мы хотим отобразить то, что было отправлено в нашу переменную qrtext
:
log.LogInformation("Generating QR Code for {0}", qrtext);
Таким образом, мы можем дважды проверить, что было отправлено.
Теперь давайте сгенерируем наш QR-код.
Генерация QR-кода в PNG
Далее мы хотим взять нашу строку, закодировать ее в QR-код, а затем экспортировать его в PNG.
Поэтому мы сделаем статический вызов библиотеки QRCode, которую мы установили ранее. Мы передадим текст, который хотим сгенерировать, и значение коррекции ошибок. Среднее значение должно подойти.
var qr = QrCode.EncodeText(qrtext, QrCode.Ecc.Medium);
Это все, что требуется для генерации QR-кода. Но он находится в формате SVG. Нам нужно преобразовать его в PNG, чтобы мы могли его отобразить.
Поскольку мы добавили вышеуказанное расширение растрового изображения, теперь наш объект qr
имеет метод для преобразования QR-кода в PNG с несколькими параметрами.
Нам нужно добавить масштаб: Я использовал 10, что позволило получить приличный размер.
Я использовал 1 для границы.
И я установил передний план (код) на SKColors.Black, а фон на SKColors.White.
Вот код для ввода:
var pngout = qr.ToPng(10, 1, SkiaSharp.SKColors.Black, SkiaSharp.SKColors.White);
Итак, теперь у нас есть наш QR-код. Это было легко! Теперь давайте упакуем его в JSON и отправим!
Создание объекта возврата JSON
Мы создавали этот код внутри public static class GenerateQRCode
. Давайте создадим еще один класс, который создаст простой объект, который мы сможем вернуть при вызове API.
Сразу за пределами класса GenerateQRCode (перед последним } ) создайте класс ReturnObject.
Этот класс будет иметь одно свойство Image
. Это будет строка, в которую мы закодируем наш PNG в виде текста.
public class ReturnObject {
public string Image { get; set; }
}
Теперь вернемся в класс GenerateQRCode и вернемся к тому, на чем мы остановились. Создадим новый объект ReturnObject:
var ourResult = new ReturnObject{};
Теперь возьмем наше PNG-изображение, преобразуем его в строку Base64 и добавим эту строку в наш возвращаемый объект:
ourResult.Image = Convert.ToBase64String(pngout);
Легко. Теперь мы просто вернем наш POCO (Plain old class object) как новый JsonResult, так что он вернет JSON вызывающей стороне:
return new JsonResult(ourResult);
И на этом все. Всего за семь строк кода мы получаем строку, преобразуем ее в QR-код и отправляем его за дверь.
Давайте попробуем это сделать.
Запуск функции локально
Мы запустим функцию и заставим ее слушать локально. Для проверки мы отправим GET-запрос с запросом.
Введите в командной строке следующее:
func start
Чтобы запустить функцию.
Вы должны увидеть что-то вроде этого:
Теперь мы отправим запрос на
http://localhost:7071/api/GenerateQRCode
Помните, что параметр, который мы указали в нашем коде, был qrtext
, поэтому я добавлю его в конец:
http://localhost:7071/api/GenerateQRCode?qrtext="hello world"
Я использую Postman для этого, поэтому я могу видеть JSON, выведенный на экран:
И небольшое логирование, которое мы сделали, показывает, что это рендеринг для «hello world»:
Итак, шаги просты:
- Отправляем GET на наш URL
- Добавьте текст для генерации и добавьте его в qrtext
- Принять JSON.
Вот весь код, который мы ввели до сих пор:
public static class QRCodeGen
{
[FunctionName("GenerateQRCode")]
public static async Task<IActionResult> GenerateQRCode(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ILogger log)
{
string qrtext = req.Query["qrtext"];
log.LogInformation("Generating QR Code for {0}", qrtext);
var qr = QrCode.EncodeText(qrtext, QrCode.Ecc.Medium);
var pngout = qr.ToPng(10, 1, SkiaSharp.SKColors.Black, SkiaSharp.SKColors.White);
var ourResult = new ReturnObject{};
ourResult.Image = Convert.ToBase64String(pngout);
return new JsonResult(ourResult);
}
}
public class ReturnObject {
public string Image { get; set; }
}
И это фантастика, но мы еще не закончили. Нам все еще нужно развернуть его. Но прежде чем мы это сделаем, давайте создадим небольшой, простой фронт-енд на JavaScript для взаимодействия с этой функцией.
Создание передней части
Мы хотим, чтобы этот генератор QR-кодов был полностью автономным. Хотя Azure Functions на самом деле не предназначены для обслуживания веб-страниц, это возможно. В данном случае мы можем настроить это так, чтобы нам не нужно было размещать наш фронт-энд где-то еще. Это простая веб-страница, поэтому давайте ее создадим.
Сначала мы создадим наш index.html.
Создайте папку www
и создайте файл index.html
.
Это будет очень простой, грубый фронт-энд — ничего вычурного, просто достаточно для работы.
Добавьте в верхней части следующее:
<!DOCTYPE html>
<html>
<body>
<h2>QR Code Generator</h2>
Это наш основной заголовок. Далее нам нужно добавить ввод, чтобы получить текст от пользователя.
<input id="inputbox" style="width: 250px;" ></input>
<br /><br />
Этот вход имеет идентификатор inputbox, поэтому мы можем получить этот текст с помощью JavaScript. Я добавил пару переносов строк после него, чтобы разделить его на части.
Далее мы создадим кнопку, которая будет вызывать функцию JavaScript:
<button type="button" onclick="GetQRCode()">Create QR Code</button>
Затем у нас будет место, куда мы вставим QR-код. Помните, что он будет закодирован в Base64, чтобы мы могли заполнить этот div изображением в нашей функции GetQRCode
.
<div id="demo"></div>
Далее мы вставим функцию GetQRCode
, которая делает XMLHttpRequest обратно в нашу Azure Function и получает JSON. Затем она анализирует его и заменяет div «demo» нашим изображением. Обратите внимание, что мы добавляем тег img
с информацией заголовка, чтобы создать изображение, которое можно просмотреть в браузере.
function GetQRCode() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var ourJSON = JSON.parse(this.responseText);
document.getElementById("demo").innerHTML = "<img src="data:image/png;base64, " + ourJSON.image + "">";
}
};
input = document.getElementById('inputbox').value
xhttp.open("GET", "/api/GenerateQRCode?qrtext=" + input, true);
xhttp.send();
}
</script>
Отлично! Теперь у нас есть индексная страница, на которой будет нарисован небольшой пользовательский интерфейс для нашего генератора QRCode.
Я знаю, что мы можем добавить коррекцию/обработку ошибок или использовать для этого библиотеку, но я хочу сделать все как можно проще.
Далее нам нужно обслужить это с помощью нашей функции Azure Function.
Обслуживание индексной страницы
Функции Azure Functions не предназначены для работы в качестве веб-серверов. На самом деле, Azure Static Web Apps справляются с этой задачей гораздо лучше, но мы пытаемся сделать все как можно проще. Одна единственная функция Azure Function для выполнения этой задачи.
Мы можем расшарить файл index.html, чтобы он обслуживался браузером. Это дает небольшой интерфейс для нашего приложения, и мы можем вернуться позже и создать полноценное веб-приложение, использующее преимущества этой функции, или добавить ее в набор микросервисов, если захотим.
Снова откройте GenerateQRCode.cs, и в верхней части нашего класса (над [FunctionName(«Form»)]) добавим еще одну функцию.
На этот раз мы хотим, чтобы эта функция возвращала сообщение HttpResponseMessage. Мы все еще собираемся сделать ее HttpTrigger, с анонимной аутентификацией, и передать те же значения, что и раньше.
Однако на этот раз нам нужно передать ExecutionContext context
. Это дает нам контекст выполнения, чтобы мы могли находить файлы в локальной файловой системе, и я немного объясню, почему.
Еще один момент — вместо IActionResult будет возвращено HttpResponseMessage.
[FunctionName("Form")]
public static HttpResponseMessage Form(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log, ExecutionContext context)
{
Далее мы откроем файл index.html
, который мы поместили в папку www
, и прочитаем его как текст. Именно отсюда берется контекст выполнения.
Мы хотим открыть файл index.html, который находится в папке www в файловой системе, где запущено приложение. Мы сделаем это с помощью этой строки кода:
string indexPage = File.ReadAllText(context.FunctionAppDirectory + "/www/index.html");
Затем мы создадим новое сообщение HttpResponseMessage с кодом состояния OK (200):
var result = new HttpResponseMessage(HttpStatusCode.OK);
Теперь нам нужно добавить к нему заголовки и заполнить «содержимое» текстом, который мы считали из нашего индексного файла:
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
result.Content = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes(indexPage));
Теперь у нас есть сообщение 200 OK с заголовком text/html, и весь текст из нашего index.html добавлен в раздел содержимого заголовка.
Это довольно халтурно, но работает.
Затем мы просто возвращаем его:
return result;
Таким образом, вся функция выглядит следующим образом:
[FunctionName("Form")]
public static HttpResponseMessage Form(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log, ExecutionContext context)
{
string indexPage = File.ReadAllText(context.FunctionAppDirectory + "/www/index.html");
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
result.Content = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes(indexPage));
return result;
}
Теперь, поскольку мы представляем этот внешний файл (index.html), который должен быть включен в сборку, нам нужно добавить его в наш файл .csproj.
В группе ItemGroup добавьте следующий код:
<None Update="wwwindex.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Я добавил свой код вместе с другими настройками публикации файлов:
Сохраните его, и давайте снова запустим нашу функцию Azure Function Locally:
func start
И посмотрим, как это работает.
Тестирование нашего фронт-энда
Теперь у меня есть моя функция, запущенная локально, и я могу просто открыть веб-браузер:
http://localhost:7071/api/Form
и вот она во всей своей красе:
Итак, я ввожу URL,
И вот мой QR-код, временно сгенерированный. Потрясающе!
Давайте развернем его в Azure.
Развертывание нашего приложения
Теперь я называю это «приложением», потому что так оно и есть. Мы вышли за рамки ожидаемой функциональности простой функции, просто чтобы показать, насколько мощными могут быть функции Azure Functions. Давайте развернем его и посмотрим, как оно работает.
С помощью Azure CLI введите
az login
Вы будете направлены через веб-браузер для входа в Azure.
В консоли Azure Console создайте новое приложение Azure Function App. Вы можете назвать его так, как вам захочется.
Выберите публикацию в виде кода и используйте следующий стек Runtime. Вы можете выбрать любой регион по своему усмотрению.
Я выберу Windows и модель Consumption (Serverless):
Я создам новую учетную запись хранилища и нажму «рассмотреть и создать».
Запишите свои данные:
Теперь нам нужно опубликовать информацию с помощью Azure CLI. Мы сделаем это с помощью команды func
, указав, что хотим опубликовать azure functionapp, имя, и добавим «—nozip», чтобы сообщить команде, что мы развертываем не zip-файл приложения, а исходный код.
func azure functionapp publish qrcodegen --nozip
По окончании работы вы должны увидеть что-то вроде этого.
Вам нужно будет войти в портал Azure, загрузить функциональное приложение и выбрать «конфигурация».
Затем найдите WEBSITE_RUN_FROM_PACKAGE
и установите значение 0
. Это позволит нашей функции запускаться из файлов, которые мы опубликовали.
Давайте посмотрим!
Запуск приложения на Azure
После развертывания вы должны увидеть экран, подобный этому:
URL «Form» — это тот, который вызовет наш пользовательский интерфейс для функции:
И вы можете вбить свой сайт и создать из него QR-код!
И это работает!!! Наш собственный генератор QR-кодов, который работает на 100% из Azure Function.
Заключение
Если вы следили за нами, спасибо, что не отступили! Теперь у вас есть классный проект и лучшее знакомство с Azure Functions.
Обслуживание веб-сайтов — это не совсем то, для чего предназначены функции Azure, но я хотел, чтобы это было полное решение от начала до конца на базе функции Azure Function. Никаких серверов, никаких баз данных, и у вас нет нескольких служб, за которыми нужно следить.
Мы узнали, как работают функции Azure Functions и насколько простыми они могут быть. Мы не написали здесь много кода! Мы узнали, как генерировать QR-коды с помощью библиотек .NET, создать мини-приложение и развернуть его в облаке. Я хотел сделать это как можно проще для хорошего введения. Если бы вы создавали «настоящее» приложение, вот что я бы порекомендовал:
- Разместить фронт-энд на Azure Static Web Apps или аналогичном сервисе.
- Проверка ошибок на входе
- Грациозная обработка ошибок и ведение журнала
- Передача изображений в хранилище для последующего извлечения.
- Разработка API таким образом, чтобы его могли использовать и другие типы приложений.
Надеюсь, это было интересно! Если вы хотите узнать больше о Azure Functions, у нас есть несколько отличных курсов на Pluralsight:
- Основы Azure Functions
- Microsoft Azure Developer: Выбор решения для хранения данных
- Microsoft Azure Developer: Внедрение Azure Functions
И снова вот видеоверсия этого руководства:
Вопросы? Комментарии? Дайте мне знать!