Elsa Server + Studio
In this topic, we will create an ASP.NET Core application that acts as both an Elsa Server and an Elsa Studio.
For Elsa Studio, we will setup the Blazor parts using Blazor WebAssembly, which static files will be served from the ASP.NET Core host application.
Create Solution
In this chapter, we will scaffold a new solution and two projects:
The Host
The Client
The host will host both the ASP.NET COre Elsa Server engine and the Blazor WebAssembly application.
The client represents the Elsa Studio application and targets the Blazor WebAssembly runtime and is hosted by the Host application.
Run the following commands to create a solution with two projects:
Setup Host
In this chapter, we will setup the host, which will host both the Elsa Server engine as well as the webassembly files for serving the Elsa Studio client assets to the browser.
Add Packages
Add the following packages:
dotnet add package Elsa dotnet add package Elsa.EntityFrameworkCore dotnet add package Elsa.EntityFrameworkCore.Sqlite dotnet add package Elsa.Identity dotnet add package Elsa.Scheduling dotnet add package Elsa.Workflows.Api dotnet add package Elsa.CSharp dotnet add package Elsa.JavaScript dotnet add package Elsa.Liquid dotnet add package Microsoft.AspNetCore.Components.WebAssembly.ServerUpdate Program.cs
Open the Program.cs file in your project and replace its contents with the code provided below. This code does a lot of things like setting up database connections, enabling user authentication, and preparing the server to handle workflows.
Program.cs
using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; using Elsa.Extensions; using Microsoft.AspNetCore.Mvc; var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseStaticWebAssets(); var services = builder.Services; var configuration = builder.Configuration; services .AddElsa(elsa => elsa .UseIdentity(identity => { identity.TokenOptions = options => options.SigningKey = "large-signing-key-for-signing-JWT-tokens"; identity.UseAdminUserProvider(); }) .UseDefaultAuthentication() .UseWorkflowManagement(management => management.UseEntityFrameworkCore()) .UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore()) .UseScheduling() .UseJavaScript() .UseLiquid() .UseCSharp() .UseHttp(http => http.ConfigureHttpOptions = options => configuration.GetSection("Http").Bind(options)) .UseWorkflowsApi() .UseRealTimeWorkflows() .AddActivitiesFrom<Program>() .AddWorkflowsFrom<Program>() ); services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("*"))); services.AddRazorPages(options => options.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute())); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseBlazorFrameworkFiles(); app.UseRouting(); app.UseCors(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.UseWorkflowsApi(); app.UseWorkflows(); app.UseWorkflowsSignalRHubs(); app.MapFallbackToPage("/_Host"); app.Run();Update appsettings.json
Add the following configuration section to
appsettings.json
orappsettings.Development.json
with the following content:{ "Http": { "BaseUrl": "https://localhost:5001", "BasePath": "/api/workflows" } }Create _Host.cshtml
To conclude the setup, create new folder called
Pages
and add a new file called_Host.cshtml
and copy in the code showcased below:Pages/_Host.cshtml
@page "/" @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ var baseUrl = $"{Request.Scheme}://{Request.Host}"; var apiUrl = baseUrl + Url.Content("~/elsa/api"); var basePath = ""; } <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> <title>Elsa Studio 3.0</title> <base href="/"/> <link rel="apple-touch-icon" sizes="180x180" href="@basePath/_content/Elsa.Studio.Shell/apple-touch-icon.png"> <link rel="icon" type="image/png" sizes="32x32" href="@basePath/_content/Elsa.Studio.Shell/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="@basePath/_content/Elsa.Studio.Shell/favicon-16x16.png"> <link rel="manifest" href="@basePath/_content/Elsa.Studio.Shell/site.webmanifest"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Grandstander:wght@100&display=swap" rel="stylesheet"> <link href="@basePath/_content/MudBlazor/MudBlazor.min.css" rel="stylesheet"/> <link href="@basePath/_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet"/> <link href="@basePath/_content/Radzen.Blazor/css/material-base.css" rel="stylesheet"> <link href="@basePath/_content/Elsa.Studio.Shell/css/shell.css" rel="stylesheet"> <link href="ElsaStudio.styles.css" rel="stylesheet"> </head> <body> <div id="app"> <div class="loading-splash mud-container mud-container-maxwidth-false"> <h5 class="mud-typography mud-typography-h5 mud-primary-text my-6">Loading...</h5> </div> </div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="@basePath/_content/BlazorMonaco/jsInterop.js"></script> <script src="@basePath/_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script> <script src="@basePath/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script> <script src="@basePath/_content/MudBlazor/MudBlazor.min.js"></script> <script src="@basePath/_content/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script> <script src="@basePath/_content/Radzen.Blazor/Radzen.Blazor.js"></script> <script> window.getClientConfig = function() { return { "apiUrl": "@apiUrl", "basePath": "@basePath" } }; </script> <script src="_framework/blazor.webassembly.js"></script> </body> </html>
Setup Client
Next, we will modify the client project.
Add Elsa Studio Packages
Navigate to the root directory of the client project and add the following Elsa Studio packages:
cd ../ElsaStudio dotnet add package Elsa.Studio dotnet add package Elsa.Studio.Core.BlazorWasm dotnet add package Elsa.Studio.Login.BlazorWasm dotnet add package Elsa.Api.ClientModify Program.cs
Open
Program.cs
and replace its existing content with the code provided below:Program.cs
using System.Text.Json; using Elsa.Studio.Dashboard.Extensions; using Elsa.Studio.Shell; using Elsa.Studio.Shell.Extensions; using Elsa.Studio.Workflows.Extensions; using Elsa.Studio.Contracts; using Elsa.Studio.Core.BlazorWasm.Extensions; using Elsa.Studio.Extensions; using Elsa.Studio.Login.BlazorWasm.Extensions; using Elsa.Studio.Login.HttpMessageHandlers; using Elsa.Studio.Options; using Elsa.Studio.Workflows.Designer.Extensions; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.Options; using Microsoft.JSInterop; // Build the host. var builder = WebAssemblyHostBuilder.CreateDefault(args); // Register root components. builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<HeadOutlet>("head::after"); builder.RootComponents.RegisterCustomElsaStudioElements(); // Register shell services and modules. builder.Services.AddCore(); builder.Services.AddShell(); builder.Services.AddRemoteBackend(elsaClient => elsaClient.AuthenticationHandler = typeof(AuthenticatingApiHttpMessageHandler)); builder.Services.AddLoginModule(); builder.Services.AddDashboardModule(); builder.Services.AddWorkflowsModule(); // Build the application. var app = builder.Build(); // Apply client config. var js = app.Services.GetRequiredService<IJSRuntime>(); var clientConfig = await js.InvokeAsync<JsonElement>("getClientConfig"); var apiUrl = clientConfig.GetProperty("apiUrl").GetString()!; app.Services.GetRequiredService<IOptions<BackendOptions>>().Value.Url = new Uri(apiUrl); // Run each startup task. var startupTaskRunner = app.Services.GetRequiredService<IStartupTaskRunner>(); await startupTaskRunner.RunStartupTasksAsync(); // Run the application. await app.RunAsync();Modify MainLayout.razor
Update
MainLayout.razor
with the following code listing:MainLayout.razor
@inherits LayoutComponentBase <main> @Body </main>
Launch the Application
To see your application in action, navigate back to the root directory containing the host project:
Then execute the following command:
Your application is now accessible at https://localhost:5001.
By default, you can log in using:
Source Code
The source code for this chapter can be found here