Overview
Custom Exception Handling (consumer)
The WUIC framework ships with typed, localized exception handling out of the box. The consumer can extend it to add telemetry, suppress known noise, or emit application-level errors — without rewriting the flow.
Current architecture (summary)
Server side (KonvergenceCore):
JsonExceptionFiltercatches everyMetaService.*(and AsmxProxy in general) exception and produces a JSON envelope{ ok:false, errorCode, args, traceId, fallbackMessage }.MetaExceptionTranslatormaps known exceptions (e.g.JsonReaderException→errors.metadata.props_bag.malformed,OperationDisabledException→errors.auth.operation_disabled,SqlException→errors.db.sql_exceptionwith full passthrough).- The generic fallback is
errors.server.unhandledwith atraceIdfor log correlation.
Client side (wuic-framework-lib):
GlobalHandler(implements Angular'sErrorHandler) is the central dispatcher. Branches in order: SQL passthrough → typed client exceptions → typed server envelope → NG04002 routing → template runtime errors → legacy → fallback.WtoolboxService.runUserCallback/runUserCallbackSync/wrapArchetypeLifecycleSyncare helpers to wrap user-supplied JS coming from metadata (callbacks, archetype lifecycle) with automatic typed envelopes.- Translations are loaded from
_wuic_translationsviaTranslationManagerService(localStorage cache) and support{argName}placeholders interpolated byGlobalHandler. - The
BehaviorSubject GlobalHandler.messageNotificationemits each typed and localized exception: the consumer subscribes here to render the dialog (see theapp.component.tsexample).
Typical wiring in the consumer (app.config.ts):
import { GlobalHandler } from './wuic-bridges/core';
providers: [
...
{ provide: ErrorHandler, useClass: GlobalHandler },
...
]Extension points
Pattern 1 — Subscribe (cross-cutting telemetry, RECOMMENDED)
Add a second subscriber to GlobalHandler.messageNotification next to the dialog one. Lets you forward each (already-typed, already-localized) exception to a custom analytics/logging system.
import { GlobalHandler } from './wuic-bridges/core';
GlobalHandler.messageNotification.subscribe((data) => {
const exc = data?.exception;
if (!exc?.errorCode) return;
// Filter known noise.Pros:
- Doesn't alter the framework flow.
- Add/remove without DI wiring changes.
- Receives the exception AFTER typing (errorCode + args interpolated).
Pattern 2 — Subclass GlobalHandler (override handleError)
When you need to act BEFORE the framework decides what to do: filter known non-actionable errors, enrich args with app context (build version, user role), convert a generic error into a typed one.
import { ErrorHandler, Injectable } from '@angular/core';
import { GlobalHandler } from './wuic-bridges/core';
@Injectable()
export class MyCustomErrorHandler extends GlobalHandler {
override handleError(e: any): void {
// 1. Suppress non-actionable noise.Wiring:
// app.config.ts
import { MyCustomErrorHandler } from './exception-handling/custom-error-handler.example';
providers: [
...
{ provide: ErrorHandler, useClass: MyCustomErrorHandler },
...Pros:
- Full control over the entry point.
- Keeps all framework logic by calling
super.handleError(e).
Caveat:
- If you DO NOT call
super.handleError, you lose typed envelopes — only use Pattern 3 if truly necessary.
Pattern 3 — Full replace (NOT RECOMMENDED)
Implementing ErrorHandler from scratch without extending GlobalHandler disables ALL built-in features: translations, typed envelopes, SQL passthrough, dedicated dialogs, NG04002 routing, dynamic-template error tagging. Only use it for very specific requirements where you're prepared to re-implement the end-to-end flow.
Pattern 4 — Throw typed exceptions from consumer code
The consumer can EMIT its own typed exceptions that receive the same treatment (translation + dialog) as framework exceptions. Import WuicClientException and throw with your own application errorCode:
import { WuicClientException } from 'wuic-framework-lib-src/exception/WuicClientException';
if (!myConfig.exportEnabled) {
throw new WuicClientException(
'errors.myapp.feature_disabled',
{ feature: 'export-pdf' },
{ surface: 'service', targetName: 'MyAppService.exportPdf' }Add the translations for errors.myapp.feature_disabled to your _wuic_translations seed (at least it-IT + en-US, plus other supported locales). From that point on any throw produces a localized dialog with no further wiring:
-- Example seed (idempotent, see scripts/upsert-wuic-translations.ps1).
INSERT INTO _wuic_translations (translation_key, locale, translation_text)
VALUES
('errors.myapp.feature_disabled', 'it-IT', "Funzionalita' '{feature}' disabilitata in questa versione."),
('errors.myapp.feature_disabled', 'en-US', "Feature ''{feature}'' is disabled in this build.");The {feature} placeholder is automatically interpolated from the args passed to the WuicClientException.
Server-side exceptions from consumer code
On the server (consumer controller/service) the pattern is analogous: throw a WuicException from the framework namespace and the JsonExceptionFilter produces the typed JSON envelope that the client localizes.
using WEB_UI_CRAFTER.Helpers.Exceptions;
if (!_myService.IsLicensed("export-pdf"))
{
throw new WuicException(
"errors.myapp.license.missing",
new Dictionary<string, object>HttpStatus controls the response HTTP code; FallbackMessage is shown when the translation is missing.
Complete example in the WuicTest project
A working example covering all 4 patterns (with comments, inline snippets, and example wiring) is available at:
WuicTest/wwwroot/src/app/exception-handling/custom-error-handler.example.ts
The file is part of the test consumer project source and contains 4 sections:
1. installCustomTelemetry() — Pattern 1 (subscribe), ready to call from AppComponent.ngOnInit.
2. MyCustomErrorHandler — Pattern 2 (subclass), ready to wire in app.config.ts.
3. Pattern 3 — commented example (NOT recommended).
4. Pattern 4 — WuicClientException throw snippet from app code.
Best practice
- Prefer Pattern 1 (subscribe) for telemetry/logging — doesn't alter the flow and is easy to remove.
- Use Pattern 2 (subclass) only when you need to intervene before the default (noise filtering, args enrichment).
- Never Pattern 3 unless you have specific enterprise requirements — you lose all built-in functionality.
- Always seed translations for your application
errorCodes (at least it-IT + en-US) before releasing to production, otherwise the dialog shows the raw key. - Use server-side `traceId` in the translation template (e.g.
"Reference: {traceId}") to ease cross-team support diagnostics.