Building a .NET RESTful Service with Dynamsoft's Document, Barcode, and MRZ SDKs

RESTful service can be accessed from anywhere with an internet connection, irrespective of platform or device. Deploying a commercial SDK as a RESTful service can simplify integration, improve accessibility, and provide better control over the deployment environment and usage. In this article, we’ll delve into how you can effortlessly integrate Dynamsoft’s Document, Barcode, and MRZ SDKs into a RESTful service with .NET.

Prerequisites

Initiate a .NET Web API Project

In .NET SDK, a Web API template is used to create a web service that follows the REST architectural style. We can use the following command to quickly create a new project in the terminal:

dotnet new webapi -n DynamsoftRestfulService

Then install the dependencies for the project:

cd DynamsoftRestfulService
dotnet add package Twain.Wia.Sane.Scanner --version 2.0.1
dotnet add package Dynamsoft.DotNet.CaptureVision.Bundle --version 3.4.1000
dotnet add package Newtonsoft.Json --version 13.0.3

dotnet add package OpenCvSharp4 --version 4.6.0.20220608
dotnet add package OpenCvSharp4.Extensions --version 4.5.5.20211231
dotnet add package OpenCvSharp4.runtime.win --version 4.6.0.20220608

The OpenCV package is used to decode the image stream uploaded by the client.

By default, the application will only respond to requests originating from the same machine on which it’s running. This means it listens only to localhost (127.0.0.1). To allow the application to listen to requests from other machines, we need to add the following code to the Program.cs file:

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(System.Net.IPAddress.Any, 5000);
});

CORS (Cross-Origin Resource Sharing) is a mechanism that allows a web application running at one origin to access the resources from a server running at a different origin. We need to enable CORS and configure it to accept requests from other origins.

  1. Install the required package:

       dotnet add package Microsoft.AspNetCore.Cors
    
  2. Add the following code to the Program.cs file:

       builder.Services.AddCors(options =>
       {
           options.AddPolicy("AllowSpecificOrigin",
               builder =>
               {
                   builder.AllowAnyOrigin()
                     .AllowAnyHeader()
                     .AllowAnyMethod();
               });
       });
    
       app.UseCors("AllowSpecificOrigin");
    

Now, run dotnet run to start the app, then open your browser and go to http://localhost:5000/WeatherForecast. If you see the following response, the app is functioning as expected:

[
    {
        "date": "2023-10-25",
        "temperatureC": 25,
        "temperatureF": 76,
        "summary": "Cool"
    },
    {
        "date": "2023-10-26",
        "temperatureC": -20,
        "temperatureF": -3,
        "summary": "Hot"
    },
    {
        "date": "2023-10-27",
        "temperatureC": -3,
        "temperatureF": 27,
        "summary": "Warm"
    },
    {
        "date": "2023-10-28",
        "temperatureC": -17,
        "temperatureF": 2,
        "summary": "Balmy"
    },
    {
        "date": "2023-10-29",
        "temperatureC": 21,
        "temperatureF": 69,
        "summary": "Warm"
    }
]

dotnet web api project

The /WeatherForecast endpoint is defined in the WeatherForecastController.cs file. We can follow the same pattern to add our own endpoints.

Integrating Dynamsoft SDKs into the RESTful Service

Create a LicenseManager.cs file to manage the license key for the Dynamsoft Capture Vision SDK. A single license key now covers Barcode, MRZ, and Document scanning:

using Dynamsoft.License;

public class LicenseManager
{
    public static string licenseKey = "YOUR-LICENSE-KEY";

    public static void Init()
    {
        int errorCode = Dynamsoft.License.LicenseManager.InitLicense(licenseKey, out string errorMsg);
        if (errorCode != (int)Dynamsoft.Core.EnumErrorCode.EC_OK &&
            errorCode != (int)Dynamsoft.Core.EnumErrorCode.EC_LICENSE_WARNING)
        {
            Console.WriteLine($"License error: {errorCode}, {errorMsg}");
        }
    }
}

Then add LicenseManager.Init(); to the program.cs file.

In the Controllers folder, create five files: ProductController.cs, DwtController.cs, DbrController.cs, DlrController.cs, and DdnController.cs. These files will contain the endpoints for the respective SDKs.

