用Azure函数建立一个二维码生成器
如何建立一个有趣的二维码项目并了解Azure Functions
如果你想学习如何构建Azure Functions,这里有一个有趣的项目。这个项目将是一个有点傻的,但却很有教育意义。
我们将建立一个二维码生成器,100%在Azure函数中运行。互联网上有成千上万的二维码生成器,所以这是一个愚蠢的练习,但我想挑战Azure函数的极限,向你展示它们有多酷,并激励你用它们构建很酷的东西。
在本教程中,你将了解到:
- Azure函数如何工作以及它们有多简单
- 如何用.NET库生成QR码
- 构建这样的东西所需的努力有多少
这很有趣,我希望你能跟上。我们在本教程中要做什么?
- 创建一个Azure函数
- 实现一个QR生成器
- 为它建立一个粗略的前端
- 用Azure Functions核心工具将其部署到Azure。
在本教程结束时,你将拥有一个实用的二维码生成器,它可能会激励你用Azure函数构建更多很酷的东西。它们结构紧凑、简单,而且容易构建。
什么是Azure函数?
Azure函数是在Azure中运行的无服务器应用程序。它们是一些小的代码片段,你可以在没有服务器或复杂实例的情况下执行。大多数Azure函数是 "微服务",只做一个小任务,而且做得很好。
Azure函数还可以响应HTTP触发器,充当一种 "小型API"。它们对HTTP请求的响应就像Web API一样,但对函数的设置要少得多。
我开始听起来像个广告,但这是很了不起的。几年前,这个同样的项目需要你建立一个服务器,或者在某个IIS服务器上建立一个ASP.NET Web API。现在,我们只需几行代码就可以做到,并把它推到云端。Azure函数让你可以专注于构建很酷的东西,而不是所有的设置。
要求
以下是本教程中你需要的东西。我将在Windows 11中构建它,但我们不使用Visual Studio。我将使用dotnet驱动,所以如果你愿意,你可以在Mac或OSX上复制它。你必须在你的机器上安装这个(免费)软件。
你将需要:
- 一个微软Azure账户
- .NET 6
- Azure Functions核心工具
- Azure CLI工具
- Visual Studio Code(或你选择的编辑器)。
那么让我们开始吧!
创建一个新的项目
我们将使用Azure CLI为我们创建一个新的Azure函数,我们将使用dotnet驱动来安装软件。
Visual Studio可以为我们自动完成这些工作,但这样做可以更好地了解所涉及的步骤,并且可以在许多平台上完成。
首先,我们将初始化一个新的函数。我们将使用func 命令来创建一个名为QRCodeGen的新项目。我们将指定工作者的运行时间为 "dotnet",以选择C#作为使用的语言。
在你的项目或资源库文件夹中运行这个命令:
Shell
func init QRCodeGen --worker-runtime dotnet
如果你看一下这个文件夹,这里并没有什么:
我们仍然需要在项目中创建一个Azure函数。Azure函数有一套模板,这取决于你要做什么类型的应用。我们要创建一个HTTP触发器,一个响应HTTP请求的函数。
func new --template "Http Trigger" --name QRCodeGen --authlevel anonymous

如果你使用的是Visual Studio Code,你可能会看到这个消息:
选择 "是 "来安装扩展程序。
现在你会看到一个样本函数,func 为你创建的。
如果你想试试,你可以在本地运行它。键入
Shell
func start
然后你会看到函数启动,控制台会给你一些关于如何使用该应用程序的指导:

