# ASP.NET Core

> Integrate the Document Editor, Document Viewer and ServerTextControl into ASP.NET Core: packages, Program.cs for MVC, Razor Pages and SPAs, secure endpoints.

- **Product:** TX Text Control .NET Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET Server > Integration Guide > ASP.NET Core
- **6279 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/aspnet-core/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/aspnet-core/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/aspnet-core/llms-full.txt

---

A real ASP.NET Core application rarely looks like a tutorial project. It has its own controllers, services and authentication, and it often needs the Document Editor, the Document Viewer and server-side document processing at the same time. All three parts of TX Text Control .NET Server fit into one application, and the same application can serve as the backend for Angular, React and JavaScript frontends.

The first steps with a new project are in [Document Editor: ASP.NET Core](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/document-editor/aspnet-core/llms-full.txt) and [Document Viewer: ASP.NET Core](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/document-viewer/aspnet-core/llms-full.txt). Read on for the packages each part needs, *Program.cs* for MVC views, Razor Pages and a backend of separate frontends, controllers that exchange documents, the use of `ServerTextControl` in request handlers and the protection of the endpoints.

### Choose the Packages

Install the packages of the parts your application uses from the [Text Control Private NuGet Feed](https://docs.textcontrol.com/tx-text-control-dotnet-server/installation/nuget/private-feed/llms-full.txt):

| Part | Packages | What they add |
|---|---|---|
| Document Editor | `TXTextControl.Web`, `TXTextControl.Web.DocumentEditor.Backend` | The HTML helper, the WebSocket middleware and the backend process with its hosted service |
| Document Viewer | `TXTextControl.Web.DocumentViewer` | The HTML helper and the `TextControl` controller that serves the pages |
| Document processing | none besides the engine | `ServerTextControl` and `MailMerge` |
| The engine, for every part | `TXTextControl.TextControl.Core.SDK` | The document engine |

The web packages contain no engine, so every application also installs an engine package. This guide uses the Core engine. The Classic engine, `TXTextControl.TextControl.ASP.SDK`, runs on Windows only and uses `TXTextControl.Web.DocumentViewer.Classic` for the viewer; the backend package of the Document Editor requires Core. [Package Overview](https://docs.textcontrol.com/tx-text-control-dotnet-server/installation/nuget/packages/llms-full.txt) describes every package.

For an application with all parts, run in the project folder:

```bash
dotnet add package TXTextControl.TextControl.Core.SDK
dotnet add package TXTextControl.Web
dotnet add package TXTextControl.Web.DocumentEditor.Backend
dotnet add package TXTextControl.Web.DocumentViewer
```

### Configure Program.cs for Views

In an MVC application, the editor and the viewer are added to views. *Program.cs* needs two additions for the Document Editor and none for the Document Viewer: the complete file is in [Configure Program.cs](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/document-editor/aspnet-core/#configure-program-cs) of the Getting Started tutorial, and it serves both components unchanged. What matters when the application grows:

- `AddHostedService<DocumentEditorWorkerManager>()` starts the backend process `TXTextControl.Web.Server.Core` when the application starts, forwards its output to the logging of your application and stops it when the application shuts down.
- `UseWebSockets` and `UseTXWebSocketMiddleware` go after `UseRouting` and before the endpoint mappings. A middleware that checks access tokens for the editor goes between the two calls; [WebSocket Architecture](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/websockets/#add-the-middleware-to-the-pipeline) explains the order and the overloads of `UseTXWebSocketMiddleware`. `UseAuthorization` after the WebSocket middleware protects your pages, but not the WebSocket connection; [Require Signed-In Users](https://docs.textcontrol.com/tx-text-control-dotnet-server/security/authentication/#require-signed-in-users) shows the order that protects both.
- The Document Viewer needs no line: the controller route of the template also maps the `TextControl` controller of the viewer package. Don't name a controller of your own `TextControl`.

When the backend process needs another address or port, bind `DocumentEditorWorkerOptions` to a section of *appsettings.json* before `AddHostedService` and pass the same values to `UseTXWebSocketMiddleware`, as [Configure the Backend Process](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-editor/architecture/#configure-the-backend-process) describes.

### Use Razor Pages

In a Razor Pages application, the HTML helpers work in *.cshtml* pages the same way as in MVC views. The Document Viewer needs its controller, which a Razor Pages application doesn't map by itself, so *Program.cs* registers the controllers and calls `UseTXDocumentViewer`:

```csharp
using TXTextControl.Web;
using TXTextControl.Web.DocumentEditor.Backend;
using TXTextControl.Web.MVC.DocumentViewer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllers();
builder.Services.AddHostedService<DocumentEditorWorkerManager>();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();

app.UseWebSockets();
app.UseTXWebSocketMiddleware();

// Maps the controller of the viewer and runs the endpoints
app.UseTXDocumentViewer();

app.MapStaticAssets();
app.MapRazorPages()
   .WithStaticAssets();

app.Run();
```

The code works as follows:

- `AddControllers` registers the `TextControl` controller of the viewer package, and `UseTXDocumentViewer` maps its route. Without both, the viewer page renders, but the requests of the viewer for its scripts return 404. `MapControllers` alone doesn't map the controller of the viewer.
- `UseTXDocumentViewer` runs the endpoints, including the Razor Pages, so `UseAuthorization` comes before it.
- The editor needs the same two additions as in an MVC application: the hosted service and the WebSocket middleware.

A page shows the editor with `@using TXTextControl.Web.MVC` and `@Html.TXTextControl().TextControl()`, and the viewer with `@using TXTextControl.Web.MVC.DocumentViewer` and `@Html.TXTextControl().DocumentViewer()`, as in the views of the Getting Started tutorials.

### Configure Program.cs for Separate Frontends

Angular, React and JavaScript frontends run as separate applications and call an ASP.NET Core backend without views, usually created from the **ASP.NET Core Web API** template. One backend serves the Document Editor, the Document Viewer and your own controllers:

```csharp
using TXTextControl.Web;
using TXTextControl.Web.DocumentEditor.Backend;
using TXTextControl.Web.MVC.DocumentViewer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddHostedService<DocumentEditorWorkerManager>();

// Allow the frontend's origin to call the viewer and your controllers
builder.Services.AddCors(options => options.AddPolicy("frontend", policy =>
{
    policy.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader();
}));

var app = builder.Build();

app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("frontend");
app.UseAuthorization();

// Document Editor
app.UseWebSockets();
app.UseTXWebSocketMiddleware();

// Document Viewer: runs the endpoints, so it comes after CORS and authorization
app.UseTXDocumentViewer();
app.MapControllers();

app.Run();
```

The code works as follows:

- `AddControllers` registers the controllers, including the `TextControl` controller of the viewer package.
- `AddCors` and `UseCors` allow requests from the origin of the frontend, here the Angular development server. The Document Viewer needs the policy because the viewer in the browser calls the backend with HTTP requests from another origin. The Document Editor needs no CORS policy. Replace the origin with the address of your frontend, for example `http://localhost:5173` for a Vite development server.
- `UseTXDocumentViewer` adds the Document Viewer to a backend without MVC views. The README of the viewer package places it after `app.UseRouting()`. Middleware that has to apply to the requests of the viewer and of your controllers, such as `UseCors`, `UseAuthentication`, `UseAuthorization` and a middleware that checks access tokens, goes before it.

The frontends connect to this backend with the WebSocket URL `wss://<backend>/api/TXWebSocket` for the editor and the base path `https://<backend>` for the viewer. [Angular](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/angular/llms-full.txt), [React](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/react/llms-full.txt) and [JavaScript](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/javascript/llms-full.txt) show the client side.

### Add a Document Controller

Frontends load documents from the backend and send edited documents back. Use the `DocumentController` of [Store Documents on the Server](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-editor/load-save/#store-documents-on-the-server): it stores DOCX documents in the *Documents* folder of the project and exchanges them as JSON objects with a name and base64-encoded data, the form in which `saveDocument` returns binary formats and `loadDocument` accepts them.

A backend created from the **ASP.NET Core Web API** template maps controllers only with `MapControllers` and has no conventional route, so the controller needs attribute routes. Change the class and the two action attributes:

```csharp
[ApiController]
[Route("[controller]")]
public class DocumentController : ControllerBase
{
    [HttpGet("Load")]
    public DocumentData Load(string name) { /* as in the Document Editor section */ }

    [HttpPost("Save")]
    public bool Save([FromBody] DocumentData documentData) { /* unchanged */ }
}
```

The actions then answer `GET /Document/Load?name=...` and `POST /Document/Save`, the URLs the [Angular](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/angular/llms-full.txt), [React](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/react/llms-full.txt) and [JavaScript](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/javascript/llms-full.txt) examples call.

### Show the Editor and the Viewer in One View

A view can combine both components, for example to show a merged preview of the template a user edits. Both packages define an `Html.TXTextControl()` helper, so the view creates the editor with the helper and the viewer in code:

```cshtml
@using TXTextControl.Web.MVC

<input type="button" onclick="showPreview()" value="Preview" />

<div style="display: flex; gap: 1rem;">
    <div style="width: 50%; height: 600px;">
        @Html.TXTextControl().TextControl(settings => {
            settings.Dock = TXTextControl.Web.DockStyle.Fill;
        }).LoadText("Documents/template.docx", TXTextControl.Web.StreamType.WordprocessingML).Render()
    </div>

    <div style="width: 50%; height: 600px;">
        @{
            var viewerSettings = new TXTextControl.Web.MVC.DocumentViewer.DocumentViewerSettings();
            viewerSettings.Dock = TXTextControl.Web.MVC.DocumentViewer.DocumentViewerSettings.DockStyle.Fill;
            viewerSettings.ShowThumbnailPane = false;

            var viewer = new TXTextControl.Web.MVC.DocumentViewer.DocumentViewer(viewerSettings);
            @viewer.Render()
        }
    </div>
</div>

<script>
    function showPreview() {
        TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, function (result) {
            fetch("/MailMerge/Merge", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(result.data)
            })
                .then(response => response.text())
                .then(merged => TXDocumentViewer.loadDocument(merged, "preview.tx"));
        });
    }
</script>
```

The controller merges the template with the data in *Documents/data.json* and returns the result:

```csharp
using Microsoft.AspNetCore.Mvc;
using TXTextControl;
using TXTextControl.DocumentServer;

public class MailMergeController : Controller
{
    [HttpPost]
    public IActionResult Merge([FromBody] string template)
    {
        using (ServerTextControl tx = new ServerTextControl())
        {
            tx.Create();
            tx.Load(Convert.FromBase64String(template), BinaryStreamType.InternalUnicodeFormat);

            using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
            {
                mailMerge.MergeJsonData(System.IO.File.ReadAllText("Documents/data.json"));
            }

            tx.Save(out byte[] data, BinaryStreamType.InternalUnicodeFormat);
            return Content(Convert.ToBase64String(data));
        }
    }
}
```

The code works as follows:

- [TXTextControl.saveDocument](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/txtextcontrol/savedocument-method/llms-full.txt) saves the template in the internal format of TX Text Control and passes the base64-encoded [data](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript/savedocumentresult/data-property/llms-full.txt) to the controller.
- The controller loads the template into a [ServerTextControl](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/llms-full.txt), merges it with [MailMerge.MergeJsonData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/mergejsondata-method/llms-full.txt) and returns the merged document as base64 text.
- [TXDocumentViewer.loadDocument](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/javascript-document-viewer/documentviewercontroller/loaddocument-method/llms-full.txt) shows the result under the name that its second parameter passes.
- [DocumentViewer](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-web-mvc-documentviewer/documentviewer/llms-full.txt) takes [DocumentViewerSettings](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-web-mvc-documentviewer/documentviewersettings/llms-full.txt), and [Render](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-web-mvc-documentviewer/controlbase/render-method/llms-full.txt) writes the viewer into the page.

The blog article [ASP.NET Core: Use the Document Editor and Viewer in the Same Razor View](https://www.textcontrol.com/blog/2024/11/08/asp-net-core-use-the-document-editor-and-viewer-in-the-same-razor-view/) updates the preview automatically while the user types.

### Use ServerTextControl in Request Handlers

Controllers and services create documents with `ServerTextControl` and `MailMerge` in the process of your application. Keep the following in mind:

- **One instance per operation.** Create a `ServerTextControl` for each request or task in a `using` block, as [Create One Instance per Operation](https://docs.textcontrol.com/tx-text-control-dotnet-server/performance/threading/#create-one-instance-per-operation) describes.
- **A service for document logic.** Put the document code into a service class that a controller receives through dependency injection. The service stays testable and reusable in other controllers.
- **Class libraries.** When the document code lives in a class library that other assemblies call, set [ServerTextControl.EntryAssembly](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/entryassembly-property/llms-full.txt), as [License File Changes](https://docs.textcontrol.com/tx-text-control-dotnet-server/installation/upgrading/previous-versions/#license-file-changes) describes.

The following service converts DOCX documents to PDF:

```csharp
using TXTextControl;

public interface IPdfService
{
    byte[] ConvertToPdf(byte[] docx);
}

public class PdfService : IPdfService
{
    public byte[] ConvertToPdf(byte[] docx)
    {
        using (ServerTextControl tx = new ServerTextControl())
        {
            tx.Create();
            tx.Load(docx, BinaryStreamType.WordprocessingML);
            tx.Save(out byte[] pdf, BinaryStreamType.AdobePDF);

            return pdf;
        }
    }
}
```

Register it in *Program.cs* before `builder.Build()` with `builder.Services.AddScoped<IPdfService, PdfService>();`. A controller receives it in its constructor:

```csharp
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class PdfController : ControllerBase
{
    private readonly IPdfService _pdfService;

    public PdfController(IPdfService pdfService)
    {
        _pdfService = pdfService;
    }

    [HttpGet("Template")]
    public IActionResult Template()
    {
        byte[] pdf = _pdfService.ConvertToPdf(System.IO.File.ReadAllBytes("Documents/template.docx"));
        return File(pdf, "application/pdf", "template.pdf");
    }
}
```

[REST Services](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/rest-services/llms-full.txt) goes further with endpoints that other applications call.

#### Separate Editing and Processing Under Load

With many concurrent users, heavy document processing in the application that hosts the WebSocket connections of the editors can block these connections. [Separate Processing From Editing](https://docs.textcontrol.com/tx-text-control-dotnet-server/performance/best-practices/#separate-processing-from-editing) and [Scaling Beyond One Instance](https://docs.textcontrol.com/tx-text-control-dotnet-server/overview/architecture/#scaling-beyond-one-instance) describe the recommended split.

### Protect the Endpoints

The endpoints of the editor and the viewer are part of your application and need the same protection as the rest of it:

- **Document Editor.** A middleware between `UseWebSockets` and `UseTXWebSocketMiddleware` checks a token before the WebSocket connection is established. See [WebSocket Security](https://docs.textcontrol.com/tx-text-control-dotnet-server/security/websockets/llms-full.txt).
- **Document Viewer.** A middleware before `UseTXDocumentViewer` checks the token that the view passes in the base path. See [Protect the Endpoints](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-viewer/architecture/#protect-the-endpoints) in the Document Viewer section.
- **Your controllers.** Document endpoints read and write files and create documents; [Authentication](https://docs.textcontrol.com/tx-text-control-dotnet-server/security/authentication/llms-full.txt) covers how to protect them.

When users sign in to your application, for example with cookie authentication, a fallback policy protects the editor, the viewer and your controllers without tokens, provided `UseAuthentication` and `UseAuthorization` come before the TX Text Control middleware. [Require Signed-In Users](https://docs.textcontrol.com/tx-text-control-dotnet-server/security/authentication/#require-signed-in-users) shows the code and the order.

### Applications on the .NET Framework

ASP.NET MVC and Web Forms applications on the .NET Framework use the Classic engine and a different setup of the Document Editor. [ASP.NET on .NET Framework](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/dotnet-framework/llms-full.txt) has the tutorials for these applications. To move such an application to ASP.NET Core and the NuGet packages, follow [From ASP.NET MVC on the .NET Framework to ASP.NET Core](https://docs.textcontrol.com/tx-text-control-dotnet-server/installation/upgrading/previous-versions/#from-asp-net-mvc-on-the-net-framework-to-asp-net-core).

### Next Steps

- [WebSocket Architecture](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/websockets/llms-full.txt): the connection of the Document Editor in detail.
- [REST Services](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/rest-services/llms-full.txt): build document endpoints that other applications call.
- [Angular](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/angular/llms-full.txt): connect an Angular application to the backend.
- [Deployment Guide](https://docs.textcontrol.com/tx-text-control-dotnet-server/deployment/llms-full.txt): publish the application to Windows, Linux, Docker and Azure.

---

## Nearby

- **Previous:** [Integration Guide](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/llms-full.txt)
- **Next:** [Angular](https://docs.textcontrol.com/tx-text-control-dotnet-server/integration/angular/llms-full.txt)