Product List Endpoints

Add an HTTP GET endpoint to retrieve the list of available Dynamsoft products:

[Route("dynamsoft/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
    private static List<String> products = new List<String>
    {
        "Dynamic Web TWAIN",
        "Dynamsoft Barcode Reader",
        "Dynamsoft Label Recognizer",
        "Dynamsoft Document Normalizer",
    };

    [HttpGet]
    public ActionResult<IEnumerable<String>> Get()
    {
        return products;
    }

}

Dynamic Web TWAIN Endpoints

Import the packages:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using Twain.Wia.Sane.Scanner;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Initialize the controller. The ScannerController handles communication with the local Dynamic Web TWAIN service:

[Route("dynamsoft/[controller]")]
[ApiController]
public class DwtController : ControllerBase
{
    private static ScannerController scannerController = new ScannerController();
    private static string host = "http://127.0.0.1:18622";
}

Add an HTTP GET endpoint to retrieve the list of available scanners. In v2.x, GetDevices returns a JSON string:

[HttpGet]
[Route("GetDevices")]
public async Task<ActionResult> GetDevices()
{
    string scannerJson = await scannerController.GetDevices(host);
    var scanners = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(scannerJson);
    if (scanners == null || scanners.Count == 0)
    {
        return Ok("No devices found");
    }
    return Ok(scanners);
}

Add an HTTP POST endpoint to initiate a scanning job. We use a ToNative helper to ensure JSON parameters from the client are correctly converted to .NET types before being passed to CreateJob:

[HttpPost]
[Route("ScanDocument")]
public async Task<IActionResult> ScanDocument()
{
    if (Request.ContentType == null) return BadRequest("No content type specified");

    if (Request.ContentType.Contains("application/json"))
    {
        using (StreamReader reader = new StreamReader(Request.Body))
        {
            var json = await reader.ReadToEndAsync();
            var parameters = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
            if (parameters == null) return BadRequest("No content found");

            // Convert JObjects/JArrays to native Dictionary/List for SDK compatibility
            parameters = (Dictionary<string, object>)ToNative(JObject.FromObject(parameters));

            parameters["license"] = LicenseManager.licenseKey;
            parameters["autoRun"] = true;

            string jobJson = await scannerController.CreateJob(host, parameters);
            var jobObj = JsonConvert.DeserializeObject<Dictionary<string, object>>(jobJson);
            
            if (jobObj == null || !jobObj.ContainsKey("jobuid"))
            {
                return BadRequest("Failed to create scan job: " + jobJson);
            }

            return Ok(jobObj["jobuid"].ToString());
        }
    }
    else
    {
        return BadRequest("Unsupported content type");
    }
}

private object ToNative(JToken token)
{
    if (token is JObject obj)
    {
        var dict = new Dictionary<string, object>();
        foreach (var prop in obj) { dict[prop.Key] = ToNative(prop.Value); }
        return dict;
    }
    else if (token is JArray arr)
    {
        var list = new List<object>();
        foreach (var item in arr) { list.Add(ToNative(item)); }
        return list;
    }
    else if (token is JValue val) { return val.Value; }
    return token.ToString();
}

Add an HTTP GET endpoint to fetch the image stream for a specific job:

[HttpGet]
[Route("GetImageStream/{jobId}")]
public async Task<ActionResult> GetImageStream(string jobId)
{
    byte[] bytes = await scannerController.GetImageStream(host, jobId);
    if (bytes == null || bytes.Length == 0)
    {
        await scannerController.DeleteJob(host, jobId);
        return Ok("No image found");
    }
    else
    {
        return File(bytes, "application/octet-stream");
    }
}

Dynamsoft Barcode Reader Endpoints

Import the packages:

using Dynamsoft.CVR;
using Dynamsoft.DBR;
using Dynamsoft.Core;
using Microsoft.AspNetCore.Mvc;
using OpenCvSharp;

Initialize the controller with a CaptureVisionRouter instance:

[Route("dynamsoft/[controller]")]
[ApiController]
public class DbrController : ControllerBase
{
    private CaptureVisionRouter cvRouter;

    public DbrController()
    {
        cvRouter = new CaptureVisionRouter();
    }
}

Add an HTTP POST endpoint to decode barcodes from an uploaded image and return the reading results. The image bytes are passed directly to cvRouter.Capture() with the PT_READ_BARCODES preset template:

[HttpPost]
[Route("DecodeBarcode")]
public async Task<IActionResult> DecodeBarcode()
{
    if (Request.ContentType == null) return BadRequest("No content type specified");

    if (Request.ContentType.Contains("multipart/form-data"))
    {
        var form = await Request.ReadFormAsync();
        var file = form.Files[0];
        if (file.Length == 0)
        {
            return BadRequest("Empty file received");
        }

        using (var memoryStream = new MemoryStream())
        {
            await file.CopyToAsync(memoryStream);
            byte[] imageBytes = memoryStream.ToArray();

            CapturedResult capturedResult = cvRouter.Capture(imageBytes, PresetTemplate.PT_READ_BARCODES);

            DecodedBarcodesResult? barcodesResult = capturedResult.GetDecodedBarcodesResult();
            BarcodeResultItem[]? items = barcodesResult?.GetItems();

            if (items == null || items.Length == 0)
            {
                return Ok("No barcode found");
            }

            var results = items.Select(item => new
            {
                text = item.GetText(),
                format = item.GetFormatString()
            }).ToArray();

            return Ok(results);
        }
    }
    else
    {
        return BadRequest("Unsupported content type");
    }
}

Dynamsoft Label Recognizer Endpoints

Import the packages:

using Dynamsoft.CVR;
using Dynamsoft.DLR;
using Dynamsoft.DCP;
using Dynamsoft.Core;
using Microsoft.AspNetCore.Mvc;

Initialize the controller with a CaptureVisionRouter instance:

[Route("dynamsoft/[controller]")]
[ApiController]
public class DlrController : ControllerBase
{
    private CaptureVisionRouter cvRouter;

    public DlrController()
    {
        cvRouter = new CaptureVisionRouter();
    }
}

Add an HTTP POST endpoint to recognize MRZ (Machine Readable Zone) from an uploaded image and return the parsed MRZ fields. The ReadPassportAndId template is used to detect and parse passport/ID documents:

[HttpPost]
[Route("DetectMrz")]
public async Task<IActionResult> DetectMrz()
{
    if (Request.ContentType == null) return BadRequest("No content type specified");

    if (Request.ContentType.Contains("multipart/form-data"))
    {
        var form = await Request.ReadFormAsync();
        var file = form.Files[0];
        if (file.Length == 0)
        {
            return BadRequest("Empty file received");
        }

        using (var memoryStream = new MemoryStream())
        {
            await file.CopyToAsync(memoryStream);
            byte[] imageBytes = memoryStream.ToArray();

            CapturedResult capturedResult = cvRouter.Capture(imageBytes, "ReadPassportAndId");

            ParsedResult? parsedResult = capturedResult.GetParsedResult();
            ParsedResultItem[]? items = parsedResult?.GetItems();

            if (items == null || items.Length == 0)
            {
                return Ok("No MRZ found");
            }

            var mrzResults = items.Select(item =>
            {
                string docType = item.GetCodeType();
                string? docId = docType == "MRTD_TD3_PASSPORT" ?
                    item.GetFieldValue("passportNumber") : item.GetFieldValue("documentNumber");

                return new
                {
                    documentType = docType,
                    documentId = docId,
                    surname = item.GetFieldValue("primaryIdentifier"),
                    givenName = item.GetFieldValue("secondaryIdentifier"),
                    nationality = item.GetFieldValue("nationality"),
                    issuingCountry = item.GetFieldValue("issuingState"),
                    dateOfBirth = item.GetFieldValue("dateOfBirth"),
                    dateOfExpiry = item.GetFieldValue("dateOfExpiry"),
                    sex = item.GetFieldValue("sex"),
                    line1 = item.GetFieldValue("line1"),
                    line2 = item.GetFieldValue("line2"),
                    line3 = item.GetFieldValue("line3")
                };
            }).ToArray();

            return Ok(mrzResults);
        }
    }
    else
    {
        return BadRequest("Unsupported content type");
    }
}

Dynamsoft Document Normalizer Endpoints

Import the packages:

using Dynamsoft.CVR;
using Dynamsoft.DDN;
using Dynamsoft.Core;
using Microsoft.AspNetCore.Mvc;
using OpenCvSharp;

Initialize the controller with a CaptureVisionRouter instance:

[Route("dynamsoft/[controller]")]
[ApiController]
public class DdnController : ControllerBase
{
    private CaptureVisionRouter cvRouter;

    public DdnController()
    {
        cvRouter = new CaptureVisionRouter();
    }
}

Add an HTTP POST endpoint to detect and normalize a document from an uploaded image. The PT_DETECT_AND_NORMALIZE_DOCUMENT preset template is used, and the deskewed image is returned as a JPEG byte stream via GetProcessedDocumentResult():

[HttpPost]
[Route("rectifyDocument")]
public async Task<IActionResult> rectifyDocument()
{
    if (Request.ContentType == null) return BadRequest("No content type specified");

    if (Request.ContentType.Contains("multipart/form-data"))
    {
        var form = await Request.ReadFormAsync();
        var file = form.Files[0];
        if (file.Length == 0)
        {
            return BadRequest("Empty file received");
        }

        using (var memoryStream = new MemoryStream())
        {
            await file.CopyToAsync(memoryStream);
            byte[] imageBytes = memoryStream.ToArray();

            CapturedResult capturedResult = cvRouter.Capture(imageBytes, PresetTemplate.PT_DETECT_AND_NORMALIZE_DOCUMENT);

            ProcessedDocumentResult? docResult = capturedResult.GetProcessedDocumentResult();
            EnhancedImageResultItem[]? enhancedItems = docResult?.GetEnhancedImageResultItems();

            if (enhancedItems != null && enhancedItems.Length > 0)
            {
                ImageData? imageData = enhancedItems[0].GetImageData();
                if (imageData != null)
                {
                    byte[] rawBytes = imageData.GetBytes();
                    int width = imageData.GetWidth();
                    int height = imageData.GetHeight();
                    EnumImagePixelFormat format = imageData.GetImagePixelFormat();

                    Mat newMat;
                    if (format == EnumImagePixelFormat.IPF_GRAYSCALED)
                    {
                        newMat = new Mat(height, width, MatType.CV_8UC1, rawBytes);
                    }
                    else
                    {
                        newMat = new Mat(height, width, MatType.CV_8UC3, rawBytes);
                    }

                    Cv2.ImEncode(".jpg", newMat, out byte[] buffer);
                    return File(buffer, "application/octet-stream");
                }
            }
        }

        return Ok("No document found");
    }
    else
    {
        return BadRequest("Unsupported content type");
    }
}

REST API Table

Method Endpoint Parameters Response Description
GET /dynamsoft/product - Product list Retrieve all Dynamsoft products.
GET /dynamsoft/dwt/GetDevices - Scanner list List available scanners.
POST /dynamsoft/dwt/ScanDocument Scanner config Job ID Initiate a scanning job.
GET /dynamsoft/dwt/GetImageStream/{jobId} jobId Image stream Fetch image stream for a specific job.
POST /dynamsoft/dbr/DecodeBarcode Image file Barcode results Decode barcodes using the Dynamsoft Barcode Reader.
POST /dynamsoft/dlr/DetectMrz Image file MRZ results Detect MRZ from an image with Dynamsoft Label Recognizer.
POST /dynamsoft/ddn/rectifyDocument Image file Rectified document image Rectify a document with Dynamsoft Document Normalizer.

Creating an HTML5 Web App for Testing the RESTful Service

Create an input element for entering the RESTful service URL. As the connection is established, the available products will be listed in the dropdown menu.

HTML

<div class="row">
    <div>
        <label>Enter host address: </label>
        <input type="text" id="host" placeholder="http://127.0.0.1:5000/">
        <button onclick="connect()">connect</button>
    </div>
</div>

JavaScript

let host = placeholder = 'http://127.0.0.1:5000/';
let dropdown = document.getElementById("dropdown");
function selectChanged() {
    switchProduct(dropdown.value)
}

async function connect() {
    dropdown.innerHTML = "";
    host = document.getElementById("host").value == "" ? placeholder : document.getElementById("host").value;

    try {
        const response = await fetch(host + 'dynamsoft/product', {
            method: 'GET',
        });

        if (response.status == 200) {
            const products = await response.json();

            products.forEach(element => {
                let optionElement = document.createElement("option");
                optionElement.value = element;
                optionElement.text = element;
                dropdown.appendChild(optionElement);
            });
            switchProduct(dropdown.value)
        }
    } catch (error) {
        console.log(error);
    }
}

Create a container for testing Dynamic Web TWAIN endpoints

HTML

<div class="container" id="dwt">
    <div class="row">
        <div>
            <button onclick="getDevices()">Get Devices</button>
            <select id="sources">
            </select>
            <button onclick="acquireImage()">Scan Documents</button>
        </div>

    </div>

    <div class="row">
        <div class="full-img">
            <img id="scanner-image">
        </div>
    </div>

    <div class="row">
        <div class="thumb-bar" id="thumb-bar">
            <div class="thumb-box" id="thumb-box">
            </div>
        </div>
    </div>
</div>

JavaScript

let selectSources = document.getElementById("sources");
let devices = [];

async function getDevices() {
    document.getElementById("loading-indicator").style.display = "flex";
    selectSources.innerHTML = "";
    let url = host + 'dynamsoft/dwt/getdevices';
    const response = await fetch(url, { "method": "GET" });

    if (response.status == 200) {

        try {
            let json = await response.json();
            if (json) {
                devices = json;
                json.forEach(element => {
                    let option = document.createElement("option");
                    option.text = element['name'];
                    option.value = element['name'];
                    selectSources.add(option);
                });
            }
        } catch (error) {
            console.log(error)
        }

    }
    document.getElementById("loading-indicator").style.display = "none";
}

async function acquireImage() {
    let url = host + 'dynamsoft/dwt/ScanDocument';
    if (devices.length > 0 && selectSources.selectedIndex >= 0) {
        let parameters = {
            device: devices[selectSources.selectedIndex]['device'],
            config: {
                IfShowUI: false,
                PixelType: 2,
                //XferCount: 1,
                //PageSize: 1,
                Resolution: 200,
                IfFeederEnabled: false,
                IfDuplexEnabled: false,
            }
        };

        let response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(parameters)
        });

        const contentType = response.headers.get('Content-Type');
        const result = await response.text();

        url = host + 'dynamsoft/dwt/GetImageStream/' + result;

        response = await fetch(url, { method: 'GET' });

        let imageBlob = await response.blob();
        let img = document.getElementById('scanner-image');
        url = URL.createObjectURL(imageBlob);
        img.src = url;

        let option = document.createElement("option");
        option.selected = true;
        option.text = url;
        option.value = url;

        let thumbnails = document.getElementById("thumb-box");
        let newImage = document.createElement('img');
        newImage.setAttribute('src', url);
        if (thumbnails != null) {
            thumbnails.appendChild(newImage);
            newImage.addEventListener('click', e => {
                if (e != null && e.target != null) {
                    let target = e.target;
                    img.src = target.src;
                }
            });
        }
    }

}

