Compare commits

...

6 Commits

Author SHA1 Message Date
Developer 02
93019362b7 Bump version to 1.2.2 and update launch settings
Updated the version in `EnvelopeGenerator.GeneratorAPI.csproj` from `1.2.1` to `1.2.2` for all version elements.

Modified the `applicationUrl` in `launchSettings.json` to change the HTTPS port from `7174` to `8088`, while keeping the HTTP port at `5131`.
2025-05-09 13:54:30 +02:00
Developer 02
7f97fd3113 Refactor ReadReceiverNameQuery and improve validation
- Removed parameters from ReadReceiverNameQuery, simplifying its structure.
- Added null check for EmailAddress in GetReceiverName method to enhance input validation.
2025-05-09 10:53:44 +02:00
Developer 02
5ce6c25393 Refactor error handling in controllers
Removed try-catch blocks from various controller methods to simplify error handling and allow exceptions to propagate naturally. Streamlined error logging and response handling using the `ThenAsync` method, enhancing code readability and reducing redundancy. Adjusted conditional checks for improved clarity.
2025-05-09 10:42:11 +02:00
Developer 02
972b258706 Refactor exception handling in middleware
Updated `HandleExceptionAsync` to set response status
directly for each exception type and streamlined JSON
serialization of error messages.
2025-05-09 10:35:04 +02:00
Developer 02
4ee5250b01 Add global exception handling middleware
Introduce `ExceptionHandlingMiddleware` to capture and log exceptions globally in the application. This middleware returns appropriate JSON error responses based on specific exception types, such as `BadRequestException` and `NotFoundException`. The middleware is registered in `Program.cs` to ensure it processes all HTTP requests.
2025-05-09 10:11:28 +02:00
Developer 02
3fce092486 Enhance NotFoundException documentation
Added XML documentation comments to the `NotFoundException` class and its constructors. This improves code readability and provides clear descriptions for developers using this exception.
2025-05-09 10:07:20 +02:00
12 changed files with 288 additions and 283 deletions

View File

@@ -6,8 +6,6 @@ namespace EnvelopeGenerator.Application.Envelopes.Queries.ReceiverName;
/// Eine Abfrage, um die zuletzt verwendete Anrede eines Empfängers zu ermitteln, /// Eine Abfrage, um die zuletzt verwendete Anrede eines Empfängers zu ermitteln,
/// damit diese für zukünftige Umschläge wiederverwendet werden kann. /// damit diese für zukünftige Umschläge wiederverwendet werden kann.
/// </summary> /// </summary>
/// <param name="Envelope">Der Umschlag, für den die Anrede des Empfängers ermittelt werden soll.</param> public record ReadReceiverNameQuery() : ReadReceiverQuery
/// <param name="OnlyLast">Gibt an, ob nur die zuletzt verwendete Anrede zurückgegeben werden soll.</param>
public record ReadReceiverNameQuery(EnvelopeQuery? Envelope = null, bool OnlyLast = true) : ReadReceiverQuery
{ {
} }

View File

@@ -1,11 +1,21 @@
namespace EnvelopeGenerator.Application.Exceptions; namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
/// Represents an exception that is thrown when a requested resource is not found.
/// </summary>
public class NotFoundException : Exception public class NotFoundException : Exception
{ {
/// <summary>
/// Initializes a new instance of the <see cref="NotFoundException"/> class.
/// </summary>
public NotFoundException() public NotFoundException()
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="NotFoundException"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public NotFoundException(string? message) : base(message) public NotFoundException(string? message) : base(message)
{ {
} }

View File

@@ -117,18 +117,10 @@ public partial class AuthController : ControllerBase
[Authorize] [Authorize]
[HttpPost("logout")] [HttpPost("logout")]
public async Task<IActionResult> Logout() public async Task<IActionResult> Logout()
{
try
{ {
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok(); return Ok();
} }
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error occurred.\n{ErrorMessage}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// Prüft, ob der Benutzer ein autorisiertes Token hat. /// Prüft, ob der Benutzer ein autorisiertes Token hat.

View File

@@ -62,8 +62,6 @@ public class EmailTemplateController : ControllerBase
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response> /// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
[HttpGet] [HttpGet]
public async Task<IActionResult> Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null) public async Task<IActionResult> Get([FromQuery] ReadEmailTemplateQuery? emailTemplate = null)
{
try
{ {
if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null)) if (emailTemplate is null || (emailTemplate.Id is null && emailTemplate.Type is null))
{ {
@@ -76,12 +74,6 @@ public class EmailTemplateController : ControllerBase
return temp is null ? NotFound() : Ok(temp); return temp is null ? NotFound() : Ok(temp);
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// Updates an temp template or resets it if no update command is provided. /// Updates an temp template or resets it if no update command is provided.
@@ -106,15 +98,13 @@ public class EmailTemplateController : ControllerBase
/// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response> /// <response code="404">Wenn die gesuchte Abfrage nicht gefunden wird.</response>
[HttpPut] [HttpPut]
public async Task<IActionResult> Update([FromQuery] EmailTemplateQuery? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null) public async Task<IActionResult> Update([FromQuery] EmailTemplateQuery? temp = null, [FromBody] UpdateEmailTemplateCommand? update = null)
{
try
{ {
if (update is null) if (update is null)
{ {
await _mediator.Send(new ResetEmailTemplateCommand(temp)); await _mediator.Send(new ResetEmailTemplateCommand(temp));
return Ok(); return Ok();
} }
else if(temp is null) else if (temp is null)
{ {
return BadRequest("No both id and type"); return BadRequest("No both id and type");
} }
@@ -124,16 +114,5 @@ public class EmailTemplateController : ControllerBase
await _mediator.Send(update); await _mediator.Send(update);
return Ok(); return Ok();
} }
}
catch(NotFoundException)
{
return BadRequest();
}
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message);
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
} }
} }

