The MailMerge TX Text Control .NET Server for ASP.NET
DocumentServer Namespace
MailMerge Class
The MailMerge class is a .NET component that can be used to effortlessly merge template documents with database content in .NET projects, such as ASP.NET web applications, web services or Windows services.
class provides very powerful document generation capabilities based on a template and JSON data to create MS Word DOCX files or PDF documents. You can specify settings such as removing blank spaces or empty fields during the merge process. But how do you design a Web API with all these options, and how do you pass the template and data to the endpoint?

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));
}
}
}
view raw test.cs hosted with ❤ by GitHub

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 ServerTextControl TX Text Control .NET Server for ASP.NET
TXTextControl Namespace
ServerTextControl Class
The ServerTextControl class implements a component that provide high-level text processing features for server-based applications.
object is created and the template is loaded. The MailMerge class is initialized with the given settings and the merge process is started. The resulting document is saved as a PDF document with the given settings and returned as a base64-encoded string.

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; }
}
}
view raw test.cs hosted with ❤ by GitHub

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;
view raw test.cs hosted with ❤ by GitHub

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"
}
}
view raw test.json hosted with ❤ by GitHub

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!