Create a container for testing Dynamsoft Barcode Reader endpoints

HTML

<div class="container" id="dbr">
    <div class="row">
        <div>
            <input type="file" id="barcode-file" accept="image/*" />
            <button onclick="decodeBarcode()">Read Barcode</button>
        </div>

    </div>

    <div class="row">
        <div id="imageview">
            <img id="barcode-image" src="placeholder.png" />
        </div>
    </div>

    <div class="row">
        <div>
            <textarea id="barcode-result"></textarea>
        </div>
    </div>

</div>

JavaScript

document.getElementById("barcode-file").addEventListener("change", function () {
    document.getElementById('barcode-result').innerHTML = '';
    let currentFile = this.files[0];
    if (currentFile == null) {
        return;
    }
    var fr = new FileReader();
    fr.onload = function () {
        let image = document.getElementById('barcode-image');
        image.src = fr.result;
    }
    fr.readAsDataURL(currentFile);
});

async function decodeBarcode() {
    let url = host + 'dynamsoft/dbr/DecodeBarcode';

    const input = document.getElementById('barcode-file');
    const file = input.files[0];

    if (!file) {
        return;
    }

    const formData = new FormData();
    formData.append('image', file);

    let response = await fetch(url, {
        method: 'POST',
        body: formData
    });

    if (response.headers.get('Content-Type').includes('application/json')) {
        let data = await response.json();
        let content = 'total barcode(s) found: ' + data.length;
        let index = 0;
        data.forEach(element => {
            content += '\n';
            content += index + ". text: " + element['text'] + ", format: " + element['format'];
            index += 1;
        });
        document.getElementById('barcode-result').innerHTML = content;
    }
    else if (response.headers.get('Content-Type').includes('text/plain')) {
        let data = await response.text();
        document.getElementById('barcode-result').innerHTML = data;
    }
}

