Examples

Exemplos C#

Exemplos completos de integração com C#, ASP.NET Core, Entity Framework e mais

Exemplos C#

Exemplos práticos e completos de como integrar o RenderHub em aplicações C# e .NET.

Instalação

dotnet add package Newtonsoft.Json
dotnet add package System.Net.Http

Configuração Básica

Variáveis de Ambiente

Adicione ao appsettings.json:

{
  "RenderHub": {
    "ApiKey": "rh_live_sua_chave_aqui",
    "BaseUrl": "https://renderhub.com/api/v1"
  }
}

Ou use variáveis de ambiente:

export RENDERHUB_API_KEY=rh_live_sua_chave_aqui

Cliente RenderHub

// Services/RenderHubClient.cs

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace MyApp.Services
{
    public class RenderHubClient
    {
        private readonly HttpClient _httpClient;
        private readonly string _apiKey;
        private readonly string _baseUrl;

        public RenderHubClient(string apiKey = null, string baseUrl = null)
        {
            _apiKey = apiKey ?? Environment.GetEnvironmentVariable("RENDERHUB_API_KEY");
            _baseUrl = baseUrl ?? "https://renderhub.com/api/v1";

            if (string.IsNullOrEmpty(_apiKey))
            {
                throw new Exception("RENDERHUB_API_KEY não configurada");
            }

            _httpClient = new HttpClient
            {
                BaseAddress = new Uri(_baseUrl),
                Timeout = TimeSpan.FromSeconds(30)
            };

            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
        }

        public async Task<RenderResponse> RenderAsync(RenderRequest request)
        {
            try
            {
                var json = JsonConvert.SerializeObject(request);
                var content = new StringContent(json, Encoding.UTF8, "application/json");

                var response = await _httpClient.PostAsync("/render", content);

                if (!response.IsSuccessStatusCode)
                {
                    var errorBody = await response.Content.ReadAsStringAsync();
                    throw new RenderHubException(
                        (int)response.StatusCode,
                        $"Erro ao renderizar: {errorBody}"
                    );
                }

                var responseBody = await response.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject<RenderResponse>(responseBody);
            }
            catch (HttpRequestException ex)
            {
                throw new Exception($"Erro de rede: {ex.Message}", ex);
            }
        }

        public async Task<TemplateResponse> CreateTemplateAsync(TemplateRequest template)
        {
            var json = JsonConvert.SerializeObject(template);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync("/templates", content);
            response.EnsureSuccessStatusCode();

            var responseBody = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<TemplateResponse>(responseBody);
        }

        public async Task<TemplateResponse> GetTemplateAsync(string templateId)
        {
            var response = await _httpClient.GetAsync($"/templates/{templateId}");
            response.EnsureSuccessStatusCode();

            var responseBody = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<TemplateResponse>(responseBody);
        }

        public void SavePdf(string pdfBase64, string filePath)
        {
            var pdfBytes = Convert.FromBase64String(pdfBase64);
            File.WriteAllBytes(filePath, pdfBytes);
        }
    }

    // Models
    public class RenderRequest
    {
        [JsonProperty("mode")]
        public string Mode { get; set; }

        [JsonProperty("template_id")]
        public string TemplateId { get; set; }

        [JsonProperty("input_type")]
        public string InputType { get; set; }

        [JsonProperty("data")]
        public object Data { get; set; }

        [JsonProperty("page_size")]
        public string PageSize { get; set; }

        [JsonProperty("orientation")]
        public string Orientation { get; set; }
    }

    public class RenderResponse
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("status")]
        public string Status { get; set; }

        [JsonProperty("pdf_base64")]
        public string PdfBase64 { get; set; }

        [JsonProperty("pages")]
        public int Pages { get; set; }

        [JsonProperty("file_size_kb")]
        public int FileSizeKb { get; set; }

        [JsonProperty("duration_ms")]
        public int DurationMs { get; set; }
    }

    public class TemplateRequest
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("content")]
        public string Content { get; set; }

        [JsonProperty("category")]
        public string Category { get; set; }
    }

    public class TemplateResponse
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("created_at")]
        public DateTime CreatedAt { get; set; }
    }

    public class RenderHubException : Exception
    {
        public int StatusCode { get; }

        public RenderHubException(int statusCode, string message)
            : base(message)
        {
            StatusCode = statusCode;
        }
    }
}

Exemplo 1: Converter HTML para PDF

// Program.cs

