Added exception handling for better tests.

This commit is contained in:
emantzoros 2025-09-19 21:43:03 +03:00
parent c48c1e6ce4
commit 2b5dda072a
11 changed files with 137 additions and 5 deletions

View File

@ -21,6 +21,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Scrutor" Version="6.1.0" /> <PackageReference Include="Scrutor" Version="6.1.0" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,71 @@
using System.Net;
using BlazorApp.Models.Exceptions;
using Newtonsoft.Json;
namespace BlazorApp.Middlewares
{
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(
RequestDelegate next,
ILogger<ErrorHandlingMiddleware> logger)
{
this.next = next;
this._logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (System.Exception ex)
{
await this.HandleAsync(context, ex);
}
}
private async Task HandleAsync(HttpContext context, System.Exception exception)
{
HandledException handled = this.HandleException(exception);
this._logger.LogError(exception, $"returning code {handled.StatusCode} and payload {handled.Message}");
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)handled.StatusCode;
await context.Response.WriteAsync(handled.Message);
}
protected HandledException HandleException(System.Exception exception)
{
HttpStatusCode statusCode;
if (exception is NotFoundException)
{
statusCode = HttpStatusCode.NotFound;
}
else if (exception is ValidationException)
{
statusCode = HttpStatusCode.BadRequest;
}
else if (exception is BusinessException)
{
statusCode = HttpStatusCode.InternalServerError;
}
else
{
statusCode = HttpStatusCode.InternalServerError;
}
return new HandledException()
{
StatusCode = statusCode,
Message = JsonConvert.SerializeObject(new { error = exception.Message })
};
}
}
}

View File

@ -0,0 +1,9 @@
namespace BlazorApp.Models.Exceptions
{
public class BusinessException : System.Exception
{
public BusinessException() : base() { }
public BusinessException(String message) : base(message) { }
public BusinessException(String message, System.Exception innerException) : base(message, innerException) { }
}
}

View File