Create a container for testing Dynamsoft Label Recognizer endpoints

HTML

<div class="container" id="dlr">
    <div class="row">
        <div>
            <input type="file" id="mrz-file" accept="image/*" />
            <button onclick="detectMrz()">Detect MRZ</button>
        </div>

    </div>

    <div class="row">
        <div id="imageview">
            <img id="mrz-image" src="placeholder.png" />
        </div>
    </div>

    <div class="row">
        <div>
            <textarea id="mrz-result"></textarea>
        </div>
    </div>

</div>

JavaScript

document.getElementById("mrz-file").addEventListener("change", function () {
    document.getElementById('barcode-result').innerHTML = '';
    let currentFile = this.files[0];
    if (currentFile == null) {
        return;
    }
    var fr = new FileReader();
    fr.onload = function () {
        let image = document.getElementById('mrz-image');
        image.src = fr.result;
    }
    fr.readAsDataURL(currentFile);
});

async function detectMrz() {
    let url = host + 'dynamsoft/dlr/DetectMrz';

    const input = document.getElementById('mrz-file');
    const file = input.files[0];

    if (!file) {
        return;
    }

    const formData = new FormData();
    formData.append('image', file);

    let response = await fetch(url, {
        method: 'POST',
        body: formData
    });

    if (response.headers.get('Content-Type').includes('application/json')) {
        let data = await response.json();
        document.getElementById('mrz-result').innerHTML = JSON.stringify(data);
    }
    else if (response.headers.get('Content-Type').includes('text/plain')) {
        let data = await response.text();
        document.getElementById('mrz-result').innerHTML = data;
    }
}