using System;
using System.Threading.Tasks;
using MyApp.Services;

class Program
{
    static async Task Main(string[] args)
    {
        var client = new RenderHubClient();

        var html = @"
<!DOCTYPE html>
<html>
  <head>
    <meta charset='UTF-8'>
    <style>
      body {
        font-family: Arial, sans-serif;
        padding: 40px;
        max-width: 800px;
        margin: 0 auto;
      }
      h1 {
        color: #2563eb;
        border-bottom: 3px solid #2563eb;
        padding-bottom: 10px;
      }
      .highlight {
        background: #fef3c7;
        padding: 20px;
        border-radius: 8px;
        margin: 20px 0;
      }
    </style>
  </head>
  <body>
    <h1>Relatório de Vendas - Janeiro 2024</h1>

    <div class='highlight'>
      <h2>Resumo</h2>
      <p>Total de vendas: R$ 150.000,00</p>
      <p>Novos clientes: 42</p>
      <p>Taxa de conversão: 15.8%</p>
    </div>

    <h2>Detalhes</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
  </body>
</html>";

        var request = new RenderRequest
        {
            Mode = "CONVERT",
            InputType = "html",
            Data = html,
            PageSize = "A4",
            Orientation = "portrait"
        };

        var result = await client.RenderAsync(request);

        // Salvar PDF
        client.SavePdf(result.PdfBase64, "relatorio.pdf");

        Console.WriteLine("✅ PDF gerado com sucesso!");
        Console.WriteLine($"Páginas: {result.Pages}");
        Console.WriteLine($"Tamanho: {result.FileSizeKb} KB");
        Console.WriteLine($"Duração: {result.DurationMs}ms");
    }
}

Exemplo 2: Fatura com Template

// Models/Invoice.cs

using System.Collections.Generic;

public class InvoiceData
{
    public string CompanyName { get; set; }
    public string CompanyCnpj { get; set; }
    public string InvoiceNumber { get; set; }
    public string InvoiceDate { get; set; }
    public string CustomerName { get; set; }
    public string CustomerEmail { get; set; }
    public string CustomerAddress { get; set; }
    public List<InvoiceItem> Items { get; set; }
    public string Subtotal { get; set; }
    public string Tax { get; set; }
    public string Total { get; set; }
}

public class InvoiceItem
{
    public string Description { get; set; }
    public int Quantity { get; set; }
    public string UnitPrice { get; set; }
    public string Total { get; set; }
}
// Services/InvoiceService.cs

using System;
using System.Linq;
using System.Threading.Tasks;
using MyApp.Services;

public class InvoiceService
{
    private readonly RenderHubClient _renderHub;
    private readonly string _templateId;

    public InvoiceService(RenderHubClient renderHub, string templateId)
    {
        _renderHub = renderHub;
        _templateId = templateId;
    }

    public async Task<string> GenerateInvoiceAsync(int customerId)
    {
        // Buscar dados (simulado)
        var customer = GetCustomer(customerId);
        var orders = GetOrders(customerId);

        // Mapear dados para o template
        var items = orders.Select(order => new InvoiceItem
        {
            Description = order.Product,
            Quantity = order.Quantity,
            UnitPrice = order.Price.ToString("F2"),
            Total = (order.Quantity * order.Price).ToString("F2")
        }).ToList();

        var subtotal = orders.Sum(o => o.Quantity * o.Price);
        var tax = subtotal * 0.15m;
        var total = subtotal + tax;

        var invoiceData = new InvoiceData
        {
            CompanyName = "Minha Empresa LTDA",
            CompanyCnpj = "12.345.678/0001-99",
            InvoiceNumber = $"2024-{customerId:D5}",
            InvoiceDate = DateTime.Now.ToString("dd/MM/yyyy"),

            CustomerName = customer.Name,
            CustomerEmail = customer.Email,
            CustomerAddress = $"{customer.Address}, {customer.City} - {customer.State}",

            Items = items,

            Subtotal = subtotal.ToString("F2"),
            Tax = tax.ToString("F2"),
            Total = total.ToString("F2")
        };

        // Renderizar PDF
        var request = new RenderRequest
        {
            Mode = "RENDER",
            TemplateId = _templateId,
            Data = invoiceData
        };

        var result = await _renderHub.RenderAsync(request);

        // Salvar PDF
        var filename = $"invoice-{invoiceData.InvoiceNumber}.pdf";
        _renderHub.SavePdf(result.PdfBase64, filename);

        Console.WriteLine($"✅ Fatura {invoiceData.InvoiceNumber} gerada!");
        return filename;
    }