正如我们所看到的,我们可以向http://localhost:7071/api/QRCodeGen 发送一个GET或POST。让我们在浏览器中加载它,看看一个简单的GET会返回什么。
我们有一个信息显示:
This HTTP triggered function was executed successfully. Pass a name in the query string or in the request body for a personalized response.
好的,很好。让我们深入研究代码,看看这条消息是什么意思。
C#
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 中的一个值进行检查。如果该变量是空的或空的,它将显示我们刚才看到的信息。
但我们如何填充这个值呢?在上面几行,我们看到查询中的一个参数,我们可以使用:
C#
string name = req.Query["name"];
如果它被填充了,它将显示一条包含名称的消息。我们需要向它发送一个URL,并将name 作为一个参数,就像这样:
http://localhost:7071/api/QRCodeGen?name=Jeremy
所以让我们来试试。
这就是我的信息。所以现在我们知道这个函数是按预期工作的。我们加载一个URL,获得参数并修改输出。
为了简单起见,我们将把这个模型用于我们的QR码发生器。我们将建立我们的URL来监听请求,并在一个GET命令中传递值,然后返回一个QR码。让我们开始生成该QR码。
安装QRCode生成工具
将文本变成一个QR码是相对复杂的。幸运的是,你使用的是.NET。你就不必手工建立你的生成器了。这个问题在很久以前就已经解决了。我们将使用一个库来为我们生成一个.PNG的QR码。我们只需要围绕它写一些代码,建立一个工具来解决我们的问题。
首先,让我们安装Manuel BL的QRCode Generator软件包。我们将使用dotnet驱动来完成。我将指定在写这篇文章时的最新版本。
外壳
dotnet add package Net.Codecrete.QrCodeGenerator --version 2.0.1
QRCode生成器很好用,但只能生成.svgs。由于我们想要一个.PNG(位图)格式,并且希望它能在多种环境下工作,我们需要安装SkiaSharp包。
外壳
dotnet add package SkiaSharp
SkiaSharp包括生成位图的功能。但你必须下载并将这个文件添加到你的项目中:
在你的项目文件夹中创建一个名为QrCodeBitmapExtensions.cs的文件,并将这个文件的内容复制到其中。这将使你能够处理位图,特别是.PNG文件。
现在我们有了我们的QR码生成器工具,所以让我们让它在我们的函数中工作。
创建QR码生成器功能
如果你打开QRCodeGen.cs ,你会看到为我们生成的Azure Function(一个方法)。它看起来会是这样的:
C#
[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 ,你唯一应该看到的是这个:
C#
namespace QRCodeGen
{
public static class QRCodeGen
{
}
}
接下来,让我们添加一个新方法来生成QR码。
首先,在名称装饰器中添加。这将给这个方法一个名字,并改变访问它的URL:
C#
[FunctionName("GenerateQRCode")]
现在,当我们运行我们的应用程序时,URL将是:
http://localhost:7071/api/GenerateQRCode
这将是我们为生成二维码而调用的URL,我们将从JavaScript中调用它:
接下来,创建该方法:
C#
public static async Task<IActionResult> Generate(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ILogger log)
{
}
我们正在创建一个名为Generate的方法。我们将传入一些参数,告诉应用程序我们想要一个HttpTrigger:
[HttpTrigger
而在这个触发器中,我们要传递一些参数。
我们想把我们的授权设置为匿名,这样任何人都可以访问它:
AuthorizationLevel.Anonymous
我们只想接受对这个URL的GET 请求,所以下面的参数指定了这一点:
"get",
然后我们将我们的路由设置为null。如果我们想要一个不同的URL或特定的路由,我们可以在这里指定它。为了简单起见,我们将保持原样:
Route = null)]
接下来,我们将传入一个HttpRequest对象。这将包含来自传入请求的数据,这样我们就可以从中获取参数。当一个GET请求被发送到我们的函数时,我们可以提取诸如头信息、参数等东西:
HttpRequest req
最后,我们将传入一个记录器,这样我们就可以记录信息:
ILogger log
很好,现在我们已经构建了一个方法。让我们把它填满。
从查询字符串中收集信息
用户必须发送他们想变成QR码的文本。这通常是一个URL。我们在早期就决定保持简单的事情。我们将只是从发送到API的查询字符串中收集这些信息。
还记得我们传入方法的HttpRequest吗?我们可以从那里得到我们的查询参数。我们可以建立一个字符串变量,然后从查询字符串中获取参数:
C#
string qrtext = req.Query["qrtext"];
添加以下信息来记录我们的输出。我们要显示发送到我们的qrtext 变量的内容。
C#
log.LogInformation("Generating QR Code for {0}", qrtext);
这样,我们可以仔细检查发送的内容。
现在,让我们生成我们的QR码。
将QR码生成PNG
接下来,我们要把我们的字符串,编码成一个QR码,然后把它输出到PNG。
因此,我们将静态调用我们先前安装的QRCode库。我们将传入我们想要生成的文本和纠错值。中等的应该就可以了。
C#
var qr = QrCode.EncodeText(qrtext, QrCode.Ecc.Medium);
这就是生成一个QR码的全部内容。但它是SVG格式的。我们需要把它转换成PNG,这样我们就可以显示它。
由于我们在上面添加了位图扩展,现在我们的qr 对象有一个方法,可以将QR码转换成PNG,有几个参数。
我们需要添加一个比例。我使用了10,这似乎产生了一个合适的尺寸。
我使用了1作为边界。
而且我把前景(代码)设置为SKColors.Black,背景设置为SKColors.White。
下面是要输入的代码:
C#
var pngout = qr.ToPng(10, 1, SkiaSharp.SKColors.Black, SkiaSharp.SKColors.White);
好了,现在我们有了我们的QR码。这很容易!现在,让我们把它打包成JSON并发送出去吧
创建一个JSON返回对象
我们已经在public static class GenerateQRCode 。 让我们创建另一个类,它将创建一个简单的对象,当API被调用时,我们可以返回。
就在GenerateQRCode类的外面(最后一个}之前)创建一个ReturnObject类。
这个类将有一个名为Image 的单一属性。这将是一个字符串,我们将把我们的PNG编码为文本放入其中。
C#
public class ReturnObject {
public string Image { get; set; }
}
现在回到GenerateQRCode类中,让我们回到刚才的位置。我们将创建一个新的ReturnObject。
C#
var ourResult = new ReturnObject{};
现在,我们将把我们的PNG图像转换成一个Base64字符串,并把这个字符串添加到我们的返回对象中。
C#
ourResult.Image = Convert.ToBase64String(pngout);
简单。现在我们只需将我们的POCO(Plain old class object)作为一个新的JsonResult返回,所以它将JSON返回给调用者。
C#
return new JsonResult(ourResult);
这就是它的作用。在大约七行代码中,我们采取了一个字符串,将其转换为QR码,并将其送出大门。
让我们试试吧。
在本地启动该函数
我们将启动这个函数,让它在本地监听。我们将发送一个带查询的GET请求来测试它。
在命令行中输入以下内容:
Shell
func start
来启动这个函数。
你应该看到像这样的东西:

