Gerando QRCode no ASP.NET Razor Pages
Gerar um QRCode no Razor Pages é uma tarefa simples quando você combina o ASP.NET Core com a biblioteca QRCoder. Neste artigo, você vai criar uma aplicação que recebe um texto em um formulário, gera a imagem do código e a exibe em outra página, de forma simples e rápida.
O que você vai construir
O projeto mostra como gerar e renderizar um QRCode em uma aplicação Razor Pages no ASP.NET Core 7.
Essa aplicação terá duas páginas. A primeira, GenerateQrCode, exibe um formulário com um campo de texto e um botão.
A segunda, RenderQrCode, recebe o conteúdo enviado, gera a imagem e a mostra para o usuário. Além disso, ela oferece um botão para voltar ao formulário.
Toda a lógica de geração fica em uma classe estática reutilizável. Dessa forma, qualquer outra página pode produzir um QRCode no Razor Pages com uma única chamada.
Todo o código-fonte do exemplo está disponível no repositório render-qrcode-web no GitHub.
Criando a aplicação para o QRCode no Razor Pages
O primeiro passo é criar o projeto a partir do template razor da CLI do .NET:
dotnet new razor -o render-qrcode
cd render-qrcode
O template cria uma aplicação com páginas .cshtml, layout compartilhado, Bootstrap e os scripts de validação do jQuery já configurados.
Segundo a documentação oficial do Razor Pages, esse modelo organiza cada tela em torno de uma página. Por isso, ele torna cenários focados em formulários, como um QRCode no Razor Pages, mais simples do que o padrão MVC.
Se você prefere controllers e views, o artigo gerando QRCode com ASP.NET Core mostra a mesma ideia aplicada ao template MVC.
Instalando os pacotes via NuGet
Para facilitar o trabalho, o projeto usa dois pacotes: QRCoder e System.Drawing.Common.
dotnet add package QRCoder -v 1.4.2
dotnet add package System.Drawing.Common
O QRCoder é uma biblioteca de código aberto, escrita em C#, que implementa a especificação do QR Code e oferece vários renderizadores de imagem.
Já o System.Drawing.Common fornece a classe Bitmap, que o renderizador QRCode da versão 1.4.2 devolve.
Pronto! Agora o projeto tem os pacotes necessários para começar.
Atenção ao System.Drawing.Common
A partir do .NET 6, a Microsoft passou a tratar o System.Drawing.Common como uma biblioteca exclusiva do Windows.
Em outros sistemas operacionais, o uso de Bitmap lança uma PlatformNotSupportedException. Além disso, o .NET 7 removeu a chave de configuração que reativava o suporte ao Linux e ao macOS.
Portanto, o exemplo deste artigo funciona como está apenas em servidores Windows.
Para hospedar em Linux ou em contêineres, o próprio QRCoder oferece renderizadores multiplataforma, como PngByteQRCode e SvgQRCode. Eles geram a imagem sem depender do GDI+.
Organizando o projeto
A ideia aqui é criar uma classe estática chamada QrCodeServices dentro de um arquivo Services.cs.
Essa classe permite reutilizar a geração do QRCode no Razor Pages em diferentes pontos da aplicação:
public static class QrCodeServices
{
}
Centralizar essa lógica também facilita mudanças futuras. Por exemplo, trocar o renderizador ou o nível de correção de erros passa a exigir alteração em um único arquivo.
Estrutura do projeto de QRCode no Razor Pages
Antes de escrever as páginas, vale visualizar onde cada arquivo vai ficar.
Ao final do tutorial, a estrutura relevante do projeto será esta:
render-qrcode/
├── Pages/
│ ├── GenerateQrCode.cshtml
│ ├── GenerateQrCode.cshtml.cs
│ ├── RenderQrCode.cshtml
│ ├── RenderQrCode.cshtml.cs
│ └── Shared/
│ └── _Layout.cshtml
├── Services.cs
└── render-qrcode.csproj
A pasta Pages concentra as telas e os respectivos PageModels. Já o arquivo Services.cs fica na raiz e guarda a classe de geração.
Essa separação mantém as páginas enxutas. Afinal, elas apenas recebem dados e exibem o resultado, enquanto o serviço cuida da imagem.
Gerando o QRCode no Razor Pages
O trecho que cria o QRCode no Razor Pages com o QRCoder é bem simples. Você precisa apenas do QRCodeGenerator e dos dados do código.
public static Bitmap GenerateImage(string content)
{
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q);
var qrCode = new QRCode(qrCodeData);
var qrCodeImage = qrCode.GetGraphic(20);
return qrCodeImage;
}
Primeiro, o QRCodeGenerator transforma o texto em uma matriz de módulos, representada pelo objeto QRCodeData.
Em seguida, a classe QRCode desenha essa matriz em um Bitmap. Já o argumento 20 do método GetGraphic define quantos pixels cada módulo ocupa na imagem final.
Nível de correção de erros
O segundo parâmetro de CreateQrCode define o nível de correção de erros, ou ECC.
O padrão QR Code prevê os níveis L, M, Q e H. Quanto mais alto o nível, maior a parte do código que pode ficar danificada sem impedir a leitura.
O nível Q recupera cerca de 25% dos dados, enquanto o H chega a aproximadamente 30%. Em contrapartida, níveis mais altos geram matrizes maiores para o mesmo conteúdo.
Na prática, o Q oferece um bom equilíbrio para um QRCode no Razor Pages exibido em tela ou impresso em boa qualidade.
Quanto cabe em um QRCode
Um QR Code na maior versão comporta até 2.953 bytes de dados binários no nível L de correção.
Esse limite cai à medida que o nível de correção sobe. Portanto, textos longos combinados com o nível H podem estourar a capacidade do código.
Nesse caso, o QRCoder lança uma exceção ao gerar os dados. Por isso, vale limitar o tamanho do campo no formulário, como veremos nas boas práticas.
Convertendo o bitmap em Base64
Agora vamos escrever um método que entrega o bitmap do QRCode no Razor Pages pronto para uma tag HTML img.
Para isso, primeiro salvamos o System.Drawing.Bitmap em um fluxo de memória. Depois, convertemos esse fluxo em uma sequência de bytes.
Por fim, montamos uma string no formato de data URI, que o atributo src da tag img aceita diretamente:
public static string BitmapToImageSource(string content)
{
var bitmap = GenerateImage(content);
var imageSource = string.Empty;
using (MemoryStream memoryStream = new MemoryStream())
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
byte[] imageBytes = memoryStream.ToArray();
imageSource = $"data:image/png;base64,{Convert.ToBase64String(imageBytes)}";
}
return imageSource;
}
Com essa abordagem, a aplicação não grava nenhum arquivo em disco. Assim, a imagem trafega embutida no próprio HTML da resposta.
Esse formato funciona muito bem para imagens pequenas e geradas sob demanda, como um QRCode no Razor Pages. Para imagens grandes ou reutilizadas, porém, um endpoint dedicado que devolve o PNG costuma aproveitar melhor o cache do navegador.
Classe completa
Como resultado final, a classe ficou com os seguintes métodos:
using QRCoder;
using System.Drawing;
public static class QrCodeServices
{
public static Bitmap GenerateImage(string content)
{
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q);
var qrCode = new QRCode(qrCodeData);
var qrCodeImage = qrCode.GetGraphic(20);
return qrCodeImage;
}
public static string BitmapToImageSource(string content)
{
var bitmap = GenerateImage(content);
var imageSource = string.Empty;
using (MemoryStream memoryStream = new MemoryStream())
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
byte[] imageBytes = memoryStream.ToArray();
imageSource = $"data:image/png;base64,{Convert.ToBase64String(imageBytes)}";
}
return imageSource;
}
}
Criando o formulário do QRCode no Razor Pages
Agora vamos trabalhar na interface que recebe os dados do QRCode no Razor Pages.
Crie uma página chamada GenerateQrCode e adicione um formulário enviado pelo método POST, com um input e um button:
<form method="post">
<div class="form-floating mb-3">
<input asp-for="ContentQrCode" class="form-control" id="floatingInput" placeholder="QR Code content">
<span asp-validation-for="ContentQrCode" class="text-danger"></span>
<label for="floatingInput">QR Code content</label>
</div>
<div class="col-12">
<button asp-page="RenderQrCode" class="btn btn-primary" type="submit">Generate QR Code</button>
</div>
</form>
Repare no atributo asp-page do botão. Ele faz o formulário ser enviado para a página RenderQrCode, e não para a própria página.
Também vamos chamar os scripts do jQuery para validar o formulário no lado do cliente:
@section scripts{
<partial name="_ValidationScriptsPartial" />
}
A página completa fica com o seguinte conteúdo:
@page
@model GenerateQrCodeModel
@{
ViewData["Title"] = "Generate QR Code";
}
<h1>@ViewData["Title"]</h1>
<form method="post">
<div class="form-floating mb-3">
<input asp-for="ContentQrCode" class="form-control" id="floatingInput" placeholder="QR Code content">
<span asp-validation-for="ContentQrCode" class="text-danger"></span>
<label for="floatingInput">QR Code content</label>
</div>
<div class="col-12">
<button asp-page="RenderQrCode" class="btn btn-primary" type="submit">Generate QR Code</button>
</div>
</form>
@section scripts{
<partial name="_ValidationScriptsPartial" />
}
O PageModel do formulário
No Razor Pages, toda página .cshtml vem acompanhada de um arquivo .cshtml.cs de mesmo nome. Nele ficam os códigos em C# que o servidor processa.
Nesse arquivo, criamos uma propriedade do tipo string ligada ao input. O arquivo .cshtml.cs ficou assim:
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
namespace render_qrcode.Pages;
public class GenerateQrCodeModel : PageModel
{
private readonly ILogger<GenerateQrCodeModel> _logger;
public GenerateQrCodeModel(ILogger<GenerateQrCodeModel> logger)
{
_logger = logger;
}
[Required(ErrorMessage = "Please provide the QR Code content!")]
public string ContentQrCode { get; set; } = null!;
public void OnGet()
{
}
}
O atributo [Required] alimenta a validação do cliente. Assim, os scripts do jQuery impedem o envio do formulário com o campo vazio e exibem a mensagem no span.
Renderizando o QRCode no Razor Pages
Agora vamos criar a interface que gera e exibe a imagem.
Adicione uma nova página chamada RenderQrCode, com uma tag img para o código e um button para voltar à página anterior:
@page "~/renderqrcode/{content?}"
@model RenderQrCodeModel
@{
ViewData["Title"] = "QR Code rendering";
}
<h1>@ViewData["Title"]</h1>
<div class="text-center">
<img src=@Model.imageSource class="rounded" alt="QRCode" width="400" height="400">
</div>
<div class="col-12">
<a asp-page="GenerateQrCode"><button class="btn btn-primary">Back</button></a>
</div>
A diretiva @page define uma rota personalizada, /renderqrcode, com um segmento opcional chamado content.
O PageModel da renderização
Agora vamos ao arquivo .cshtml.cs dessa página. Ele precisa de uma propriedade que armazene o src da imagem e de um handler que capture o conteúdo enviado pelo POST.
O resultado fica assim:
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace render_qrcode.Pages;
public class RenderQrCodeModel : PageModel
{
private readonly ILogger<RenderQrCodeModel> _logger;
public RenderQrCodeModel(ILogger<RenderQrCodeModel> logger)
{
_logger = logger;
}
public string imageSource = "";
public void OnPost(string content)
{
// Lê o valor do campo enviado pelo formulário
imageSource = QrCodeServices.BitmapToImageSource(Request.Form["ContentQrCode"]);
}
}
O método OnPost responde ao envio do formulário que solicita o QRCode no Razor Pages. Ele lê o campo ContentQrCode em Request.Form e chama o serviço que devolve a data URI.
Em seguida, a página usa @Model.imageSource no atributo src, e o navegador desenha o QRCode no Razor Pages sem nenhuma requisição adicional.
Como alternativa, você pode decorar uma propriedade com [BindProperty]. Nesse caso, o model binding do ASP.NET Core preenche o valor automaticamente, sem acessar Request.Form diretamente.
Payloads prontos para o QRCode no Razor Pages
Até aqui, o formulário aceita qualquer texto livre. No entanto, muitos leitores de QR Code reconhecem formatos especiais, como URLs e credenciais de rede Wi-Fi.
O QRCoder oferece a classe PayloadGenerator justamente para montar esses formatos sem erros de sintaxe.
Por exemplo, o código abaixo gera o conteúdo de um link e o envia ao gerador:
using static QRCoder.PayloadGenerator;
var generator = new Url("https://github.com/codebude/QRCoder/");
var payload = generator.ToString();
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(payload, QRCodeGenerator.ECCLevel.Q);
Da mesma forma, a classe WiFi produz um código que conecta o celular à rede ao ser lido:
// Nome da rede, senha e tipo de autenticação
var wifi = new WiFi("My-WiFi-Name", "s3cr3t-p4ssw0rd", WiFi.Authentication.WPA);
var wifiPayload = wifi.ToString();
Assim, basta passar a string gerada para o método BitmapToImageSource e exibir o QRCode no Razor Pages normalmente.
Segundo a documentação do projeto, quando você informa apenas um domínio, sem http:// ou https://, o gerador de URL acrescenta o prefixo http:// automaticamente.
Adicionando o link no layout
Para finalizar, adicione a página GenerateQrCode ao menu do layout, no arquivo _Layout.cshtml:
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/GenerateQrCode">Generate QR Code</a>
</li>
Pronto! Agora você tem um gerador de QRCode no Razor Pages com ASP.NET Core.
Como o fluxo da requisição funciona
Vale entender o caminho completo que uma requisição percorre nesse exemplo.
Primeiro, o navegador faz um GET para /GenerateQrCode, e o handler OnGet devolve o formulário vazio.
Quando o usuário clica no botão, o tag helper asp-page já definiu o atributo formaction apontando para /renderqrcode. Por isso, o navegador envia o POST diretamente para a segunda página.
Em seguida, o Razor Pages escolhe o handler OnPost do RenderQrCodeModel, que gera a data URI e a guarda em imageSource.
Por fim, o Razor renderiza o HTML com a imagem embutida. Ou seja, a resposta já chega ao navegador com o código pronto para exibição.
Esse fluxo dispensa JavaScript personalizado, banco de dados e armazenamento de arquivos. Dessa forma, o exemplo continua pequeno e fácil de adaptar.
Testando o QRCode no Razor Pages
Execute a aplicação com dotnet run e acesse o novo item do menu para testar o QRCode no Razor Pages.
Na primeira tela, informe o conteúdo do código, como uma URL ou um texto curto.
Se você enviar o campo vazio, a validação do cliente exibe a mensagem definida no atributo [Required].
Depois do envio, a página RenderQrCode mostra a imagem gerada, pronta para leitura com a câmera do celular.
Alternativa multiplataforma para o QRCode no Razor Pages
Como vimos, o renderizador QRCode depende do Bitmap e, portanto, do Windows.
Se a aplicação precisar rodar em Linux, em contêineres ou no macOS, troque o renderizador por PngByteQRCode. Ele devolve diretamente os bytes do PNG, sem passar pelo GDI+:
using QRCoder;
public static class CrossPlatformQrCodeServices
{
public static string GenerateImageSource(string content)
{
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q);
// Gera o PNG em memória, sem depender do System.Drawing
var qrCode = new PngByteQRCode(qrCodeData);
byte[] imageBytes = qrCode.GetGraphic(20);
return $"data:image/png;base64,{Convert.ToBase64String(imageBytes)}";
}
}
Com essa versão, o pacote System.Drawing.Common deixa de ser necessário. Além disso, o código fica mais curto, porque o MemoryStream sai de cena.
As páginas continuam iguais, já que o método também devolve uma data URI. Em outras palavras, você troca apenas a chamada no OnPost.
Boas práticas para o QRCode no Razor Pages
Limite o tamanho do conteúdo com o atributo [StringLength]. Dessa forma, o usuário recebe uma mensagem clara em vez de uma exceção de capacidade.
Valide também no servidor, verificando ModelState.IsValid antes de gerar o QRCode no Razor Pages. Afinal, a validação do cliente pode ser contornada.
Libere o Bitmap depois do uso com using, porque ele mantém recursos nativos do GDI+ em memória.
Por fim, lembre que o tag helper de formulário do Razor Pages inclui o token antiforgery automaticamente. Assim, o POST para a página de renderização já nasce protegido contra CSRF.
Conclusão
Com poucas linhas de código, o ASP.NET Core e o QRCoder entregam um gerador funcional de QRCode no Razor Pages.
A classe QrCodeServices concentra a geração e a conversão para Base64. Por isso, outras páginas podem reaproveitá-la sem duplicar código.
Lembre-se, contudo, de que o renderizador baseado em System.Drawing.Common exige Windows a partir do .NET 6. Para hospedar em Linux, prefira os renderizadores PngByteQRCode ou SvgQRCode.
O código completo está no repositório do projeto, e a biblioteca pode ser explorada no repositório do QRCoder.
FAQ
Preciso do System.Drawing.Common para usar o QRCoder?
Somente para os renderizadores QRCode e ArtQRCode. Os renderizadores PngByteQRCode e SvgQRCode funcionam sem essa dependência.
O exemplo funciona em Linux ou Docker?
Não como está, porque o Bitmap exige Windows desde o .NET 6. Para Linux, troque o renderizador por PngByteQRCode ou SvgQRCode.
Qual nível de correção de erros devo usar?
O nível Q atende bem à maioria dos casos. Use H quando o código for impresso em superfícies sujeitas a desgaste ou receber um logotipo no centro.
Por que usar uma data URI em vez de salvar a imagem?
A data URI dispensa gravação em disco e requisições extras. Para imagens pequenas geradas sob demanda, isso simplifica bastante a aplicação.
Posso gerar o QRCode dentro do OnGet?
Sim. Basta receber o conteúdo pela rota ou pela query string e chamar o mesmo serviço no handler OnGet.