    // Métodos auxiliares (simulados)
    private Customer GetCustomer(int id)
    {
        return new Customer
        {
            Id = id,
            Name = "João Silva Comércio LTDA",
            Email = "joao@example.com",
            Address = "Rua das Flores, 123",
            City = "São Paulo",
            State = "SP"
        };
    }

    private List<Order> GetOrders(int customerId)
    {
        return new List<Order>
        {
            new Order { Product = "Notebook Dell", Quantity = 2, Price = 3500.00m },
            new Order { Product = "Mouse Logitech", Quantity = 5, Price = 150.00m },
            new Order { Product = "Teclado Mecânico", Quantity = 3, Price = 450.00m }
        };
    }
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

public class Order
{
    public string Product { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

Exemplo 3: Integração com ASP.NET Core

// Startup.cs / Program.cs

using Microsoft.Extensions.DependencyInjection;
using MyApp.Services;

// ConfigureServices (ou builder.Services em .NET 6+)
services.AddSingleton<RenderHubClient>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    var apiKey = config["RenderHub:ApiKey"];
    return new RenderHubClient(apiKey);
});
// Controllers/PdfController.cs

using Microsoft.AspNetCore.Mvc;
using MyApp.Services;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class PdfController : ControllerBase
{
    private readonly RenderHubClient _renderHub;

    public PdfController(RenderHubClient renderHub)
    {
        _renderHub = renderHub;
    }

    [HttpPost("generate")]
    public async Task<IActionResult> GeneratePdf([FromBody] PdfRequest request)
    {
        try
        {
            var renderRequest = new RenderRequest
            {
                Mode = "RENDER",
                TemplateId = request.TemplateId,
                Data = request.Data
            };

            var result = await _renderHub.RenderAsync(renderRequest);

            return Ok(new
            {
                success = true,
                pdf_base64 = result.PdfBase64,
                pages = result.Pages,
                file_size_kb = result.FileSizeKb
            });
        }
        catch (RenderHubException ex)
        {
            return StatusCode(ex.StatusCode, new
            {
                error = "Erro ao gerar PDF",
                message = ex.Message
            });
        }
    }

    [HttpPost("download")]
    public async Task<IActionResult> DownloadPdf([FromBody] PdfRequest request)
    {
        try
        {
            var renderRequest = new RenderRequest
            {
                Mode = "RENDER",
                TemplateId = request.TemplateId,
                Data = request.Data
            };

            var result = await _renderHub.RenderAsync(renderRequest);

            var pdfBytes = Convert.FromBase64String(result.PdfBase64);

            return File(pdfBytes, "application/pdf", "document.pdf");
        }
        catch (RenderHubException ex)
        {
            return StatusCode(ex.StatusCode, new { error = ex.Message });
        }
    }
}

public class PdfRequest
{
    public string TemplateId { get; set; }
    public object Data { get; set; }
}

Exemplo 4: Webhooks

// Controllers/WebhookController.cs

using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class WebhookController : ControllerBase
{
    private readonly string _webhookSecret;

    public WebhookController(IConfiguration config)
    {
        _webhookSecret = config["RenderHub:WebhookSecret"];
    }

    [HttpPost]
    public async Task<IActionResult> HandleWebhook()
    {
        // Ler payload
        using var reader = new StreamReader(Request.Body);
        var payload = await reader.ReadToEndAsync();

        // Validar assinatura HMAC
        var signature = Request.Headers["X-RenderHub-Signature"].ToString();

        if (!ValidateSignature(payload, signature))
        {
            return Unauthorized(new { error = "Invalid signature" });
        }

        // Processar webhook
        var data = JsonConvert.DeserializeObject<WebhookPayload>(payload);

        if (data.Status == "DONE")
        {
            Console.WriteLine($"✅ PDF {data.Id} pronto!");

            // Baixar PDF da URL temporária
            using var httpClient = new HttpClient();
            var pdfBytes = await httpClient.GetByteArrayAsync(data.PdfUrl);

            // Salvar em storage
            await File.WriteAllBytesAsync($"pdfs/{data.Id}.pdf", pdfBytes);

            // Atualizar banco de dados
            // await _db.Jobs.UpdateAsync(data.JobId, new { Status = "COMPLETED" });

            // Notificar usuário
            // await _notificationService.NotifyAsync(data.JobId);
        }
        else if (data.Status == "FAILED")
        {
            Console.WriteLine($"❌ Erro ao gerar PDF: {data.Error}");

            // Atualizar banco de dados
            // await _db.Jobs.UpdateAsync(data.JobId, new { Status = "FAILED" });
        }

        // IMPORTANTE: Retornar 200 OK rapidamente
        return Ok(new { received = true });
    }

    private bool ValidateSignature(string payload, string signature)
    {
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_webhookSecret));
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
        var expectedSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();

