Empezar.BlazorWebAPI
1.9.3-alpha
dotnet add package Empezar.BlazorWebAPI --version 1.9.3-alpha
NuGet\Install-Package Empezar.BlazorWebAPI -Version 1.9.3-alpha
<PackageReference Include="Empezar.BlazorWebAPI" Version="1.9.3-alpha" />
paket add Empezar.BlazorWebAPI --version 1.9.3-alpha
#r "nuget: Empezar.BlazorWebAPI, 1.9.3-alpha"
// Install Empezar.BlazorWebAPI as a Cake Addin #addin nuget:?package=Empezar.BlazorWebAPI&version=1.9.3-alpha&prerelease // Install Empezar.BlazorWebAPI as a Cake Tool #tool nuget:?package=Empezar.BlazorWebAPI&version=1.9.3-alpha&prerelease
Empezar.BlazorWebAPI
Ease development and maintenance of a blazor web app server project, serving WASM as well as prerendering razor components.
Aim is to consolidate all the common services that all API projects serving WASM or MAUI applications need in secure and robust manner.
Installation
Install the nuget package for Empezar.BlazorWebAPI in your API Project.
You can also use Manager Nuget Packages
to install the same.
dotnet add package Empezar.BlazorWebAPI
Usage
Register the service in Program.cs of API project.
Please note that this service already encapsulates Empezar.Cloud
service. Hence, if using this package, you do not need to register Empezar.Cloud
service.
var builder = WebApplication.CreateBuilder(args);
builder
//Register core services
.AddEZRBlazorWebAPI()
//Register authentication services using AzureAD and Google providers for a single tenant setup, for multi tenant setup, use AddAuthenticationMultiTenant. Register your claims.
.AddAuthenticationSingleTenant<Users, UsersReadParams>((user, claims) =>
{
claims.Add(new(ClaimTypes.Name, user.uname));
})
.AddScoped<IFormFactor, FormFactor>()
//Register UI services to prerender razor components in your SharedUI
.AddSharedUIServices(builder.Configuration[BaseConfigConstants.KestralLoopBackAdress]!, AppPlatform.Web)
//Multi-culture support
.Configure<RequestLocalizationOptions>(options => options.SetDefaultCulture(Constants.SupportedCultures.First())
.AddSupportedCultures(Constants.SupportedCultures).AddSupportedUICultures(Constants.SupportedCultures))
//Register API project to render Razor components in InteractiveWebAssembly mode
.AddRazorComponents().AddInteractiveWebAssemblyComponents()
//Share authentication state between server and client
.AddAuthenticationStateSerialization(options => options.SerializeAllClaims = true);
var app = builder.Build();
if (app.Environment.IsDevelopment()) app.UseWebAssemblyDebugging();
app.UseRequestLocalization();
//Use services and API's provided by the package. ErrorDisplay is a razor component to gracefully handle errors during prerender and show freindly error page. You will need to pass your models library to register your models with postgres for JSONB datatypes
app.UseEZRBlazorWebAPI<ErrorDisplay>(typeof(Users).Assembly)
//Use File upload API (Cloud agnostic using Empezar.Cloud)
.UseEZRFileUpload(UploadValidator)
//Use System Parameters API
.UseEZRSysParams()
//Use Injest Logs API
.UseIngestLog()
//Register your App.razor
.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode()
//Register razor pages from other assemblies
.AddAdditionalAssemblies(typeof(BaseComponent).Assembly,
typeof(OneHaul.SharedUI._Imports).Assembly, typeof(OneHaul.WebClient._Imports).Assembly);
app.Run();
Configurations can be done in appSettings.config or environment valriables as shown below
"EZRBlazorWebAPI": {
"KestralLoopBackAdress": "https://localhost:5001/",
"MaxRequestBodySizeInMB": "20",
"ZeptoMailKey": "Your Zepto Account Key, which will enable sending emails",
"GGWinClientId":"Google Client Id for Windows app (if using MAUI)",
"GGWinClientSecret": "Google Client Secret for Windows app (if using MAUI)",
"GGWinCallbackPath": "Google callback path on Windows (if using MAUI)",
"GGAndClientId": "Google Client Id for Android app (if using MAUI)",
"GGAndClientSecret": "Google Client Secret for Android app (if using MAUI)",
"GGAndCallbackPath": "Google callback path on Android (if using MAUI)",
"GGMacClientId": "Google Client Id for iOS & Mac app (if using MAUI)",
"GGMacClientSecret": "Google Client Secret for iOS & Mac app (if using MAUI)",
"GGMacCallbackPath": "Google callback path for iOS & Mac app (if using MAUI)",
"JWTIss": "Your JWT Issuer",
"JWTAud": "Your JWT Audience",
"JWTKey": "Your JWT Key",
"AgGridLicenseKey": "Your AG-Grid component license key",
"Cloud": {
"Type": "AWS",
"BucketName": "your-bucket-name",
"AzureBlobConnection":"your-azure-connectionstring",
"AllowedFileTypes": [ "docx", "xlsx", "csv", "pdf", "jpeg", "jpg", "png", "zip" ]
},
"Authentications": {
"JWT": {
"Expiry": 4320
},
"Microsoft": {
"ClientId": "Your Microsoft Client Id",
"ClientSecret": "Your Microsoft Client Secret",
"Instance": "https://login.microsoftonline.com/",
"Domain": "common",
"TenantId": "common",
"CallbackPath": "/login-callback",
"CallbackPath_Win": "http://localhost",
"CallbackPath_Android": "com.empezardigital.onehaul://auth",
"CallbackPath_Mac": "http://localhost"
},
"Google": {
"ClientId": "Your Google Client Id",
"ClientSecret": "Your Google Client Secret",
}
},
"Scalar": {
"DocumentName": "DocumentName",
"Title": "Application Title",
"Theme": "Default",
"Endpoint": "apidocs"
},
"PGConnections": {
"EnableAuditLog": true
},
"RateLimiter": {
"AuthUserPermitLimit": 50,
"AuthUserWindowInSec": 10,
"UnAuthUserPermitLimit": 20,
"UnAuthUserWindowInSec": 60
},
"EZRAPIs": {
"File": {
"UploadRoles": [ "admin" ],
"DownloadRoles": [ "admin" ],
"DeleteRoles": [ "admin" ]
},
"SysParams": {
"CreateRoles": [ "admin" ],
"ReadRoles": [ "admin" ],
"UpdateRoles": [ "admin" ]
},
"Auth": {
"SignInFromSingleLocation": false,
"GetOTP": {
"BlockDomainOTP": true,
"ForDomain": "adani.com"
}
}
},
"CSPHeaders": {
"default-src": "'self'",
"script-src": "'self' 'wasm-unsafe-eval' 'nonce-{nonce}' 'sha256-QW7zjkPzGiErwRHGBPrB5pIBLJwqu7+aHJOafqD0Wfo=' ",
"style-src": "'self' 'unsafe-inline' fonts.googleapis.com",
"img-src": "'self' data: fonts.googleapis.com",
"connect-src": "'self'",
"font-src": "'self' data: fonts.googleapis.com fonts.scalar.com fonts.gstatic.com",
"object-src": "'none'",
"base-uri": "'self'",
"form-action": "'self'",
"frame-ancestors": "'none'",
"block-all-mixed-content": "",
"upgrade-insecure-requests": ""
}
}
OR
export EZRBlazorWebAPI__Cloud__Type="AWS"
export EZRBlazorWebAPI__Cloud__AzureBlobConnection="YourConnectionString"
Points to note
1. The nuget requires you to develop using Postgres database
2. All API's will be registered under /api
endpoint and WASM application will be served on root
3. The nuget registers Scalar to display api documentation path can be configured in appsettings Scalar:Endpoint
4. Exception Handling and CSP registration has been added to pipeline.
5. Exception logs from WASM and MAUI are synced to API
6. [DapperJson] attribute is used on classes and properties which needs to be stored in postgres as JSONB datatype
7. API registers AzureAD, Google, Cookie & JWT Authentication. API works with Cookie Authentication and MAUI works with JWT authentication. AzureAD & Google will authenticate and generate Cookie on web and JWT on MAUI.
Base API's Provided
AppSettings Service
:
To share AppSettings between API and MAUI application
Authentication Services
:
Below services are available for Single / Multi Tenant: Login via Microsoft / Google. Validate & exchange Microsoft / Google token for application JWT Token Generate token with APIKey Other API's for OTP Authentication and Authenticator Code Logout
File Services
:
File Upload / Download & Delete
System Parameter Services
:
Use this as a master where data on a key is stored / retrieved / upserted as Json[]. Upserting single record on uin is available.
Ingest Log Service
:
Sync exception Logs > Warning from WASM and MAUI to Web
Check Scalar endpoint for all the API's generated by the nuget along with your created API's
Tools for Development
Fluid API
Make your class related to your functionality implementing IEndpointDefinition
.
With endpoints object you will now get NewEndPoint
method where you can define a new endpoint and register CRUD / Custom API's easily. It uses
public class SampleAPI : IEndpointDefinition
{
public void MapEndpoints(WebApplication endpoints) =>
endpoints.NewEndPoint(BaseAPI.SysParams)
.CreateAPI<SysParams<object>, long>(
createBuilder: (m, c) => SimpleBuilder.Create(@$"WITH find as (
SELECT ARRAY['value', (index-1)::TEXT] as path, (contact->>'uin')::INT uin
FROM master.sysparams, jsonb_array_elements(sysvalue->'value') with ordinality arr(contact, index)
WHERE ((({m.sysvalue}::JSONB)->>'uin')::INT IS NOT NULL) AND systype = {m.systype} AND ((contact->>'uin')::INT = (({m.sysvalue}::JSONB)->>'uin')::INT)
LIMIT 1
), genuin as (
SELECT path, COALESCE(uin, nextval('sysparamsuin')) uin
FROM (SELECT path, uin, 1 ord FROM find UNION ALL
SELECT null, null, 2 ORDER BY ord LIMIT 1) a
), jpath as (
SELECT COALESCE(path, ARRAY['value', uin::TEXT]) as path, uin
FROM genuin
), updtbl as (
UPDATE master.sysparams
SET sysvalue = JSONB_SET(sysvalue, jp.path, JSONB_SET({m.sysvalue}::JSONB, '{{uin}}'::TEXT[], to_jsonb(jp.uin), true), true)
FROM jpath jp
WHERE systype = {m.systype}
)
SELECT uin FROM jpath;"),
roles: app.Configuration.GetSection("EZRBlazorWebAPI:EZRAPIs:SysParams:CreateRoles").Get<string[]>()!,
validateModel: upsertValidator,
onSuccess: onUpsertSuccess)
.ReadAPI<SysParamsReadParams, SysParams<object>>(
createBuilder: (r, c) => SimpleBuilder.Create($"SELECT * FROM master.sysparams sp WHERE systype = {r.systype}"),
roles: app.Configuration.GetSection("EZRBlazorWebAPI:EZRAPIs:SysParams:ReadRoles").Get<string[]>()!,
validateModel: readValidator,
onAfterRead: onAfterRead,
onAfterReadAsync: onAfterReadAsync)
.UpdateAPI<SysParams<object>>(
createBuilder: (m, c) => SimpleBuilder.Create($"UPDATE master.sysparams SET sysvalue = {m.sysvalue}::JSONB WHERE systype = {m.systype}"),
roles: app.Configuration.GetSection("EZRBlazorWebAPI:EZRAPIs:SysParams:UpdateRoles").Get<string[]>()!,
validateModel: updateValidator,
onSuccess: onUpdateSuccess)
.CustomAPI(
pattern: "/",
method: HttpMethod.Get,
customDelegate: (ClaimsPrincipal caller) => "Hi",
routebuilder: r => r.RequireRole("admin").WithDescription("This is real test"));
}
Libraries compatible in API / WASM and MAUI
IQurl
Manage httpClient calls easily
var response = await qurl.OnPath(Url.Path(BaseAPI.AppSettings).Query("platform", BaseConstants.CurrentPlatform.ToString()), HttpMethod.Get, logger)
.ReadAsStringAsync(CancellationToken.None);
ICrypt
Inject ICrypt for Encryption and Decryption accross all platforms. Encrypt / Decrypt model or string cross platform.
async Task<IResult> (AppPlatform platform, IConfiguration config, ICrypt crypt) =>
{
string? microsoftCallbackURLSection = null, googleClientId = null, googleClientSecret = null, googleCallbackURL = null;
switch (platform)
{
case AppPlatform.Windows:
microsoftCallbackURLSection = "EZRBlazorWebAPI:Authentications:Microsoft:CallbackPath_Win";
googleClientId = endpoints.Configuration[BaseConfigConstants.GGWinClientId];
googleClientSecret = endpoints.Configuration[BaseConfigConstants.GGWinClientSecret];
googleCallbackURL = endpoints.Configuration[BaseConfigConstants.GGWinCallbackPath];
break;
case AppPlatform.Android:
microsoftCallbackURLSection = "EZRBlazorWebAPI:Authentications:Microsoft:CallbackPath_Android";
googleClientId = endpoints.Configuration[BaseConfigConstants.GGAndClientId];
googleClientSecret = endpoints.Configuration[BaseConfigConstants.GGAndClientSecret];
googleCallbackURL = endpoints.Configuration[BaseConfigConstants.GGAndCallbackPath];
break;
case AppPlatform.IOS or AppPlatform.MacCatalyst:
microsoftCallbackURLSection = "EZRBlazorWebAPI:Authentications:Microsoft:CallbackPath_Mac";
googleClientId = endpoints.Configuration[BaseConfigConstants.GGMacClientId];
googleClientSecret = endpoints.Configuration[BaseConfigConstants.GGMacClientSecret];
googleCallbackURL = endpoints.Configuration[BaseConfigConstants.GGMacCallbackPath];
break;
case AppPlatform.WASM:
microsoftCallbackURLSection = "EZRBlazorWebAPI:Authentications:Microsoft:CallbackPath";
googleClientId = string.Empty;
googleClientSecret = string.Empty;
googleCallbackURL = string.Empty;
break;
default:
throw new Exception($"Invalid Platform {platform}");
}
string? environment = null;
return Results.Text(await crypt.EncryptAsync(new AppConfig(
Environment: string.IsNullOrEmpty(environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")) ? "Production" : environment,
Microsoft:
new(Authority: $"{endpoints.Configuration["EZRBlazorWebAPI:Authentications:Microsoft:Instance"]!}{endpoints.Configuration["EZRBlazorWebAPI:Authentications:Microsoft:TenantId"]!}/v2.0",
ClientId: endpoints.Configuration["EZRBlazorWebAPI:Authentications:Microsoft:ClientId"]!,
CallbackURL: endpoints.Configuration[microsoftCallbackURLSection]!),
Google:
new(ClientId: googleClientId!,
ClientSecret: googleClientSecret!,
CallbackURL: googleCallbackURL!),
AgGridLicenseKey: endpoints.Configuration["EZRBlazorWebAPI:AgGridLicenseKey"]
), BaseConstants.AppConfigEncryptionKey));
}
IMini
Compress / Decompress bytes, strings, files
//Compress
await iMini.CompressAsync(byteArray);
//Decompress
await iMini.DecompressAsync(byteArray);
Import / Export
Import and Export excel files or razor components as PDF's or extract HTML to send as email
Chron
Create your class and inherit from Chron
, implement ExecuteBackgroundTask
to register a background process. Time can be registered in the constructor.
Chron
takes care that the background service should run on only 1 container in case if multiple have scaled up
AsyncChron
Chron job in MAUI and WASM
BaseAppCache
Lazy Caching services on client side
Fily
File service for WASM and MAUI
Port of Handson Table and AG-Grid
Use components <HandsonTable />
or <AGGrid />
DBFactory
use DBFactory.CreateDbConnection to create connection objects. It accepts a boolean isReadOnly param to determine which connection to return (Read Only or Read/Write)
Please explore for many more such features that can't be documented
License
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
-
net9.0
- Amazon.AspNetCore.DataProtection.SSM (>= 3.5.0)
- Dapper (>= 2.1.66)
- Dapper.SimpleSqlBuilder (>= 4.2.3)
- Empezar.BlazorWebClient (>= 1.9.3-alpha)
- Empezar.Cloud (>= 1.9.3-alpha)
- Microsoft.AspNetCore.Authentication.Google (>= 9.0.2)
- Microsoft.AspNetCore.OpenApi (>= 9.0.2)
- Microsoft.Identity.Web (>= 3.7.1)
- Npgsql (>= 9.0.2)
- Scalar.AspNetCore (>= 2.0.18)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.9.3-alpha | 20 | 2/21/2025 |
1.9.2-alpha | 38 | 2/14/2025 |
1.9.1-alpha | 36 | 2/14/2025 |
1.9.0-alpha | 41 | 2/14/2025 |