现在我们将发送一个请求到
http://localhost:7071/api/GenerateQRCode
记得我们在代码中指定的参数是qrtext ,所以我将把它加到最后。
http://localhost:7071/api/GenerateQRCode?qrtext="hello world"
我使用Postman来做这个,所以我可以看到JSON渲染出来的:

而我们做的那点日志显示,它正在为 "hello world "进行渲染。
所以步骤很简单:
- 向我们的URL发送一个GET
- 附加生成的文本并将其添加到qrtext中
- 接受JSON。
下面是我们到目前为止输入的所有代码。
C#
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函数并不是真正为了提供网页而设计的,但这是可能的。在这种情况下,我们可以把它设置好,这样我们就不需要在其他地方托管我们的前端。这只是一个简单的网页,所以让我们来提供它。
首先,我们将创建我们的index.html。
创建一个名为www 的文件夹,并创建一个名为index.html 的文件。
这将是一个超级简单、粗糙的前端--没有什么花哨的东西,只要能完成工作就可以了。
在顶部添加以下内容。
HTML
<!DOCTYPE html>
<html>
<body>
<h2>QR Code Generator</h2>
这就是我们的基本标题。接下来,我们需要添加一个输入,从用户那里获取文本。
HTML
<input id="inputbox" style="width: 250px;" ></input>
<br /><br />
这个输入的id是inputbox,所以我们可以用JavaScript抓取这个文本。我在它后面加了几个换行符,以使其间隔开来。
接下来,我们将创建一个按钮,它将调用一个JavaScript函数。
HTML
<button type="button" onclick="GetQRCode()">Create QR Code</button>
然后,我们将有一个地方,我们将插入QR码。请记住,这将是Base64编码的,这样我们就可以在我们的GetQRCode 函数中用图像填充这个div。
HTML
<div id="demo"></div>
接下来,我们将放入GetQRCode ,该函数会向我们的Azure函数发出一个XMLHttpRequest,并检索JSON。然后解析它,用我们的图片替换 "demo "div。请注意,我们正在添加一个带有头信息的img 标签,这样它就会创建一个我们可以在浏览器中查看的图像。
C#
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函数来提供这个服务。
提供索引页
Azure函数的目的不是为了成为Web服务器。事实上,Azure静态Web应用做得更好,但我们要尽可能地保持简单。一个单一的Azure函数就可以完成这个任务。
我们可以把index.html文件推送出去,这样它就会被浏览器提供。这为我们的应用程序提供了一个小的界面,如果我们愿意的话,我们可以回头建立一个完整的网络应用,利用这个功能,或者把它添加到一个微服务的捆绑中。
再次打开GenerateQRCode.cs,在我们的类的顶部(在[FunctionName("Form")]上面)让我们添加另一个函数。
我们希望这个函数这次能返回一个HttpResponseMessage。我们仍然要把它变成一个HttpTrigger,采用匿名认证,并传入和以前一样的值。
不过这一次,我们需要传入ExecutionContext context 。这给了我们执行的上下文,所以我们可以在本地文件系统中定位文件,我将在后面解释为什么。
另一个是这将返回HttpResponseMessage而不是IActionResult。
C#
[FunctionName("Form")]
public static HttpResponseMessage Form([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
HttpRequest req, ILogger log, ExecutionContext context){
接下来,我们要打开我们放在www 文件夹中的index.html 文件,并将其作为文本全部读入。这就是执行环境的来源。
我们要打开index.html文件,该文件位于应用程序运行的文件系统的www文件夹中。我们用这行代码来做这件事。
C#
string indexPage = File.ReadAllText(context.FunctionAppDirectory + "/www/index.html");
然后我们将创建一个新的HttpResponseMessage,状态代码为OK(200)。
C#
var result = new HttpResponseMessage(HttpStatusCode.OK);
现在我们需要给它添加一些标题,并在 "内容 "中填入我们从索引文件中读取的文本。
C#
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
result.Content = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes(indexPage));
现在我们有了一个带有text/html 头的200 OK消息,而且我们把index.html中的所有文本都添加到头的内容部分。
这是很黑的,但它是有效的。
然后我们简单地返回它。
C#
return result;
所以整个函数看起来像这样:
C#
[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文件中。在一个项目组中,加入以下代码。
HTML
<None Update="www\index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
我把我的代码和其他的文件发布设置一起加进去了。
保存它,然后让我们再次在本地运行我们的Azure函数。
壳
func start
让我们来看看它是如何工作的。
测试我们的前端
现在我的函数已经在本地运行了,所以我可以直接调出一个网页浏览器来。
http://localhost:7071/api/Form
就可以看到它的全貌了:

因此,我将输入一个URL。
这就是我的QR码,临时生成的。太棒了
让我们把它部署到Azure。
部署我们的应用程序
我现在称这个为 "应用程序",因为它是。我们已经超越了一个简单函数的预期功能,只是为了展示Azure函数的强大。让我们来部署这个,看看它是如何工作的。
使用Azure CLI,键入
壳
az login
你会被引导通过网络浏览器登录Azure。
在Azure控制台中,创建一个新的Azure函数应用。你可以随心所欲地命名它。
选择以代码形式发布,并使用以下运行时间栈。你可以选择任何你想要的区域。
我将选择Windows和消费(无服务器)模式。
我将创建一个新的存储账户,并按 "审查和创建"
记下你的细节:
现在我们需要用Azure CLI来发布。我们将通过使用func ,并指定我们要发布一个Azure functionapp,名称,并加上"-nozip "来告诉该命令我们不是在部署应用程序的压缩包,而是源代码。
外壳
func azure functionapp publish qrcodegen --nozip
完成后,你应该看到类似这样的东西:
你需要登录你的Azure门户,加载功能应用并选择 "配置"。
然后找到WEBSITE_RUN_FROM_PACKAGE ,将值设为0 。这将使我们的函数能够从我们发布的文件中运行。
让我们来看看!
在Azure上运行应用程序
部署后,你应该看到这样的屏幕。
表单 "这个URL将为我们的函数调出用户界面。
而且你可以输入你的网站,并从中创建一个二维码!
而且它还能工作!我们自己的二维码生成器100%从Azure函数中运行。
总结
如果你一直在关注,感谢你的坚持!你现在已经有了一个很酷的项目,并对Azure函数有了更深的了解。这个项目的完整源代码在这里。
为网站提供服务并不完全是Azure函数的设计目的,但我希望这是在Azure函数上从头到尾的一个完整解决方案。没有服务器,没有数据库,你也没有几个服务需要跟踪。
我们了解了Azure函数是如何工作的,以及它们可以多么简单。我们在这里没有写多少代码!我们学习了如何用.NET库生成二维码,建立一个小型应用并将其部署到云中。我想尽可能地保持简单,以便进行良好的介绍。如果你要建立一个 "真正的 "应用,我的建议是这样的:
- 在Azure Static Web Apps或类似服务上托管前端
- 对输入的错误进行检查
- 优雅的错误处理和日志记录
- 将图像推入存储,以便以后检索
- 设计API,使其也能被其他类型的应用所使用。