Generate Word documents from JSON data
Posted by admin
Creating Documents is universal truth across organizations. Every organization either big or small
C# is a programming language that supports Object Oriented Programming. Therefore, it is actively preferred by many institutions. There are many libraries available in the market for PDF generation. Almost all of them are HTML based and have several drawbacks including the need for a lot of developer’s time.
Template-based PDF generation is the preferred approach for agile organizations. Business users can use existing templates as-is. EDocGen is the best API in this segment. In this article, we integrate of C# application with the REST API.
The application is developed with Asp.Net Core 5. So let's get started.
Before diving into the code part of the application, let me tell you the working logic of the application I developed. The application is a web project. In short, the application enables the conversion of a file from JSON format to pdf format, which is requested by the client to be converted to pdf format. The converted file is sent to the client's email address by e-mail.
The client will send a file in JSON format to the Asp.Net Core Web API I wrote and an e-mail address that the converted file will reach.
Now let's go to the project setup.
Firstly we will create an ASP.NET Core Web API application from a Visual Studio application.
Then we will give a project name and select the .Net Core version of the project. In this project, we will choose .Net Core 5.0.
We have successfully created our project. Now we will install the packages that we will use in our project. Let's install the following packages via Nuget Manager.
● Newtonsoft.JSON
● RestSharp
● StackExchange.Redis
Finally, we will prefer Redis cache as the cache mechanism in the application. Instead of installing Redis cache on our computer, we will run it in a docker container. For this, let's open a file named 'docker-compose.yml' in the file location of the application and paste the following codes into it.
version: '3'
|
Then let's open a terminal in the file location and run Redis in a docker
container with the command:
docker compose up -d |
The project structure will be as follows:
DocumentController: Handle client’s post request with JSON file and email.
AuthenticationService: Check token is in the Redis cache.
LoginService: Get a token with username and password.
CacheService: Generate caching operations.
DocumentService: Send JSON file to ‘/generate/bulk’ service for generating a file with pdf format.
EmailService: Send generated file with pdf format to ‘/output/email’ service for emailing to the client.
FileUploadService: Save the file in ‘/Sources’ folder
It is the first step of the flow. AuthenticationService runs first in every request made to the endpoint. In AuthenticationService, first, the token value is searched in the Redis cache. If the token exists in the cache, the file generation process continues. If there is no token value in the cache, LoginService is activated and a token is obtained with username/password information. The resulting token is set to Redis cache with a TTL (Time-to-Live) of 3 hours.
AuthenticationService
public class AuthenticationService
{
private readonly CacheService cacheService;
private readonly LoginService loginService;
public AuthenticationService(CacheService cacheService, LoginService loginService)
{
this.cacheService = cacheService;
this.loginService = loginService;
}
public async Task setCredential()
{
if (isNotTokenCached())
{
await setTokenToCache();
}
}
private bool isNotTokenCached()
{
var result = cacheService.Get(RedisConstant.PDF_GENERATOR_TOKEN);
return string.IsNullOrEmpty(result);
}
private async Task setTokenToCache()
{
var token = await loginService.loginAccount();
cacheService.Set(RedisConstant.PDF_GENERATOR_TOKEN, token, TimeSpan.FromHours(3));
}
}
LoginService
public class LoginService
{
private readonly string username;
private readonly string password;
public LoginService(IConfiguration configuration)
{
this.username = configuration.GetSection("UserInfo:Username").Value;
this.password = configuration.GetSection("UserInfo:Password").Value;
}
public async Task loginAccount()
{
var client = new RestClient("https://app.edocgen.com");
var request = new RestRequest("/login", Method.Post);
request.AddHeader("Content-Type", "application/json");
request.AddJsonBody(
new
{
username = username,
password = password
});
RestResponse response;
try
{
response = await client.ExecuteAsync(request);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw new LoginException($"Error occurred while getting token with username: {username}");
}
validateResponse(response);
return JsonConvert.DeserializeObject(response.Content).token;
}
private void validateResponse(RestResponse response)
{
if (isNotResponseValid(response))
throw new InvalidServiceResponseException("Error occurred while generating document");
}
private bool isNotResponseValid(RestResponse response)
{
return !(response != null && response.StatusCode == System.Net.HttpStatusCode.OK);
}
}
This is the next step after the authentication step. In this step, we will perform validation of the JSON format file sent by the client and save the file in the file '/Sources' folder.
After this process, the following service will go to the step that will request the creation of the JSON file in pdf format.
Url |
|
Method |
HTTP POST |
Parameters |
● documentId(string, formData): id of the template. ● format(string, formData) : output format docx or pdf. Default is docx. ● outputFileName(string, formData): file name for the output file. ● inputFile(file, formData): file containing marker values. Suppoerts JSON, XLSX and XML. |
Headers |
● x-access-token(string, header): authorization header as obtained by calling login ● Content-Type(string header): multipart/form-data |
If the request is successful, the output generation step will start.
DocumentService
public class DocumentService
{
private readonly CacheService cacheService;
private readonly EmailService emailService;
private readonly FileUploadService fileUploadService;
private readonly AuthenticationService authenticationService;
public DocumentService(CacheService cacheService, EmailService emailService,
FileUploadService fileUploadService, AuthenticationService authenticationService)
{
this.cacheService = cacheService;
this.emailService = emailService;
this.fileUploadService = fileUploadService;
this.authenticationService = authenticationService;
}
public async Task executeDocumentProcess(FileUploadDto fileUploadDto, string email)
{
try
{
FileUpload.validateFile(fileUploadDto);
fileUploadService.saveFileToFolder(fileUploadDto);
FileRequestModel fileRequestModel = createFileRequestModel(fileUploadDto);
var response = await generateDocument(fileRequestModel);
await emailService.processOutput(fileRequestModel, email);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw new FileGenerationException("Error occurred while generating document");
}
}
private async void setServiceCredential()
{
await authenticationService.setCredential();
}
private FileRequestModel createFileRequestModel(FileUploadDto fileUploadDto)
{
var token = cacheService.Get(RedisConstant.PDF_GENERATOR_TOKEN);
var filePath = @"./Sources/" + fileUploadDto.file.FileName;
var fileName = fileUploadDto.file.FileName + DateTime.Now.ToString();
FileRequestModel fileRequestModel = new FileRequestModel()
{
token = token,
filePath = filePath,
fileName = fileName
};
return fileRequestModel;
}
private bool isNotResponseValid(RestResponse response) {
return !(response != null && response.StatusCode == System.Net.HttpStatusCode.OK);
}
private async Task generateDocument(FileRequestModel fileRequestModel)
{
var client = new RestClient("https://app.edocgen.com");
var request = new RestRequest("/api/v1/document/generate/bulk", Method.Post);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("x-access-token", fileRequestModel.token);
request.AddParameter("documentId", FileGenerationConstants.DEFAULT_TEMPLATE_ID);
request.AddFile("inputFile", fileRequestModel.filePath);
request.AddParameter("outputFileName", fileRequestModel.fileName);
request.AddParameter("format", FileGenerationConstants.PDF_OUTPUT_FORMAT);
var response = await client.ExecuteAsync(request);
validateResponse(response);
return response;
}
private void validateResponse(RestResponse response)
{
if (isNotResponseValid(response))
throw new InvalidServiceResponseException($"Error occurred as a result of service call: {response.ResponseUri} , " +
$"message: {response.ErrorMessage} , " +
$"exception: {response.ErrorException}");
}
}
}
This is the next step after converting the JSON formatted file sent by the client to pdf. In this step, we will run a 10-second Thread.sleep before output is generated in EmailService. This is due to the fact that there may be a delay in generating the file with pdf format.
The first step in the output generation phase will be to get the id information of the pdf file created in the previous step. For this, we will send a request to the service below.
Url |
https://app.edocgen.com/api/v1 /api/v1/output/name/{output_file_name} |
Method |
HTTP GET |
Headers |
● x-access-token(string, header): authorization header as obtained by calling login ● Content-Type(string header): multipart/form-data |
We obtain the id information from the output information obtained as a result of this service call. After obtaining the id information of the generated pdf file, we will proceed to the e-mailing step.
The service we will use to send e-mails is as follows.
Url |
https://app.edocgen.com/api/v1 /api/v1/output/email |
Method |
HTTP POST |
Body |
{ “outId”:”string”, “emailId”:”string” } |
Headers |
● x-access-token(string, header): authorization header as obtained by calling login ● Content-Type(string header): multipart/form-data |
We will set the ID of the pdf obtained in the previous step in the field “outId” in the body of the email service. We will add the e-mail address information sent by the customer to the "emailId" field and make a request to the service.
EmailService
public class EmailService
{
private CacheService cacheService;
public EmailService(CacheService cacheService)
{
this.cacheService = cacheService;
}
public async Task processOutput(FileRequestModel fileRequestModel, string email)
{
try
{
var outputId = getOutputId(fileRequestModel);
var client = new RestClient("https://app.edocgen.com");
var request = new RestRequest("/api/v1/output/email", Method.Post);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("x-access-token", fileRequestModel.token);
request.AddJsonBody(
new
{
outId = outputId,
emailId = email
});
var response = await client.ExecuteAsync(request);
validateResponse(response);
}
catch (Exception ex)
{
throw new ProcessOutputException(ex.Message);
}
}
private string getOutputId(FileRequestModel fileRequestModel)
{
Thread.Sleep(10000);
var client = new RestClient("https://app.edocgen.com");
var request = new RestRequest($"/api/v1/output/name/{fileRequestModel.fileName}.{FileGenerationConstants.PDF_OUTPUT_FORMAT}.{FileGenerationConstants.ZIP_OUTPUT_FORMAT}", Method.Get);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("x-access-token", fileRequestModel.token);
var response = client.Execute(request);
validateResponse(response);
var outputModelDto = JsonConvert.DeserializeObject(response.Content);
validateOutputResponse(outputModelDto);
return outputModelDto.output[0]._id;
}
private void validateResponse(RestResponse response)
{
if (isNotResponseValid(response))
throw new InvalidServiceResponseException($"Error occurred as a result of service call: {response.ResponseUri} , " +
$"message: {response.ErrorMessage} , " +
$"exception: {response.ErrorException}");
}
private bool isNotResponseValid(RestResponse response)
{
return !(response != null && response.StatusCode == System.Net.HttpStatusCode.OK);
}
private void validateOutputResponse(OutputModelDto outputModelDto)
{
if (isNotOutputResponseValid(outputModelDto))
throw new InvalidOutputResponseException("Output model is not valid");
}
private bool isNotOutputResponseValid(OutputModelDto outputModelDto)
{
return !(outputModelDto != null && outputModelDto.output.Any());
}
This is the step where the file in JSON format and email information sent from the client are handled. In this step, we'll start the authentication process first, then continue the document generation process.
DocumentController
[Route("api/[controller]/generate")]
[ApiController]
public class DocumentController : ControllerBase
{
private readonly AuthenticationService authenticationService;
private readonly DocumentService documentService;
public DocumentController(DocumentService documentService, AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
this.documentService = documentService;
}
[HttpPost]
[Route("{email}")]
public async Task generateDocument([FromForm] FileUploadDto fileUploadDto, string email)
{
await authenticationService.setCredential();
await documentService.executeDocumentProcess(fileUploadDto, email);
return new ResponseModel() {
isSuccess = true,
statusCode = 200,
message = $"The file named {fileUploadDto.file.FileName} converted to {FileGenerationConstants.PDF_OUTPUT_FORMAT} format successfully. Sent to {email} address."
};
}
After developing our application according to the flow and structure we designed, now it's time to test it. We will use the postman client for testing.
Let's run the application. After we run it, we will send a request to the “api/document/generate/{email}” endpoint we created. We will send this JSON file to our endpoint.
JSON_Data.json
[
{
"Invoice_Number": "SBU-2053501",
"Invoice_Date": "31-07-2020",
"Terms_Payment": "Net 15",
"Company_Name": "Company A",
"Billing_Contact": "A-Contact1",
"Address": "New york, United States",
"Logo":"62b83ddcd406d22dc7516b53",
"para": "61b334ee7c00363e11da3439",
"Email":"[email protected]",
"subtemp": "62c85b97f156ce4fbdb01bcb",
"ITH": [{
"Heading1":"Item Description",
"Heading2": "Amount"
}],
"IT": [{
"Item_Description": "Product Fees: X",
"Amount": "5,000"
}]
},
{
"Invoice_Number": "SBU-2053502",
"Invoice_Date": "31-07-2020",
"Terms_Payment": "Net 15",
"Company_Name": "Company B",
"Billing_Contact": "B-Contact2",
"Address": "Seattle, United States",
"Logo":"62b83ddcd406d22dc7516b53",
"para": "61b334ee7c00363e11da3439",
"Email":"[email protected]",
"subtemp": "62c85b97f156ce4fbdb01bcb",
"ITH": [{
"Heading1":"Item Description",
"Heading2": "Amount"
}],
"IT": [{
"Item_Description": "Product Fees: X",
"Amount": "5,000"
},
{
"Item_Description": "Product Fees: Y",
"Amount": "6,000"
}]
}
]
Now let's test with the postman as follows.
We see that we have received a successful response. Let's check our email address now.
We have tested that EDocGen successfully converts our JSON file to pdf files and sends mail.
We have come to the end of this article. This time, we experienced how fast and effectively the document generate services developed by EDocGen work through the C# language. See you in the next post.
Posted by admin
Creating Documents is universal truth across organizations. Every organization either big or small
Posted by admin
If your organization is creating a large number of fillable PDF or read-only PDF documents from Excel
Posted by admin
Generate PDF documents by auto-filling templates with SQL Server data.