Designing a MailMerge Web API Endpoint with ASP.NET Core in C#
This article shows how to create a Web API endpoint that merges templates with JSON data using TX Text Control .NET Server Core. It shows how to design the endpoint to accept the template, the data, and settings for the merge process.

The Mail
Learn More
The MailMerge class provides very effective ways to merge data into MS Word compatible templates. This updated ultimate guide provides an overview of all the important features and functionalities of the mail merge process.
An Ultimate Guide to Mail Merge with MS Word Documents in C#
Designing the Web API
A typical merge process requires a template, data and settings for the MailMerge class, and additional settings for the generated PDF, such as author information.
Let's start with the Web API endpoint. The following code snippet shows the Web API endpoint that accepts a POST request with a JSON payload containing the template, data, and settings for the merge process.
public class DocumentController : Controller
{
[HttpPost]
public IActionResult Merge(
[FromBody] MergeBody mergeBody)
{
#region Error Checking
// error checking: MergeBody cannot be null
if (mergeBody == null)
{
return BadRequest();
}
// error checking: Template must not be null
if (mergeBody.Template == null)
{
return BadRequest("Template must not be null");
}
#endregion
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl())
{
tx.Create();
// load the template
try {
tx.Load(Convert.FromBase64String(mergeBody.Template), TXTextControl.BinaryStreamType.InternalUnicodeFormat);
}
catch (Exception ex) // format not valid
{
return BadRequest(ex.Message);
}
// create a new MailMerge object
MailMerge mailMerge = new MailMerge()
{
TextComponent = tx,
// set the merge settings
DataSourceCulture = new CultureInfo(mergeBody.MergeSettings.DataSourceCulture),
FormFieldMergeType = (FormFieldMergeType)mergeBody.MergeSettings.FormFieldMergeType,
MergeCulture = new CultureInfo(mergeBody.MergeSettings.MergeCulture),
RemoveEmptyBlocks = mergeBody.MergeSettings.RemoveEmptyBlocks ?? false,
RemoveEmptyFields = mergeBody.MergeSettings.RemoveEmptyFields ?? false,
RemoveEmptyImages = mergeBody.MergeSettings.RemoveEmptyImages ?? false,
RemoveEmptyLines = mergeBody.MergeSettings.RemoveEmptyLines ?? false,
RemoveTrailingWhitespace = mergeBody.MergeSettings.RemoveTrailingWhitespace ?? false,
};
// merge the data
mailMerge.MergeJsonData(mergeBody.MergeData, true);
// save settings
TXTextControl.SaveSettings saveSettings = new TXTextControl.SaveSettings()
{
Author = mergeBody.MergeSettings.Author,
CreationDate = (DateTime)mergeBody.MergeSettings.CreationDate,
CreatorApplication = mergeBody.MergeSettings.CreatorApplication,
DocumentSubject = mergeBody.MergeSettings.DocumentSubject,
DocumentTitle = mergeBody.MergeSettings.DocumentTitle,
LastModificationDate = (DateTime)mergeBody.MergeSettings.LastModificationDate,
UserPassword = mergeBody.MergeSettings.UserPassword
};
byte[] results = null;
// save the document as PDF
tx.Save(out results, TXTextControl.BinaryStreamType.AdobePDF, saveSettings);
// return the merged document as base64 string
return Ok(Convert.ToBase64String(results));
}
}
}
First, we need to do some error handling by checking that there is a MergeBody object and that it has a template and data provided to it. If not, we return a 400 Bad Request response.
Then a Server
The MergeBody Class
The MergeBody class is a simple class that contains the template, data, and settings for the merge process. The following code snippet shows the MergeBody class, the MergeSettings class, and the DocumentSettings class.
using Microsoft.AspNetCore.Mvc;
namespace TXTextControl.DocumentProcessing.Models
{
public class MergeBody
{
[BindProperty(Name = "mergeData")]
public string MergeData { get; set; }
[BindProperty(Name = "template")]
public string Template { get; set; }
[BindProperty(Name = "mergeSettings")]
public MergeSettings MergeSettings { get; set; }
}
public class DocumentSettings
{
[BindProperty(Name = "author")]
public string Author { get; set; }
[BindProperty(Name = "creationDate")]
public DateTime? CreationDate { get; set; }
[BindProperty(Name = "creatorApplication")]
public string CreatorApplication { get; set; }
[BindProperty(Name = "documentSubject")]
public string DocumentSubject { get; set; }
[BindProperty(Name = "documentTitle")]
public string DocumentTitle { get; set; }
[BindProperty(Name = "lastModificationDate")]
public DateTime? LastModificationDate { get; set; }
[BindProperty(Name = "userPassword")]
public string UserPassword { get; set; }
}
public class MergeSettings : DocumentSettings
{
[BindProperty(Name = "removeEmptyFields")]
public bool? RemoveEmptyFields { get; set; }
[BindProperty(Name = "removeEmptyBlocks")]
public bool? RemoveEmptyBlocks { get; set; }
[BindProperty(Name = "removeEmptyImages")]
public bool? RemoveEmptyImages { get; set; }
[BindProperty(Name = "removeTrailingWhitespace")]
public bool? RemoveTrailingWhitespace { get; set; }
[BindProperty(Name = "removeEmptyLines")]
public bool? RemoveEmptyLines { get; set; }
[BindProperty(Name = "formFieldMergeType")]
public int? FormFieldMergeType { get; set; }
[BindProperty(Name = "culture")]
public string Culture { get; set; }
[BindProperty(Name = "dataSourceCulture")]
public string DataSourceCulture { get; set; }
[BindProperty(Name = "mergeCulture")]
public string MergeCulture { get; set; }
}
}
Calling the Web API
Now that we have the Web API endpoint, we can call it from a client application. The following code snippet shows how to call the Web API endpoint using the HttpClient class:
textControl1.Save(out data, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
var MergeBody = new TXTextControl.DocumentProcessing.Models.MergeBody()
{
MergeData = JsonConvert.SerializeObject(customer),
Template = Convert.ToBase64String(data),
MergeSettings = new TXTextControl.DocumentProcessing.Models.MergeSettings()
{
Author = "John Doe",
CreationDate = DateTime.Now,
CreatorApplication = "TX Text Control",
DocumentSubject = "Subject",
DocumentTitle = "Title",
LastModificationDate = DateTime.Now,
UserPassword = "password",
RemoveEmptyFields = true,
RemoveEmptyBlocks = true,
RemoveEmptyImages = true,
RemoveTrailingWhitespace = true,
RemoveEmptyLines = true,
FormFieldMergeType = 1,
Culture = "en-US",
DataSourceCulture = "en-US",
MergeCulture = "en-US"
}
};
var client = new HttpClient();
// convert content to json with lower case properties
var content = new StringContent(JsonConvert.SerializeObject(MergeBody, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), Encoding.UTF8, "application/json");
var response = client.PostAsync("https://localhost:7241/Document/Merge", content).Result;
// get the response as a string
var responseString = response.Content.ReadAsStringAsync().Result;
The HttpClient class is used to send a POST request to the Web API endpoint with the JSON payload containing the template, data, and settings for the merge process. The response is read as a base64-encoded string and saved as a PDF file.
Let's take a look at the JSON payload that is sent to the Web API endpoint:
{
"mergeData":"{\"FirstName\":\"John\",\"LastName\":\"Doe\"}",
"template":"CAcBAA4AAAAAAAAAAAAXAAIAqwBGAGkAcgBzAHQATgBhAG0AZQC7AHQAZQB4AHQAQwBvAG4AdAByAG8AbAAxAAAANgIAAAMAAQABAAEAAAAAAAAAAgCfhwEAAQALAAAAAAAAQAEAkgcAAABQAQAMAAAAAAAAAABAAAAAAAAAAFABAAwADAAAAAAAAEAAAAAAAAAAUDj/AAAAAAAAkAEAAAAAAAACIkFyaWFsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABABgAAAAAAAAAAAAAAAAAZAAgAg8AAAABbgQB3AgBSg0BuBEBJhYBlBoBAh8BcCMB3icBTCwBujABKDUBljkBBD4BAAAAAAAAAAAAFAAAAEYAaQByAHMAdABOAGEAbQBlAAAAAQAHAAAAAAAAACwAAABNAEUAUgBHAEUARgBJAEUATABEAAAARgBpAHIAcwB0AE4AYQBtAGUAAAAAAAAAAAAAAAAAAAAAAAAAAABBAHIAaQBhAGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQEAAABAAEACQYAAQAAAC4AAP//AAAAALcAAQAAAABAAAAAUAEAAgAJBAAAAAA8AABkAAAAAAEAAAAJBAAAAAAAAABkAAAAAAEAAAAJBAAAAAAAAABkAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAEAAQABABgAAAAAAAAAAAAAAAAAAAAAAAEAUwB5AG0AYgBvAGwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQBAAAwAAQA0C8AAOA9AACgBaAFoAWgBQAAAEAAAAAAAAAAAAEAAAABAA4AAAAAAAAAAAAkAQAAAQAAAAAAOP8AAAAAAACQAQAAAAAAAAIiQXJpYWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQByAGkAYQBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAFAAAAAAAAAAAAAAAABkACACDwAAAAFuBAHcCAFKDQG4EQEmFgGUGgECHwFwIwHeJwFMLAG6MAEoNQGWOQEEPgEAAQAJBgABAAAALgAA//8AAAAAtwABAAAAAEAAAABQAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAACQRkAAAAWwBOAG8AcgBtAGEAbABdAAAAUwB5AG0AYgBvAGwAAAAAAABAIAABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQB6AAAAMgAAAABAAAAAADcCNwI3AjcC0C8AAOA9AACgBaAFoAWgBSAKP4AAAAAAAAAAAAEAAAAAAAAAGwEAAAAAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAxgHGAcYBxgEAAABAAAAAQAAAAEAAAABAAAAAAAAAAAAAAA==",
"mergeSettings":{
"removeEmptyFields":true,
"removeEmptyBlocks":true,
"removeEmptyImages":true,
"removeTrailingWhitespace":true,
"removeEmptyLines":true,
"formFieldMergeType":1,
"culture":"en-US",
"dataSourceCulture":"en-US",
"mergeCulture":"en-US",
"author":"John Doe",
"creationDate":"2024-07-12T14:44:27.7222043+02:00",
"creatorApplication":"TX Text Control",
"documentSubject":"Subject",
"documentTitle":"Title",
"lastModificationDate":"2024-07-12T14:44:27.7244266+02:00",
"userPassword":"password"
}
}
Considerations
When designing a Web API for document generation, there are a few things to consider:
- Security
Ensure that authentication and authorization mechanisms are in place to secure the Web API endpoint. - Validation
Prevent injection attacks and other security vulnerabilities by validating input data. - Performance
To improve the responsiveness of the Web API, consider using a queuing system to offload the document generation process to a background task. - Scalability
Use distributed architecture and load balancing to make the Web API scalable. - Monitoring
Monitor the Web API for performance and availability using tools such as Application Insights.
Performance Considerations
Performance is an important consideration when generating large documents on the server side. You may want to consider using a queueing system to offload the document generation process to a background task to improve the responsiveness of the Web API. Depending on template size and data structure, document generation can be a complex, time-consuming task.
When generating documents with many nested merge blocks and 100s or 1000s of pages, this task can take several seconds or even minutes. A typical HTTP request should be returned within milliseconds with no significant delay. We have published a sample that shows how to queue requests and implement a REST web API that calls a web hook after the document has been successfully generated.
Learn More
Depending on the template size and data structure, generating documents can be a complex, time-consuming task. This sample shows how to build an asynchronous, RESTful Web API that calls a WebHook after the document generation process is terminated.
Generating Documents using a RESTful, Asynchronous Web API using WebHooks
Conclusion
In this article, we have seen how to design a Web API for document generation using the MailMerge class. We have also seen how to call the Web API from a client application and some considerations to keep in mind when designing a Web API for document generation.
By following these best practices, you can create a robust and scalable Web API for document generation that meets the needs of your application.
Download the sample project from our GitHub repository and try it out for yourself!
Download and Fork This Sample on GitHub
We proudly host our sample code on github.com/TextControl.
Please fork and contribute.
Requirements for this sample
- TX Text Control .NET Server
- Visual Studio 2022
Related Posts
Use MailMerge in .NET on Linux to Generate Pixel-Perfect PDFs from DOCX…
This article explores how to use the TX Text Control MailMerge feature in .NET applications on Linux to generate pixel-perfect PDFs from DOCX templates. This powerful combination enables…
Generating Dynamic NDAs Using TX Text Control MailMerge in C# .NET
This article demonstrates how to generate dynamic NDAs using TX Text Control MailMerge in C# .NET. It covers the process of creating a template, binding data, and generating the final document.
Designing a Maintainable PDF Generation Web API in ASP.NET Core (Linux) C#…
This article shows how to create a PDF generation Web API in ASP.NET Core on Linux using TX Text Control .NET Server. The clean architecture is used to create a maintainable and testable solution.
Manipulating Table Cells During the MailMerge Process in .NET C#
This article shows how to manipulate table cells during the mail merge process in .NET C#. The FieldMerged event can be used to manipulate the table cells after they are merged.
When to Generate Documents Server-Side Instead of Client-Side: A Focus on…
When it comes to document generation, deciding whether to handle the task server-side or client-side is a key architectural decision for any organization. This article discusses the benefits of…