@ -0,0 +1,10 @@
using System.Net;
namespace BlazorApp.Models.Exceptions
{
public class HandledException
{
public HttpStatusCode StatusCode { get; set; }
public string Message { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace BlazorApp.Models.Exceptions
{
public class NotFoundException : System.Exception
{
public NotFoundException() : base() { }
public NotFoundException(String message) : base(message) { }
public NotFoundException(String message, System.Exception innerException) : base(message, innerException) { }
}
}

View File

@ -0,0 +1,9 @@
namespace BlazorApp.Models.Exceptions
{
public class ValidationException : System.Exception
{
public ValidationException() : base() { }
public ValidationException(String message) : base(message) { }
public ValidationException(String message, System.Exception innerException) : base(message, innerException) { }
}
}

View File

@ -4,6 +4,7 @@ using BlazorApp.Components;
using BlazorApp.Data.Context; using BlazorApp.Data.Context;
using BlazorApp.Interfaces.Repositories; using BlazorApp.Interfaces.Repositories;
using BlazorApp.Interfaces.Services; using BlazorApp.Interfaces.Services;
using BlazorApp.Middlewares;
using BlazorApp.Models.Config; using BlazorApp.Models.Config;
using BlazorApp.Repositories.Database; using BlazorApp.Repositories.Database;
using BlazorApp.Repositories.InMemory; using BlazorApp.Repositories.InMemory;
@ -40,6 +41,7 @@ builder.Services.AddClientServices();
var app = builder.Build(); var app = builder.Build();
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.ExecuteDbMigration(); app.ExecuteDbMigration();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())

View File

@ -1,9 +1,9 @@
using BlazorApp.Data; using BlazorApp.Data;
using BlazorApp.Data.Context; using BlazorApp.Data.Context;
using BlazorApp.Interfaces.Repositories; using BlazorApp.Interfaces.Repositories;
using BlazorApp.Models.Exceptions;
using BlazorApp.Shared.Queries; using BlazorApp.Shared.Queries;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
namespace BlazorApp.Repositories.Database namespace BlazorApp.Repositories.Database
{ {
@ -27,7 +27,7 @@ namespace BlazorApp.Repositories.Database
Customer data = await this._dbContext.Customers.FindAsync(id); Customer data = await this._dbContext.Customers.FindAsync(id);
if (data is null) if (data is null)
{ {
throw new KeyNotFoundException($"Customer with id: {id} not found"); throw new NotFoundException($"Customer with id: {id} not found");
} }
return data; return data;
} }

View File

@ -0,0 +1,11 @@
namespace BlazorApp.Services
{
public class BaseService<T>
{
protected readonly ILogger<T> _logger;
public BaseService(ILogger<T> logger)
{
this._logger = logger;
}
}
}

View File

@ -1,32 +1,38 @@
using BlazorApp.Interfaces.Repositories; using BlazorApp.Interfaces.Repositories;
using BlazorApp.Interfaces.Services; using BlazorApp.Interfaces.Services;
using BlazorApp.Models.Exceptions;
using BlazorApp.Shared.Models; using BlazorApp.Shared.Models;
using BlazorApp.Shared.Models.Pagination; using BlazorApp.Shared.Models.Pagination;
using BlazorApp.Shared.Queries; using BlazorApp.Shared.Queries;
namespace BlazorApp.Services namespace BlazorApp.Services
{ {
public class CustomerService : ICustomerService public class CustomerService : BaseService<CustomerService>, ICustomerService
{ {
private readonly ICustomerRepository _customerRepository; private readonly ICustomerRepository _customerRepository;
public CustomerService(ICustomerRepository customerRepository) public CustomerService(ICustomerRepository customerRepository, ILogger<CustomerService> logger) : base(logger)
{ {
this._customerRepository = customerRepository; this._customerRepository = customerRepository;
} }
public async Task Delete(string id) public async Task Delete(string id)
{ {
this._logger.LogInformation($"Gonna delete customer with id: {id}");
await this._customerRepository.Delete(id); await this._customerRepository.Delete(id);
this._logger.LogInformation($"Deleted customer with id: {id}");
} }
public async Task<Customer> Get(string id) public async Task<Customer> Get(string id)
{ {
this._logger.LogInformation($"Gonna get customer with id: {id}");
Data.Customer data = await this._customerRepository.Get(id); Data.Customer data = await this._customerRepository.Get(id);
this._logger.LogInformation($"Got customer with id: {id}");
return data.Convert(); return data.Convert();
} }
public async Task<PaginatedResult<Customer>> Query(CustomerQuery query) public async Task<PaginatedResult<Customer>> Query(CustomerQuery query)
{ {
this._logger.LogInformation($"Querying customers");
IEnumerable<Data.Customer> datas = await this._customerRepository.Query(query); IEnumerable<Data.Customer> datas = await this._customerRepository.Query(query);
return new PaginatedResult<Customer>() return new PaginatedResult<Customer>()
{ {
@ -37,12 +43,16 @@ namespace BlazorApp.Services
public async Task Save(Customer customer) public async Task Save(Customer customer)
{ {
this._logger.LogInformation($"Gonna save new customer");
await this._customerRepository.Save(customer); await this._customerRepository.Save(customer);
this._logger.LogInformation($"New customer saved successfully");
} }
public async Task Update(Customer customer) public async Task Update(Customer customer)
{ {
this._logger.LogInformation($"Gonna update customer with id: {customer.Id}");
await this._customerRepository.Update(customer); await this._customerRepository.Update(customer);
this._logger.LogInformation($"Customer with id: {customer.Id} updated successfully");
} }
public async Task<int> Count() => await this._customerRepository.Count(); public async Task<int> Count() => await this._customerRepository.Count();

View File

@ -14,6 +14,6 @@
"CustomerCacheKey": "customer", "CustomerCacheKey": "customer",
"CustomerCacheSeconds": 5 "CustomerCacheSeconds": 5
}, },
"MockCustomers": true, "MockCustomers": false,
"MockCustomersCount": 238 "MockCustomersCount": 238
} }