Create a container for testing Dynamsoft Document Normalizer endpoints

HTML

<div class="container" id="ddn">
    <div class="row">
        <div>
            <input type="file" id="document-file" accept="image/*" />
            <button onclick="rectifyDocument()">Rectify Document</button>
        </div>

    </div>

    <div class="row">
        <div id="imageview">
            <img id="document-image" src="placeholder.png" />
        </div>
    </div>

    <div class="row">
        <div>
            <textarea id="document-result"></textarea>
            <img id="document-rectified-image" />
        </div>
    </div>

</div>

JavaScript

document.getElementById("document-file").addEventListener("change", function () {
    let currentFile = this.files[0];
    if (currentFile == null) {
        return;
    }
    var fr = new FileReader();
    fr.onload = function () {
        let image = document.getElementById('document-image');
        image.src = fr.result;
    }
    fr.readAsDataURL(currentFile);
});

async function rectifyDocument() {
    let url = host + 'dynamsoft/ddn/rectifyDocument';

    const input = document.getElementById('document-file');
    const file = input.files[0];

    if (!file) {
        return;
    }

    const formData = new FormData();
    formData.append('image', file);

    let response = await fetch(url, {
        method: 'POST',
        body: formData
    });

    if (response.headers.get('Content-Type').includes('text/plain')) {
        let data = await response.text();
        document.getElementById('document-result').innerHTML = data;

        let divElement = document.getElementById("document-result");
        divElement.style.display = "block";

        divElement = document.getElementById("document-rectified-image");
        divElement.style.display = "none";
    }
    else if (response.headers.get('Content-Type').includes('application/octet-stream')) {
        let data = await response.blob();
        let img = document.getElementById('document-rectified-image');
        let url = URL.createObjectURL(data);
        img.src = url;

        let divElement = document.getElementById("document-rectified-image");
        divElement.style.display = "block";

        divElement = document.getElementById("document-result");
        divElement.style.display = "none";
    }
}

Source Code

https://github.com/yushulx/dynamsoft-sdk-webapi-restful-service/tree/main/dotnet