        return signature == expectedSignature;
    }
}

public class WebhookPayload
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("job_id")]
    public string JobId { get; set; }

    [JsonProperty("status")]
    public string Status { get; set; }

    [JsonProperty("pdf_url")]
    public string PdfUrl { get; set; }

    [JsonProperty("error")]
    public string Error { get; set; }
}

Exemplo 5: Processamento em Lote

// Services/BatchInvoiceService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class BatchInvoiceService
{
    private readonly RenderHubClient _renderHub;
    private readonly string _templateId;

    public BatchInvoiceService(RenderHubClient renderHub, string templateId)
    {
        _renderHub = renderHub;
        _templateId = templateId;
    }

    public async Task GenerateInvoicesAsync(List<int> customerIds)
    {
        foreach (var customerId in customerIds)
        {
            try
            {
                Console.WriteLine($"Processando fatura para cliente {customerId}...");

                var invoiceData = await FetchCustomerDataAsync(customerId);

                var request = new RenderRequest
                {
                    Mode = "RENDER",
                    TemplateId = _templateId,
                    Data = invoiceData
                };

                var result = await _renderHub.RenderAsync(request);

                var filename = $"invoices/invoice-{customerId}.pdf";
                _renderHub.SavePdf(result.PdfBase64, filename);

                Console.WriteLine($"✅ Fatura gerada: {filename}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ Erro para cliente {customerId}: {ex.Message}");
            }

            // Aguardar para não exceder rate limit
            await Task.Delay(200); // 200ms = 5 req/s (bem abaixo do limite)
        }

        Console.WriteLine("\nProcessamento concluído!");
    }

    private async Task<InvoiceData> FetchCustomerDataAsync(int customerId)
    {
        // Simular busca no banco
        await Task.Delay(10);

        return new InvoiceData
        {
            CustomerName = $"Cliente {customerId}",
            InvoiceNumber = $"2024-{customerId}",
            Total = "1500.00"
        };
    }
}

// Uso
var customerIds = new List<int> { 1001, 1002, 1003, 1004, 1005 };
var batchService = new BatchInvoiceService(client, "tpl_invoice_v1");
await batchService.GenerateInvoicesAsync(customerIds);

Exemplo 6: Retry com Exponential Backoff

// Services/RetryHelper.cs

using System;
using System.Threading.Tasks;
using Polly;
using Polly.Retry;

public static class RetryHelper
{
    public static async Task<RenderResponse> RenderWithRetryAsync(
        RenderHubClient client,
        RenderRequest request,
        int maxRetries = 3)
    {
        var retryPolicy = Policy
            .Handle<RenderHubException>(ex => ex.StatusCode == 429) // Rate limit
            .WaitAndRetryAsync(
                maxRetries,
                retryAttempt => TimeSpan.FromSeconds(Math.Min(Math.Pow(2, retryAttempt), 30)),
                onRetry: (exception, timeSpan, retryCount, context) =>
                {
                    Console.WriteLine($"Rate limited. Tentativa {retryCount}/{maxRetries}. Aguardando {timeSpan.TotalSeconds}s...");
                }
            );

        return await retryPolicy.ExecuteAsync(async () =>
        {
            return await client.RenderAsync(request);
        });
    }
}

// Uso
var result = await RetryHelper.RenderWithRetryAsync(client, request);

Ou sem Polly:

public static async Task<RenderResponse> RenderWithRetryAsync(
    RenderHubClient client,
    RenderRequest request,
    int maxRetries = 3)
{
    for (int attempt = 0; attempt < maxRetries; attempt++)
    {
        try
        {
            return await client.RenderAsync(request);
        }
        catch (RenderHubException ex) when (ex.StatusCode == 429)
        {
            if (attempt == maxRetries - 1)
            {
                throw;
            }

            var delay = Math.Min(Math.Pow(2, attempt), 30);
            Console.WriteLine($"Rate limited. Aguardando {delay}s...");
            await Task.Delay(TimeSpan.FromSeconds(delay));
        }
    }

    throw new Exception("Não deveria chegar aqui");
}

