Skip to content

Commit 9ac34fd

Browse files
authored
Merge pull request #44 from PandaTechAM/development
SignalR
2 parents 3bbd8d3 + 0357dad commit 9ac34fd

File tree

14 files changed

+308
-96
lines changed

14 files changed

+308
-96
lines changed

Readme.md

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
## Introduction
44

55
**Pandatech.ResponseCrafter** is a comprehensive NuGet package for .NET 8+, specifically designed to enhance exception
6-
handling and logging in ASP.NET Core applications. This package simplifies managing standard and custom exceptions by
7-
crafting detailed error responses suitable for both development and production environments.
6+
handling and logging in ASP.NET Core applications, and now extended to support SignalR hubs. This package simplifies
7+
managing standard and custom exceptions by crafting detailed error responses suitable for both development and
8+
production environments.
89

910
## Features
1011

11-
* **Custom Exception Handling:** Streamlines the process of managing both standard HTTP exceptions and custom
12-
exceptions.
12+
* **Custom Exception Handling:** Streamlines the process of managing both standard HTTP exceptions and custom exceptions
13+
for both REST APIs and SignalR.
1314
* **Detailed Error Responses:** Generates verbose error messages, including stack traces for in-depth debugging in
1415
development environments.
1516
* **Environment-Sensitive Logging:** Provides flexible logging and response behavior based on visibility
@@ -20,8 +21,8 @@ crafting detailed error responses suitable for both development and production e
2021
message. Logging remains the same as in `Private`.
2122
* **Frontend-Friendly Error Messages:** Supports converting error messages to your desired case convention, facilitating
2223
easier integration with frontend localization systems.
23-
* **Standardized Error Responses:** Provides a standardized error response format, making it easier for frontend
24-
applications to parse and display error messages. The error response format is shown below:
24+
* **Standardized Error Responses for REST and SignalR:** Provides a standardized error response format, making it easier
25+
for frontend applications to parse and display error messages. The error response format for REST APIs is shown below:
2526

2627
```json
2728
{
@@ -35,7 +36,22 @@ crafting detailed error responses suitable for both development and production e
3536
},
3637
"Message": "the_request_was_invalid_or_cannot_be_otherwise_served."
3738
}
38-
````
39+
```
40+
41+
For SignalR, the standard error response format is:
42+
43+
```json
44+
{
45+
"InvocationId": "0HMVFE0A0HMVFE0A284AMHMV00HMVFE0A284AM0A284AM",
46+
"Instance": "SendMessage",
47+
"StatusCode": 400,
48+
"Errors": {
49+
"email": "email_address_is_not_in_a_valid_format",
50+
"password": "password_must_be_at_least_8_characters_long"
51+
},
52+
"Message": "the_request_was_invalid_or_cannot_be_otherwise_served."
53+
}
54+
```
3955

4056
## Installation
4157

@@ -89,25 +105,80 @@ public enum NamingConvention
89105
}
90106
```
91107

92-
### 2. Define Custom Exceptions:
108+
### 2. Setup Exception Handling for SignalR:
109+
110+
For SignalR support, register the exception filter for your hubs (or per hub) as follows:
111+
112+
```csharp
113+
builder.Services.AddSignalR(options => options.AddFilter<SignalRExceptionFilter>());
114+
```
115+
116+
If you already have the existing configuration for REST APIs, like
117+
`builder.AddResponseCrafter(NamingConvention.ToSnakeCase);`, SignalR messages will automatically use the same error
118+
handling and response crafting.
119+
120+
### 3. Implement Hub Methods with Standard Arguments:
121+
122+
To allow proper response handling, the hub methods should use the `HubArgument<T>` structure, which provides a unique
123+
invocation ID for tracing errors and crafting detailed responses.
124+
125+
```csharp
126+
public async Task SendMessage(HubArgument<Message> hubArgument)
127+
{
128+
throw new BadRequestException("This is a test exception");
129+
await Clients.All.ReceiveMessage(hubArgument.Argument);
130+
}
131+
```
132+
133+
### 4. Define Custom Exceptions:
93134

94135
Create custom exception classes that inherit from `ApiException` or use the predefined ones. Use `ErrorDetails` records
95136
for
96137
specific error messages related to API requests.
97138

98-
### 3. Configure Middleware:
139+
### 5. Configure Middleware:
99140

100141
* Implement the exception handling middleware in your application's pipeline.
101142

102143
```csharp
103144
app.UseResponseCrafter();
104145
```
105146

106-
### 4. Logging and Error Responses:
147+
## SignalR-Specific Error Handling and Structure
148+
149+
When using the package with SignalR, the following structures are used for standardizing request and response handling:
150+
**HubErrorResponse**
151+
This class is used to format error responses sent back to the client:
152+
153+
```csharp
154+
public class HubErrorResponse
155+
{
156+
public required string InvocationId { get; set; }
157+
public required string Instance { get; set; }
158+
public int StatusCode { get; set; }
159+
public string Message { get; set; } = string.Empty;
160+
public Dictionary<string, string>? Errors { get; set; }
161+
}
162+
```
163+
164+
**HubArgument<T>**
165+
This class wraps around the standard arguments passed to SignalR methods, adding an `InvocationId` to enable unique
166+
error tracing.
167+
168+
```csharp
169+
public class HubArgument<T>
170+
{
171+
public required string InvocationId { get; set; }
172+
public required T Argument { get; set; }
173+
}
174+
```
175+
176+
## Logging and Error Responses:
107177

108-
The package automatically logs warnings or errors and provides crafted responses based on the exception type.
178+
The package automatically logs warnings or errors and provides crafted responses based on the exception type, whether
179+
for REST APIs or SignalR hubs.
109180

110-
## Custom HTTP Exception Already Created
181+
## Predefined HTTP Exceptions
111182

112183
* `BadRequestException`
113184
* `UnauthorizedException`
@@ -161,8 +232,10 @@ UnauthorizedException.ThrowIf(userUnauthorized, "User is unauthorized");
161232
InternalServerErrorException.ThrowIf(userUnauthorized, "User is unauthorized");
162233
```
163234

164-
These examples show how to use the `ThrowIfNullOrNegative`, `ThrowIfNullOrWhiteSpace`, `ThrowIfNullOrEmpty` and `ThrowIfNull` helper methods
165-
from `BadRequestException`, `InternalServerErrorException` and `NotFoundException`. Adjust the object names and values according to your specific
235+
These examples show how to use the `ThrowIfNullOrNegative`, `ThrowIfNullOrWhiteSpace`, `ThrowIfNullOrEmpty` and
236+
`ThrowIfNull` helper methods
237+
from `BadRequestException`, `InternalServerErrorException` and `NotFoundException`. Adjust the object names and values
238+
according to your specific
166239
application needs.
167240

168241
## Recommendations

src/ResponseCrafter/PandaExceptionHandler.cs renamed to src/ResponseCrafter/ApiExceptionHandler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515

1616
namespace ResponseCrafter;
1717

18-
public class PandaExceptionHandler : IExceptionHandler
18+
public class ApiExceptionHandler : IExceptionHandler
1919
{
20-
private readonly ILogger<PandaExceptionHandler> _logger;
20+
private readonly ILogger<ApiExceptionHandler> _logger;
2121
private readonly NamingConvention _convention;
2222
private readonly string _visibility;
2323
private const string DefaultMessage = "something_went_wrong_please_try_again_later_and_or_contact_it_support";
2424

2525
private const string ConcurrencyMessage =
2626
"a_concurrency_conflict_occurred._please_reload_the_resource_and_try_you_update_again";
2727

28-
public PandaExceptionHandler(ILogger<PandaExceptionHandler> logger, IConfiguration configuration,
28+
public ApiExceptionHandler(ILogger<ApiExceptionHandler> logger, IConfiguration configuration,
2929
NamingConventionOptions convention)
3030
{
3131
_logger = logger;
@@ -169,7 +169,7 @@ private async Task HandleApiExceptionAsync(HttpContext httpContext, ApiException
169169
private async Task HandleGeneralExceptionAsync(HttpContext httpContext, Exception exception,
170170
CancellationToken cancellationToken)
171171
{
172-
var verboseMessage = CreateVerboseExceptionMessage(exception);
172+
var verboseMessage = exception.CreateVerboseExceptionMessage();
173173

174174
var response = new ErrorResponse
175175
{
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace ResponseCrafter.Dtos;
2+
3+
/// <summary>
4+
/// Represents a standard structure for error responses sent back to SignalR clients.
5+
/// </summary>
6+
public class HubErrorResponse
7+
{
8+
public required string InvocationId { get; set; }
9+
public required string Instance { get; set; }
10+
public int StatusCode { get; set; }
11+
public string Message { get; set; } = string.Empty;
12+
public Dictionary<string, string>? Errors { get; set; }
13+
}

src/ResponseCrafter/Extensions/WebApplicationExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public static WebApplicationBuilder AddResponseCrafter(this WebApplicationBuilde
1111
NamingConvention namingConvention = NamingConvention.Default)
1212
{
1313
builder.Services.AddSingleton(new NamingConventionOptions { NamingConvention = namingConvention });
14-
builder.Services.AddExceptionHandler<PandaExceptionHandler>();
14+
builder.Services.AddExceptionHandler<ApiExceptionHandler>();
1515

1616

1717
return builder;

src/ResponseCrafter/Helpers/ExceptionMessageBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace ResponseCrafter.Helpers;
55

66
public static class ExceptionMessageBuilder
77
{
8-
public static string CreateVerboseExceptionMessage(Exception exception)
8+
public static string CreateVerboseExceptionMessage(this Exception exception)
99
{
1010
var stringBuilder = new StringBuilder();
1111

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace ResponseCrafter.Helpers;
2+
3+
/// <summary>
4+
/// Generic class representing the hub method argument structure.
5+
/// </summary>
6+
/// <typeparam name="T">Type of the main argument value.</typeparam>
7+
public class HubArgument<T>
8+
{
9+
public required string InvocationId { get; set; }
10+
public required T Argument { get; set; }
11+
}

src/ResponseCrafter/ResponseCrafter.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<Copyright>MIT</Copyright>
99
<PackageIcon>pandatech.png</PackageIcon>
1010
<PackageReadmeFile>Readme.md</PackageReadmeFile>
11-
<Version>3.0.1</Version>
11+
<Version>4.0.0</Version>
1212
<PackageId>Pandatech.ResponseCrafter</PackageId>
1313
<PackageTags>Pandatech, library, exception handler, exception, middleware, Api response</PackageTags>
1414
<Title>ResponseCrafter</Title>
1515
<Description>Handling exceptions, custom Dtos.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-response-crafter</RepositoryUrl>
17-
<PackageReleaseNotes>Added extensions, updated nugets</PackageReleaseNotes>
17+
<PackageReleaseNotes>SignalR ResponseCrafting Support Has Been Added</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
@@ -25,8 +25,9 @@
2525
<ItemGroup>
2626
<PackageReference Include="Humanizer" Version="2.14.1" />
2727
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
28+
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.8" />
2829
<PackageReference Include="PandaTech.FluentImporter" Version="2.0.13" />
29-
<PackageReference Include="Pandatech.GridifyExtensions" Version="1.5.4" />
30+
<PackageReference Include="Pandatech.GridifyExtensions" Version="1.6.2" />
3031
<PackageReference Include="PandaTech.ServiceResponse" Version="1.2.12" />
3132
</ItemGroup>
3233

0 commit comments

Comments
 (0)