C# / ASP.NET
In this chapter, you’ll integrate Oicana into a C# web service using ASP.NET Core. ASP.NET Core is Microsoft’s modern, cross-platform framework for building web applications and APIs. We’ll create a simple web service that compiles your Oicana template to PDF and serves it via an HTTP endpoint.
Let’s start with a fresh ASP.NET project by executing dotnet new webapi in a new directory. The starter project has a single endpoint defined in Program.cs. We can try that endpoint out in the swagger UI. Start up the service (dotnet run) and open the URL printed in the terminal. If the page is empty, navigate to /swagger. In the swagger UI, expand the /weatherforecast endpoint, press “Try it out”, then “Execute”. This will send an HTTP request to the running ASP.NET service and return made up weather data.
New service endpoint
Section titled “New service endpoint”We will define a new endpoint to compile our Oicana template to a PDF and return the PDF file to the user.
-
Create a new directory in the .NET project called
templatesand copyexample-0.1.0.zipinto that directory. -
Add the
OicanaNuGet package as a dependency withdotnet add package Oicana --prerelease. -
Read the template file and prepare it for compilation at the beginning of
Program.cs:Part of Program.cs using System.Text.Json.Nodes;using Oicana.Config;using Oicana.Inputs;using Oicana.Template;var templateFile =await File.ReadAllBytesAsync("templates/example-0.1.0.zip");var template = new Template(templateFile); -
Replace the generated
/weatherforecastendpoint with the following:Part of Program.cs app.MapPost("compile", () =>{var stream = template.Compile(new Dictionary<string, JsonNode>(),new Dictionary<string, BlobInput>(),ExportOptions.Pdf(),new CompilationOptions(CompilationMode.Development));var now = DateTimeOffset.Now;return Results.File(fileStream: stream,contentType: "application/pdf",fileDownloadName: $"example_{now:yyyy_MM_dd_HH_mm_ss_ffff}.pdf");}).WithOpenApi();This code defines a new POST endpoint at
/compile. For every request, it compiles the template to PDF with two empty input dictionaries and returns the file. We useCompilationMode.Developmenthere to demonstrate how the template falls back to the development value you defined for theinfoinput ({ "name": "Chuck Norris" }). In a later step we will explicitly set a value for the input.
After restarting the service and refreshing the swagger UI, you should see the new endpoint. Open up the endpoint description and click “Try it out” and “Execute” to send a request to the server. You should see a successful response with a download button for the PDF file.
About performance
Section titled “About performance”The PDF generation should not take longer than a couple of milliseconds. You can look at the request duration in the network tab of your browser’s debugging tools for an estimation. The first request to an ASP.NET service can be significantly slower than later ones, because ASP.NET does some preparation during the first request.
For a better measurement of the compilation speed on your machine, you can use a Stopwatch in the endpoint code.
Passing inputs from C#
Section titled “Passing inputs from C#”Now let’s use the template with inputs that you defined in the previous chapter. First, make sure to update the packed template in your ASP.NET project. Run oicana pack in the template directory and replace example-0.1.0.zip in the ASP.NET project with the new file.
Our compile endpoint is currently calling the template’s Compile method with empty dictionaries. This compiles the template without any explicit inputs. The first dictionary could contain JSON inputs (key to JsonNode) and the second blob inputs (key to BlobInput). Now we’ll provide an input value and switch to production mode.
Change the endpoint to set the name input you defined earlier.
app.MapPost("compile", () =>{ var jsonInputs = new Dictionary<string, JsonNode> { ["info"] = JsonSerializer.Deserialize<JsonNode>("{ \"name\": \"Baby Yoda\" }")! }; var stream = template.Compile( jsonInputs, new Dictionary<string, BlobInput>(), ExportOptions.Pdf(), new CompilationOptions(CompilationMode.Production)); var now = DateTimeOffset.Now; // ... more code from before});Notice that we switched to CompilationMode.Production now that we’re providing explicit input values. Production mode is the recommended default for all document compilation in your application - it ensures you never accidentally generate a document with test data. In production mode, the template will never fall back to development values. If an input value is missing in production mode and the input does not have a default value, the compilation will fail unless your template handles none values for that input.
Calling the endpoint now, will result in a PDF with “Baby Yoda” instead of “Chuck Norris”. Building on this minimal service, one could set input values based on database entries or the request payload. Take a look at the open source ASP.NET example project on GitHub for a more complete showcase of the Oicana C# integration.
Complete code at the end of this chapter
using System.Text.Json;using System.Text.Json.Nodes;using Oicana.Config;using Oicana.Inputs;using Oicana.Template;
var templateFile = await File.ReadAllBytesAsync("templates/example-0.1.0.zip");var template = new Template(templateFile);
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI();}
app.MapPost("compile", () =>{ var jsonInputs = new Dictionary<string, JsonNode> { ["info"] = JsonSerializer.Deserialize<JsonNode>("{ \"name\": \"Baby Yoda\" }")! }; var stream = template.Compile( jsonInputs, new Dictionary<string, BlobInput>(), ExportOptions.Pdf(), new CompilationOptions(CompilationMode.Production)); var now = DateTimeOffset.Now; return Results.File( fileStream: stream, contentType: "application/pdf", fileDownloadName: $"example_{now:yyyy_MM_dd_HH_mm_ss_ffff}.pdf" );}).WithOpenApi();
app.Run();