View File

@@ -60,17 +60,15 @@ public class EnvelopeController : ControllerBase
[Authorize] [Authorize]
[HttpGet] [HttpGet]
public async Task<IActionResult> GetAsync([FromQuery] ReadEnvelopeQuery envelope) public async Task<IActionResult> GetAsync([FromQuery] ReadEnvelopeQuery envelope)
{
try
{ {
if (User.GetId() is int intId) if (User.GetId() is int intId)
return await _envelopeService.ReadByUserAsync(intId, min_status: envelope.Status, max_status: envelope.Status).ThenAsync( return await _envelopeService.ReadByUserAsync(intId, min_status: envelope.Status, max_status: envelope.Status).ThenAsync(
Success: envelopes => Success: envelopes =>
{ {
if(envelope.Id is int id) if (envelope.Id is int id)
envelopes = envelopes.Where(e => e.Id == id); envelopes = envelopes.Where(e => e.Id == id);
if(envelope.Status is int status) if (envelope.Status is int status)
envelopes = envelopes.Where(e => e.Status == status); envelopes = envelopes.Where(e => e.Status == status);
if (envelope.Uuid is string uuid) if (envelope.Uuid is string uuid)
@@ -89,12 +87,6 @@ public class EnvelopeController : ControllerBase
return StatusCode(StatusCodes.Status500InternalServerError); return StatusCode(StatusCodes.Status500InternalServerError);
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// Ruft das Ergebnis eines Dokuments basierend auf der ID ab. /// Ruft das Ergebnis eines Dokuments basierend auf der ID ab.
@@ -107,8 +99,6 @@ public class EnvelopeController : ControllerBase
/// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response> /// <response code="500">Ein unerwarteter Fehler ist aufgetreten.</response>
[HttpGet("doc-result")] [HttpGet("doc-result")]
public async Task<IActionResult> GetDocResultAsync([FromQuery] int id, [FromQuery] bool view = false) public async Task<IActionResult> GetDocResultAsync([FromQuery] int id, [FromQuery] bool view = false)
{
try
{ {
if (User.GetId() is int intId) if (User.GetId() is int intId)
return await _envelopeService.ReadByUserAsync(intId).ThenAsync( return await _envelopeService.ReadByUserAsync(intId).ThenAsync(
@@ -142,12 +132,6 @@ public class EnvelopeController : ControllerBase
return StatusCode(StatusCodes.Status500InternalServerError); return StatusCode(StatusCodes.Status500InternalServerError);
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// ///
@@ -158,8 +142,6 @@ public class EnvelopeController : ControllerBase
[Authorize] [Authorize]
[HttpPost] [HttpPost]
public async Task<IActionResult> CreateAsync([FromQuery] CreateEnvelopeCommand envelope) public async Task<IActionResult> CreateAsync([FromQuery] CreateEnvelopeCommand envelope)
{
try
{ {
envelope.UserId = User.GetId(); envelope.UserId = User.GetId();
var res = await _mediator.Send(envelope); var res = await _mediator.Send(envelope);
@@ -172,10 +154,4 @@ public class EnvelopeController : ControllerBase
else else
return Ok(res); return Ok(res);
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
} }

View File

@@ -83,8 +83,6 @@ public class EnvelopeReceiverController : ControllerBase
[Authorize] [Authorize]
[HttpGet] [HttpGet]
public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver) public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver)
{
try
{ {
var username = User.GetUsernameOrDefault(); var username = User.GetUsernameOrDefault();
@@ -98,7 +96,7 @@ public class EnvelopeReceiverController : ControllerBase
return await _erService.ReadByUsernameAsync( return await _erService.ReadByUsernameAsync(
username: username, username: username,
min_status: envelopeReceiver.Status?.Min, min_status: envelopeReceiver.Status?.Min,
max_status:envelopeReceiver.Status?.Max, max_status: envelopeReceiver.Status?.Max,
envelopeQuery: envelopeReceiver.Envelope, envelopeQuery: envelopeReceiver.Envelope,
receiverQuery: envelopeReceiver.Receiver, receiverQuery: envelopeReceiver.Receiver,
ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty<int>()) ignore_statuses: envelopeReceiver.Status?.Ignore ?? Array.Empty<int>())
@@ -110,12 +108,6 @@ public class EnvelopeReceiverController : ControllerBase
return StatusCode(StatusCodes.Status500InternalServerError, msg); return StatusCode(StatusCodes.Status500InternalServerError, msg);
}); });
} }
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred. {message}", ex.Message);
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab. /// Ruft den Namen des zuletzt verwendeten Empfängers basierend auf der angegebenen E-Mail-Adresse ab.
@@ -133,8 +125,9 @@ public class EnvelopeReceiverController : ControllerBase
[HttpGet("salute")] [HttpGet("salute")]
public async Task<IActionResult> GetReceiverName([FromQuery] ReadReceiverNameQuery receiverName) public async Task<IActionResult> GetReceiverName([FromQuery] ReadReceiverNameQuery receiverName)
{ {
try if (receiverName.EmailAddress is null)
{ return BadRequest();
return await _erService.ReadLastUsedReceiverNameByMail(receiverName.EmailAddress).ThenAsync( return await _erService.ReadLastUsedReceiverNameByMail(receiverName.EmailAddress).ThenAsync(
Success: res => res is null ? Ok(string.Empty) : Ok(res), Success: res => res is null ? Ok(string.Empty) : Ok(res),
Fail: IActionResult (msg, ntc) => Fail: IActionResult (msg, ntc) =>
@@ -146,12 +139,6 @@ public class EnvelopeReceiverController : ControllerBase
return StatusCode(StatusCodes.Status500InternalServerError); return StatusCode(StatusCodes.Status500InternalServerError);
}); });
} }
catch (Exception ex)
{
_logger.LogError(ex, "{message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// Datenübertragungsobjekt mit Informationen zu Umschlägen, Empfängern und Unterschriften. /// Datenübertragungsobjekt mit Informationen zu Umschlägen, Empfängern und Unterschriften.
@@ -193,8 +180,6 @@ public class EnvelopeReceiverController : ControllerBase
[Authorize] [Authorize]
[HttpPost] [HttpPost]
public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeReceiverCommand request) public async Task<IActionResult> CreateAsync([FromBody] CreateEnvelopeReceiverCommand request)
{
try
{ {
CancellationToken cancel = default; CancellationToken cancel = default;
int userId = User.GetId(); int userId = User.GetId();
@@ -225,7 +210,7 @@ public class EnvelopeReceiverController : ControllerBase
#region Add document #region Add document
var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel); var document = await _documentExecutor.CreateDocumentAsync(request.Document.DataAsBase64, envelope.Uuid, cancel);
if(document is null) if (document is null)
return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed."); return StatusCode(StatusCodes.Status500InternalServerError, "Document creation is failed.");
#endregion #endregion
@@ -244,8 +229,8 @@ public class EnvelopeReceiverController : ControllerBase
SELECT @OUT_SUCCESS as [@OUT_SUCCESS];"; SELECT @OUT_SUCCESS as [@OUT_SUCCESS];";
foreach(var rcv in res.SentReceiver) foreach (var rcv in res.SentReceiver)
foreach(var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Array.Empty<Signature>()) foreach (var sign in request.Receivers.Where(r => r.EmailAddress == rcv.EmailAddress).FirstOrDefault()?.Signatures ?? Array.Empty<Signature>())
{ {
using (SqlConnection conn = new(_cnnStr)) using (SqlConnection conn = new(_cnnStr))
{ {
@@ -301,12 +286,6 @@ public class EnvelopeReceiverController : ControllerBase
return Ok(res); return Ok(res);
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
/// <summary> /// <summary>
/// ///

View File

@@ -20,8 +20,6 @@ public class EnvelopeTypeController : ControllerBase
[HttpGet] [HttpGet]
public async Task<IActionResult> GetAllAsync() public async Task<IActionResult> GetAllAsync()
{
try
{ {
return await _service.ReadAllAsync().ThenAsync( return await _service.ReadAllAsync().ThenAsync(
Success: Ok, Success: Ok,
@@ -31,10 +29,4 @@ public class EnvelopeTypeController : ControllerBase
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError); return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
}); });
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
} }

View File

@@ -41,9 +41,7 @@ public class ReceiverController : CRUDControllerBaseWithErrorHandling<IReceiverS
if (receiver.Id is null && receiver.EmailAddress is null && receiver.Signature is null) if (receiver.Id is null && receiver.EmailAddress is null && receiver.Signature is null)
return await base.GetAll(); return await base.GetAll();
try if (receiver.Id is int id)
{
if(receiver.Id is int id)
return await _service.ReadByIdAsync(id).ThenAsync( return await _service.ReadByIdAsync(id).ThenAsync(
Success: Ok, Success: Ok,
Fail: IActionResult (msg, ntc) => Fail: IActionResult (msg, ntc) =>
@@ -58,12 +56,6 @@ public class ReceiverController : CRUDControllerBaseWithErrorHandling<IReceiverS
return NotFound(); return NotFound();
}); });
} }
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
#region REMOVED ENDPOINTS #region REMOVED ENDPOINTS
/// <summary> /// <summary>

View File

@@ -10,9 +10,9 @@
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>EnvelopeGenerator.GeneratorAPI</Product> <Product>EnvelopeGenerator.GeneratorAPI</Product>
<Version>1.2.1</Version> <Version>1.2.2</Version>
<FileVersion>1.2.1</FileVersion> <FileVersion>1.2.2</FileVersion>
<AssemblyVersion>1.2.1</AssemblyVersion> <AssemblyVersion>1.2.2</AssemblyVersion>
<PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath> <PackageOutputPath>Copyright © 2025 Digital Data GmbH. All rights reserved.</PackageOutputPath>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile> <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@@ -0,0 +1,84 @@
namespace EnvelopeGenerator.GeneratorAPI.Middleware;
using EnvelopeGenerator.Application.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Text.Json;
/// <summary>
/// Middleware for handling exceptions globally in the application.
/// Captures exceptions thrown during the request pipeline execution,
/// logs them, and returns an appropriate HTTP response with a JSON error message.
/// </summary>
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
/// </summary>
/// <param name="next">The next middleware in the request pipeline.</param>
/// <param name="logger">The logger instance for logging exceptions.</param>
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
/// <summary>
/// Invokes the middleware to handle the HTTP request.
/// </summary>
/// <param name="context">The HTTP context of the current request.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context); // Continue down the pipeline
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, _logger);
}
}
/// <summary>
/// Handles exceptions by logging them and writing an appropriate JSON response.
/// </summary>
/// <param name="context">The HTTP context of the current request.</param>
/// <param name="exception">The exception that occurred.</param>
/// <param name="logger">The logger instance for logging the exception.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
{
context.Response.ContentType = "application/json";
string message;
switch (exception)
{
case BadRequestException badRequestEx:
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
message = badRequestEx.Message;
break;
case NotFoundException notFoundEx:
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
message = notFoundEx.Message;
break;
default:
logger.LogError(exception, "Unhandled exception occurred.");
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
message = "An unexpected error occurred.";
break;
}
await context.Response.WriteAsync(JsonSerializer.Serialize(new
{
message
}));
}
}

View File

@@ -15,6 +15,7 @@ using EnvelopeGenerator.GeneratorAPI.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using DigitalData.Core.Abstractions.Security.Extensions; using DigitalData.Core.Abstractions.Security.Extensions;
using EnvelopeGenerator.GeneratorAPI.Middleware;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -169,6 +170,8 @@ var app = builder.Build();
deferredProvider.Factory = () => app.Services; deferredProvider.Factory = () => app.Services;
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.MapOpenApi(); app.MapOpenApi();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.

View File

@@ -24,7 +24,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "https://localhost:7174;http://localhost:5131", "applicationUrl": "https://localhost:8088;http://localhost:5131",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }