Blazor ShowErrors Service
using CurrieTechnologies.Razor.SweetAlert2;
using Frontend.Common;
using System.Net.Http.Json;
namespace Frontend.Services;
public class ShowErrors(SweetAlertService Swal)
{
private readonly SweetAlertService _swal = Swal;
public async Task DisplayError(HttpResponseMessage response)
{
var message = await response.Content.ReadFromJsonAsync();
List<string> showError = message.Message.Split(",").ToList(); var stringOfError = ""; foreach (var i in showError) { stringOfError += $"{i} \n"; } await Swal.FireAsync( new SweetAlertOptions { Title = stringOfError, Icon = SweetAlertIcon.Error }); } public async Task DisplayValidationError(HttpResponseMessage response) { var validationResponse = await response.Content.ReadFromJsonAsync<ValidationErrorResponse>(); if (validationResponse?.Errors != null) { string stringOfError = ""; foreach (var error in validationResponse.Errors) { stringOfError += $"{error.Key} : {string.Join(", ", error.Value)}\n"; } await _swal.FireAsync(new SweetAlertOptions { Title = $"{stringOfError}", Icon = SweetAlertIcon.Error }); } } }
--- ## 2. What Is the Purpose of This Class? The `ShowErrors` class is a **custom service that displays backend errors to the user through SweetAlert pop-up messages**. Suppose a user is registering on your website. They enter an email address that already exists in the database. The backend might return an error such as:
json
{
"message": "Email already exists"
}
Instead of writing the same code to read the response and display a SweetAlert in every component, you can call:
csharp
await showErrors.DisplayError(response);
Similarly, if the backend returns field validation errors, you can call:
csharp
await showErrors.DisplayValidationError(response);
This class centralizes error-display logic in one place, making it reusable across your Login, Signup, and other pages. ## 3. Understanding the `using` Statements ### A. SweetAlert2
csharp
using CurrieTechnologies.Razor.SweetAlert2;
This namespace provides the SweetAlert2 types used by your service, including: - `SweetAlertService` - `SweetAlertOptions` - `SweetAlertIcon` **Why is it needed?** It allows you to display attractive pop-up messages instead of relying only on plain text or browser alerts. For example, the following code displays an error pop-up:
csharp
await Swal.FireAsync(
new SweetAlertOptions
{
Title = "Something went wrong",
Icon = SweetAlertIcon.Error
});
### B. Your common classes
csharp
using Frontend.Common;
This imports the classes defined in your `Frontend.Common` namespace. In this code, those classes include: - `ErrorResponse` - `ValidationErrorResponse` These classes represent the structure of the JSON error responses returned by your backend. ### C. Reading JSON from an HTTP response
csharp
using System.Net.Http.Json;
This namespace provides convenient HTTP JSON extension methods, including:
csharp
ReadFromJsonAsync()
You use this method to deserialize a JSON response body into a C# object. For example:
csharp
var message =
await response.Content.ReadFromJsonAsync();
This converts JSON data into an `ErrorResponse` object. --- ## 4. Understanding the Namespace
csharp
namespace Frontend.Services;
A namespace groups related classes together. Here, `ShowErrors` belongs to the `Frontend.Services` namespace. **Why is this useful?** It keeps service classes organized separately from your Razor pages, models, and other application code. For example, a Razor component can access the service by importing its namespace:
razor
@using Frontend.Services
If the namespace is already imported globally, you do not need to add it to every component. --- ## 5. Understanding the Class Declaration
csharp
public class ShowErrors(SweetAlertService Swal)
{
This declaration uses a C# feature called a **primary constructor**. The class accepts a `SweetAlertService` object as a constructor parameter. ### What is dependency injection doing here? Instead of creating a SweetAlert service manually, the application provides the registered service through dependency injection. The parameter:
csharp
SweetAlertService Swal
receives the service instance. The class then uses that service to display pop-ups. **In simple words:** `ShowErrors` does not create the SweetAlert service itself. It receives the service from the application. --- ## 6. Storing the SweetAlert Service in a Field
csharp
private readonly SweetAlertService _swal = Swal;
This creates a private field named `_swal` and initializes it using the primary constructor parameter `Swal`. ### Explanation of each keyword - `private`: Only this class can directly access the field. - `readonly`: The field can be assigned during initialization or in a constructor, but cannot be reassigned afterward. - `SweetAlertService`: The field's data type. - `_swal`: The field name. - `= Swal`: Assigns the injected service to the field. You use `_swal` in `DisplayValidationError()`. However, the class also uses `Swal` directly in `DisplayError()`. Both refer to the injected SweetAlert service, so you could use `_swal` consistently in both methods. --- ## 7. Understanding `DisplayError()`
csharp
public async Task DisplayError(HttpResponseMessage response)
{
This method handles **general errors returned by the backend**. Examples include: - Email already exists. - Invalid login credentials. - An operation could not be completed. - A general business-rule error occurred. ### What does the parameter mean?
csharp
HttpResponseMessage response
The method receives the HTTP response from an API request. For example, in a Signup component:
csharp
var response = await http.PostAsJsonAsync("Auth/signup", user);
if (!response.IsSuccessStatusCode)
{
await showErrors.DisplayError(response);
}
The API request produces an HTTP response, and the component passes that response to `DisplayError()`. ### What does `async Task` mean? - `async` allows the method to use `await`. - `Task` represents the asynchronous operation. - `public` allows other classes to call this method. The method is asynchronous because reading JSON and displaying a SweetAlert can involve asynchronous operations. --- ## 8. Converting the Backend Response into a C# Object
csharp
var message = await response.Content.ReadFromJsonAsync();
This line reads the HTTP response body and attempts to convert its JSON content into an `ErrorResponse` object. Suppose the backend returns:
json
{
"message": "Email already exists"
}
Your `ErrorResponse` class might look like this:
csharp
namespace Frontend.Common;
public class ErrorResponse
{
public string Message { get; set; } = "";
}
After deserialization, the result is conceptually equivalent to:
csharp
message.Message = "Email already exists";
### Understanding the parts - `response.Content`: The content body returned by the backend. - `ReadFromJsonAsync<ErrorResponse>()`: Reads the JSON and converts it into the specified C# type. - `<ErrorResponse>`: Tells C# which class should represent the JSON data. - `await`: Waits for the asynchronous deserialization operation to complete. - `var message`: Stores the resulting object. **Important:** `ReadFromJsonAsync<T>()` can return `null`, for example if the JSON body is empty. Invalid JSON can also cause an exception. Your current code assumes that a valid, non-null response object is returned. --- ## 9. Splitting the Error Message
csharp
List showError = message.Message.Split(",").ToList();
This line splits the error message wherever it encounters a comma and converts the resulting pieces into a list of strings. Suppose the backend returns:
text
Email already exists, Password is invalid, Account is disabled
The `Split(",")` operation separates the text into these pieces:
text
Email already exists
Password is invalid
Account is disabled
Then `ToList()` converts the resulting string array into a `List<string>`. The resulting list conceptually looks like this:
csharp
List showError = new()
{
"Email already exists",
" Password is invalid",
" Account is disabled"
};
Notice that spaces after commas remain at the beginning of some strings. ### Why split the message? The developer expects the backend to return multiple error messages separated by commas. Splitting makes it possible to process each message individually. **Important:** This only works correctly if the backend uses commas as separators. If an individual error message itself contains commas, the code will split that message too. --- ## 10. Creating an Empty String
csharp
var stringOfError = "";
This creates an initially empty string. The variable will be used to combine the individual errors into one string before displaying the SweetAlert. For example, it starts as:
text
""
After processing the first error, it becomes:
text
Email already exists
After processing the next error, it becomes:
text
Email already exists
Password is invalid
The newline character `\n` helps separate the messages. --- ## 11. Processing Each Error with `foreach`
csharp
foreach (var i in showError)
{
stringOfError += $"{i} \n";
}
### What is `foreach`? `foreach` is a C# loop that processes each item in a collection one at a time. In this case, the collection is:
csharp
showError
Each item is temporarily represented by the variable:
csharp
i
### How does it execute? Suppose the list contains:
text
Email already exists
Password is invalid
Account is disabled
The loop processes the first error, then the second, and finally the third. This expression:
csharp
stringOfError += $"{i} \n";
adds the current error and a newline to the existing string. The `$` symbol indicates string interpolation, which allows the variable `i` to be inserted into the string. The `+=` operator appends the new text to the existing value. ### What is the final result? Conceptually, `stringOfError` contains:
text
Email already exists
Password is invalid
Account is disabled
Each error is placed on a separate line. --- ## 12. Displaying the General Error in SweetAlert
csharp
await Swal.FireAsync(
new SweetAlertOptions
{
Title = stringOfError,
Icon = SweetAlertIcon.Error
});
This displays a SweetAlert pop-up containing the combined error message. ### A. `Swal.FireAsync()` This method asks the SweetAlert service to display a pop-up. The `await` keyword waits for the asynchronous operation to complete. ### B. `new SweetAlertOptions` This creates an options object that configures the pop-up. ### C. `Title = stringOfError` This sets the pop-up's title to the combined error text. Despite the property being named `Title`, you are using it to display all the error messages. ### D. `Icon = SweetAlertIcon.Error` This selects the error icon, usually displayed as a red error symbol. ### Expected result If the backend returns three errors, the user sees an error pop-up containing all three messages. --- ## 13. Understanding `DisplayValidationError()`
csharp
public async Task DisplayValidationError(HttpResponseMessage response)
{
This method handles **field-level validation errors** returned by the backend. General errors and validation errors are not always structured in the same way. A general error might look like this:
json
{
"message": "Invalid login credentials"
}
A validation error response might look like this:
json
{
"errors": {
"Email": [
"Email is required",
"Email format is invalid"
],
"Password": [
"Password must contain at least 8 characters"
]
}
}
The second response associates each error with a particular field. That is why this method uses a separate response model, `ValidationErrorResponse`. --- ## 14. Reading the Validation Response
csharp
var validationResponse =
await response.Content.ReadFromJsonAsync();
This line reads the response body and converts the JSON into a `ValidationErrorResponse` object. A corresponding model could look like this:
csharp
namespace Frontend.Common;
public class ValidationErrorResponse
{
public Dictionary? Errors { get; set; }
}
The exact model should match the JSON structure returned by your backend. ### What is a `Dictionary`? A dictionary stores key-value pairs. In this example: - The **key** is the field name, such as `Email` or `Password`. - The **value** is an array of validation messages associated with that field. For example:
csharp
{
"Email": new[]
{
"Email is required",
"Email format is invalid"
}
}
This structure allows the code to process each field and its associated errors. --- ## 15. Checking Whether Validation Errors Exist
csharp
if (validationResponse?.Errors != null)
{
This condition checks whether the validation response and its `Errors` property are available. ### What does `?.` mean? The `?.` operator is called the null-conditional operator. Instead of directly accessing:
csharp
validationResponse.Errors
which could throw a `NullReferenceException` if `validationResponse` is null, the code uses:
csharp
validationResponse?.Errors
If `validationResponse` is null, the expression evaluates to null rather than attempting to access `Errors`. The condition then checks that `Errors` is not null. **Why is this useful?** It prevents the code inside the `if` block from executing when no validation-error collection is available. **Important:** This does not handle malformed JSON or exceptions during deserialization. It only handles a null result or a null `Errors` property. --- ## 16. Creating a String for Validation Errors
csharp
string stringOfError = "";
This creates an empty string that will contain all the validation messages. Unlike `DisplayError()`, which splits a single comma-separated message, this method processes field names and their corresponding collections of errors. --- ## 17. Looping Through Validation Errors
csharp
foreach (var error in validationResponse.Errors)
{
stringOfError += $"{error.Key} : {string.Join(", ", error.Value)}\n";
}
This is the main part of the validation-error method. ### A. `foreach (var error in validationResponse.Errors)` This loop processes each key-value pair in the dictionary. For example, the first item might contain:
text
Key: Email
Value: ["Email is required", "Email format is invalid"]
The next item might contain:
text
Key: Password
Value: ["Password must contain at least 8 characters"]
### B. `error.Key`
csharp
error.Key
Returns the name of the field associated with the errors. For example:
text
Email
### C. `error.Value`
csharp
error.Value
Returns the collection of validation messages for that field. For example:
text
Email is required
Email format is invalid
### D. `string.Join(", ", error.Value)` This combines multiple strings into one string, separating them with a comma and a space. For example:
csharp
string.Join(", ", new[]
{
"Email is required",
"Email format is invalid"
});
Produces:
text
Email is required, Email format is invalid
### E. Combining everything
csharp
stringOfError += $"{error.Key} : {string.Join(", ", error.Value)}\n";
This adds the field name, a colon, the messages, and a newline to the accumulated string. The result could look like:
text
Email : Email is required, Email format is invalid
Password : Password must contain at least 8 characters
This makes it easier for the user to see which field has a problem. --- ## 18. Displaying the Validation Errors
csharp
await _swal.FireAsync(new SweetAlertOptions
{
Title = $"{stringOfError}",
Icon = SweetAlertIcon.Error
});
This displays the validation errors using SweetAlert. ### `Title = $"{stringOfError}"` This assigns the combined validation-error text to the pop-up's title. The string interpolation is unnecessary here because the expression contains no variable substitutions. You could simply write:
csharp
Title = stringOfError
### `Icon = SweetAlertIcon.Error` This tells SweetAlert to display an error icon. ### Why use `_swal` instead of `Swal`? Earlier, the class stored the injected service in this field:
csharp
private readonly SweetAlertService _swal = Swal;
This method uses the field to display the alert. Both approaches use the injected SweetAlert service. For consistency, you could use `_swal` in both methods. --- ## 19. Difference Between the Two Methods | Feature | `DisplayError()` | `DisplayValidationError()` | |---|---|---| | Purpose | Displays general backend errors | Displays field-level validation errors | | Response model | `ErrorResponse` | `ValidationErrorResponse` | | Expected structure | A message string | A dictionary of fields and messages | | Processing | Splits a comma-separated message | Loops through field names and their errors | | Output | Combined error text | Field names with their associated messages | | Icon | Error | Error | ### Example of when to call each method Use the general error method when the backend returns:
json
{
"message": "Invalid email or password"
}
csharp
await showErrors.DisplayError(response);
Use the validation error method when the backend returns:
json
{
"errors": {
"Email": ["Email is required"],
"Password": ["Password is required"]
}
}
csharp
await showErrors.DisplayValidationError(response);
The method you call must match the response structure returned by the backend. --- ## 20. How This Service Is Used in Your Blazor Application For example, a Signup component can inject the service:
razor
@inject ShowErrors showErrors
After submitting the form, the component sends a request to the backend:
csharp
var response = await http.PostAsJsonAsync("Auth/signup", user);
It can then decide how to handle the response:
csharp
if (response.IsSuccessStatusCode)
{
// Handle successful registration.
}
else if ((int)response.StatusCode == 400)
{
await showErrors.DisplayValidationError(response);
}
else
{
await showErrors.DisplayError(response);
}
This is an illustrative example. Use the branches that match the actual response formats and status codes returned by your API. ### Execution flow 1. The user submits the Signup form. 2. The frontend sends the user's information to the backend. 3. The backend validates the information. 4. The backend returns a success or error response. 5. The frontend passes the response to the appropriate `ShowErrors` method. 6. The service deserializes the response into a C# model. 7. It builds a readable message. 8. SweetAlert displays the message to the user. --- ## 21. Important Improvements ### A. Handle null responses safely In `DisplayError()`, this line assumes `message` and `message.Message` are not null:
csharp
List showError = message.Message.Split(",").ToList();
If the response body is empty or the `Message` property is null, this can fail. Add null checks and a fallback message. ### B. Avoid relying on commas as separators The code splits a single message using commas. This can incorrectly divide a message that naturally contains commas. A structured list of error messages from the backend is more reliable when multiple independent errors must be displayed. ### C. Consider how SweetAlert displays newlines You currently use `\n` to separate messages. Depending on the SweetAlert option and rendering behavior, newlines in the title may not appear as separate visible lines. For reliable formatting, consider using a supported text or HTML content option from your installed SweetAlert2 library. If you use HTML, encode or safely render backend-provided messages rather than inserting untrusted strings directly into HTML. ### D. Improve validation-response handling The null check protects against a missing validation-error collection, but JSON deserialization can still fail. If the backend sometimes returns an empty body or a different error structure, handle those cases gracefully. ### E. Use the injected field consistently The class currently uses `Swal` in `DisplayError()` and `_swal` in `DisplayValidationError()`. Both refer to the same injected service, but using `_swal` in both methods makes the code more consistent. --- ## 22. Final Summary The `ShowErrors` class is a reusable error-display service for your Blazor frontend. - `ErrorResponse` represents general backend errors. - `ValidationErrorResponse` represents field-specific validation errors. - `ReadFromJsonAsync<T>()` converts JSON into C# objects. - `Split()` separates a comma-delimited message. - `foreach` processes each error. - `string.Join()` combines multiple validation messages. - `SweetAlertService.FireAsync()` displays the error pop-up. **The key idea:** Instead of repeating error-parsing and SweetAlert code in every Razor component, you place it in one reusable service and call the appropriate method whenever an API request fails. public class ErrorResponse { public int Statuscode { get; set; } public string Message { get; set; } public string? Details { get; set; } public DateTime TimeSpent { get; set; } } public class ValidationErrorResponse { public string? Type { get; set; } public string? Title { get; set; } public int Status { get; set; } public Dictionary<string, string[]>? Errors { get; set; } public string? TraceId { get; set; } } 