Exemplo 7: Background Jobs com Hangfire

dotnet add package Hangfire
dotnet add package Hangfire.SqlServer
// Startup.cs

using Hangfire;
using Hangfire.SqlServer;

services.AddHangfire(config => config
    .UseSqlServerStorage(Configuration.GetConnectionString("HangfireConnection")));

services.AddHangfireServer();

app.UseHangfireDashboard();
// Jobs/GenerateInvoiceJob.cs

using Hangfire;
using System.Threading.Tasks;

public class GenerateInvoiceJob
{
    private readonly RenderHubClient _renderHub;
    private readonly string _templateId;

    public GenerateInvoiceJob(RenderHubClient renderHub, string templateId)
    {
        _renderHub = renderHub;
        _templateId = templateId;
    }

    [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 2, 10, 30 })]
    public async Task ExecuteAsync(int customerId)
    {
        Console.WriteLine($"Processando fatura para cliente {customerId}");

        var invoiceData = await FetchCustomerDataAsync(customerId);

        var request = new RenderRequest
        {
            Mode = "RENDER",
            TemplateId = _templateId,
            Data = invoiceData
        };

        var result = await _renderHub.RenderAsync(request);

        var filename = $"invoices/invoice-{customerId}.pdf";
        _renderHub.SavePdf(result.PdfBase64, filename);

        Console.WriteLine($"Fatura gerada: {filename}");
    }

    private async Task<InvoiceData> FetchCustomerDataAsync(int customerId)
    {
        // Buscar do banco de dados
        await Task.Delay(10);
        return new InvoiceData
        {
            CustomerName = $"Cliente {customerId}",
            InvoiceNumber = $"2024-{customerId}",
            Total = "1500.00"
        };
    }
}
// Disparar jobs

var customerIds = new List<int> { 1001, 1002, 1003, 1004, 1005 };

foreach (var customerId in customerIds)
{
    BackgroundJob.Enqueue<GenerateInvoiceJob>(job => job.ExecuteAsync(customerId));
}

Console.WriteLine($"{customerIds.Count} jobs adicionados à fila");

Exemplo 8: Entity Framework + Múltiplos Clientes

// Services/InvoiceGeneratorService.cs

using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;

public class InvoiceGeneratorService
{
    private readonly AppDbContext _db;
    private readonly RenderHubClient _renderHub;
    private readonly string _templateId;

    public InvoiceGeneratorService(
        AppDbContext db,
        RenderHubClient renderHub,
        string templateId)
    {
        _db = db;
        _renderHub = renderHub;
        _templateId = templateId;
    }

    public async Task GenerateAllInvoicesAsync()
    {
        // Buscar clientes ativos com seus pedidos
        var customers = await _db.Customers
            .Where(c => c.Active)
            .Include(c => c.Orders)
            .ThenInclude(o => o.Product)
            .ToListAsync();

        foreach (var customer in customers)
        {
            if (!customer.Orders.Any())
            {
                continue;
            }

            // Mapear dados
            var invoiceData = new InvoiceData
            {
                CustomerName = customer.Name,
                CustomerEmail = customer.Email,
                InvoiceNumber = $"{DateTime.Now.Year}-{customer.Id}",
                Items = customer.Orders.Select(order => new InvoiceItem
                {
                    Description = order.Product.Name,
                    Quantity = order.Quantity,
                    UnitPrice = order.Product.Price.ToString("F2"),
                    Total = (order.Quantity * order.Product.Price).ToString("F2")
                }).ToList(),
                Total = customer.Orders
                    .Sum(o => o.Quantity * o.Product.Price)
                    .ToString("F2")
            };

            // Gerar PDF
            try
            {
                var request = new RenderRequest
                {
                    Mode = "RENDER",
                    TemplateId = _templateId,
                    Data = invoiceData
                };

                var result = await _renderHub.RenderAsync(request);

                var filename = $"invoices/{customer.Id}-{invoiceData.InvoiceNumber}.pdf";
                _renderHub.SavePdf(result.PdfBase64, filename);

                Console.WriteLine($"✅ Fatura gerada para {customer.Name}: {filename}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ Erro para {customer.Name}: {ex.Message}");
            }
        }
    }
}

// DbContext
public class AppDbContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<Product> Product { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}

Próximos Passos

On this page