Benutzerdefinierte Fehlerseiten für die Statuscodes 404 und 500 im HomeController hinzugefügt, um verschiedene Benutzerfälle zu behandeln.

This commit is contained in:
Developer 02 2024-04-26 12:22:09 +02:00
parent 966b7de3c4
commit 6b3c90c618
13 changed files with 137 additions and 51 deletions

View File

@ -19,5 +19,7 @@ namespace EnvelopeGenerator.Application.Contracts
Task<IServiceResult<bool>> VerifyAccessCodeAsync(string uuid, string signature, string accessCode); Task<IServiceResult<bool>> VerifyAccessCodeAsync(string uuid, string signature, string accessCode);
Task<IServiceResult<bool>> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode); Task<IServiceResult<bool>> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode);
Task<IServiceResult<bool>> IsExisting(string envelopeReceiverId);
} }
} }

View File

@ -2,6 +2,7 @@
{ {
public enum EnvelopeFlag public enum EnvelopeFlag
{ {
EnvelopeOrReceiverNonexists EnvelopeOrReceiverNonexists,
NonDecodableEnvelopeReceiverId
} }
} }

View File

@ -6,19 +6,20 @@ namespace EnvelopeGenerator.Application
{ {
public static class EnvelopeGeneratorExtensions public static class EnvelopeGeneratorExtensions
{ {
public static void LogEnvelopeError(this ILogger logger, string receiverId, string? message, params object?[] args) public static void LogEnvelopeError(this ILogger logger, string envelopeEeceiverId, Exception? exception = null, string? message = null, params object?[] args)
{ {
(string? envelopeUuid, string? receiverSignature) = receiverId.DecodeEnvelopeReceiverId(); var sb = new StringBuilder(envelopeEeceiverId.DecodeEnvelopeReceiverId().ToTitle());
var sb = new StringBuilder($"Envelope Uuid: {envelopeUuid}\nReceiver Signature: {receiverSignature}");
if (message is not null) if (message is not null)
sb.AppendLine().Append(message); sb.AppendLine().Append(message);
logger.Log(LogLevel.Error, sb.ToString(), args); if(exception is null)
logger.Log(LogLevel.Error, sb.ToString(), args);
else
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
} }
public static void LogEnvelopeError(this ILogger logger, string uuid, string? signature = null, string? message = null, params object?[] args) public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args)
{ {
var sb = new StringBuilder($"Envelope Uuid: {uuid}"); var sb = new StringBuilder($"Envelope Uuid: {uuid}");
@ -28,7 +29,10 @@ namespace EnvelopeGenerator.Application
if (message is not null) if (message is not null)
sb.AppendLine().Append(message); sb.AppendLine().Append(message);
logger.Log(LogLevel.Error, sb.ToString(), args); if (exception is null)
logger.Log(LogLevel.Error, sb.ToString(), args);
else
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
} }
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple) public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)

View File

@ -95,5 +95,16 @@ namespace EnvelopeGenerator.Application.Services
return await VerifyAccessCodeAsync(uuid: uuid, signature: signature, accessCode: accessCode); return await VerifyAccessCodeAsync(uuid: uuid, signature: signature, accessCode: accessCode);
} }
public async Task<IServiceResult<bool>> IsExisting(string envelopeReceiverId)
{
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
if (uuid is null || signature is null)
return Failed(false).WithFlag(EnvelopeFlag.NonDecodableEnvelopeReceiverId);
int count = await _repository.CountAsync(uuid:uuid, signature:signature);
return Successful(count > 0);
}
} }
} }

View File

@ -12,5 +12,7 @@ namespace EnvelopeGenerator.Infrastructure.Contracts
Task<EnvelopeReceiver?> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true); Task<EnvelopeReceiver?> ReadByUuidSignatureAsync(string uuid, string signature, bool withEnvelope = true, bool withReceiver = true);
Task<string?> ReadAccessCodeAsync(string uuid, string signature); Task<string?> ReadAccessCodeAsync(string uuid, string signature);
Task<int> CountAsync(string uuid, string signature);
} }
} }

View File

@ -24,11 +24,12 @@ namespace EnvelopeGenerator.Infrastructure.Repositories
query = query.Where(er => er.Receiver != null && er.Receiver.Signature == signature); query = query.Where(er => er.Receiver != null && er.Receiver.Signature == signature);
if (withEnvelope) if (withEnvelope)
query = query.Include(er => er.Envelope); query = query.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
.Include(er => er.Envelope).ThenInclude(e => e!.History)
.Include(er => er.Envelope).ThenInclude(e => e!.History);
if (withReceiver) if (withReceiver)
query = query.Include(er => er.Receiver); query = query.Include(er => er.Receiver);
return query; return query;
} }
@ -45,5 +46,7 @@ namespace EnvelopeGenerator.Infrastructure.Repositories
=> await ReadWhere(uuid:uuid, signature:signature) => await ReadWhere(uuid:uuid, signature:signature)
.Select(er => er.AccessCode) .Select(er => er.AccessCode)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
public async Task<int> CountAsync(string uuid, string signature) => await ReadWhere(uuid: uuid, signature: signature).CountAsync();
} }
} }

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc; using EnvelopeGenerator.Web.Models;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims; using System.Security.Claims;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers
@ -38,5 +39,28 @@ namespace EnvelopeGenerator.Web.Controllers
} }
return null; return null;
} }
public static ViewResult ViewError(this Controller controller, ErrorViewModel errorViewModel) => controller.View("_Error", errorViewModel);
public static ViewResult ViewError404(this Controller controller) => controller.ViewError(new ErrorViewModel()
{
Title = "404",
Subtitle = "Die von Ihnen gesuchte Seite ist nicht verfügbar",
Body = "Sie können derzeit nur an Sie gerichtete Briefe einsehen und unterschreiben.",
});
public static ViewResult ViewEnvelopeNotFound(this Controller controller) => controller.ViewError(new ErrorViewModel()
{
Title = "404",
Subtitle = "Umschlag nicht gefunden",
Body = "Wenn Sie diese URL in Ihrer E-Mail erhalten haben, wenden Sie sich bitte an das IT-Team."
});
public static ViewResult ViewInnerServiceError(this Controller controller) => controller.ViewError(new ErrorViewModel()
{
Title = "500",
Subtitle = "Ein unerwarteter Fehler ist aufgetreten",
Body = "Bitte kontaktieren Sie das IT-Team."
});
} }
} }

View File

@ -1,7 +1,6 @@
using EnvelopeGenerator.Application.Contracts; using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.Services; using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Common; using EnvelopeGenerator.Common;
using EnvelopeGenerator.Web.Models;
using EnvelopeGenerator.Web.Services; using EnvelopeGenerator.Web.Services;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
@ -10,6 +9,10 @@ using System.Security.Claims;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using DigitalData.Core.API; using DigitalData.Core.API;
using DigitalData.Core.Application; using DigitalData.Core.Application;
using EnvelopeGenerator.Application;
using DigitalData.Core.Contracts.CultureServices;
using DigitalData.Core.CultureServices;
using Azure;
namespace EnvelopeGenerator.Web.Controllers namespace EnvelopeGenerator.Web.Controllers
{ {
@ -19,12 +22,15 @@ namespace EnvelopeGenerator.Web.Controllers
private readonly IEnvelopeReceiverService _envRcvService; private readonly IEnvelopeReceiverService _envRcvService;
private readonly IEnvelopeService _envelopeService; private readonly IEnvelopeService _envelopeService;
private readonly IEnvelopeHistoryService _historyService; private readonly IEnvelopeHistoryService _historyService;
public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService) : base(databaseService, logger) private readonly IKeyTranslationService _translator;
public HomeController(DatabaseService databaseService, EnvelopeOldService envelopeOldService, ILogger<HomeController> logger, IEnvelopeReceiverService envelopeReceiverService, IEnvelopeService envelopeService, IEnvelopeHistoryService historyService, IKeyTranslationService keyTranslationService) : base(databaseService, logger)
{ {
this.envelopeOldService = envelopeOldService; this.envelopeOldService = envelopeOldService;
_envRcvService = envelopeReceiverService; _envRcvService = envelopeReceiverService;
_envelopeService = envelopeService; _envelopeService = envelopeService;
_historyService = historyService; _historyService = historyService;
_translator = keyTranslationService;
} }
[HttpGet("/EnvelopeKey/{envelopeReceiverId}")] [HttpGet("/EnvelopeKey/{envelopeReceiverId}")]
@ -33,15 +39,18 @@ namespace EnvelopeGenerator.Web.Controllers
ViewData["EnvelopeKey"] = envelopeReceiverId; ViewData["EnvelopeKey"] = envelopeReceiverId;
try try
{ {
(string envelopeUuid, string receiverSignature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); var erResult = await _envRcvService.ReadByEnvelopeReceiverIdAsync(envelopeReceiverId: envelopeReceiverId);
var envelopeResult = await _envelopeService.ReadByUuidAsync(envelopeUuid, signature: receiverSignature, withEnvelopeReceivers:true); var er = erResult.Data;
var envelope = envelopeResult.Data; var receiver = er?.Receiver;
var envelopeReceiver = envelope?.EnvelopeReceivers?.FirstOrDefault(); var envelope = er?.Envelope;
var mailAddress = envelopeReceiver?.Receiver?.EmailAddress; var mailAddress = receiver?.EmailAddress;
var useAccessCode = envelope?.UseAccessCode;
ViewData["EnvelopeResult"] = envelopeResult; if (erResult is null)
if(envelopeResult.IsSuccess && envelope is not null && mailAddress is not null && (envelope.UseAccessCode ?? false)) {
_logger.LogError(MessageKey.ServiceOutputNullError.TranslateWith(_translator));
return this.ViewEnvelopeNotFound();
}
else if (erResult.IsSuccess && mailAddress is not null && (envelope?.UseAccessCode ?? false))
{ {
EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId); EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId);
@ -56,32 +65,37 @@ namespace EnvelopeGenerator.Web.Controllers
} }
else else
{ {
envelopeResult.WithMessageKey(MessageKey.FailedToSendAccessCode); _logger.LogServiceMessage(erResult);
_logger.LogError($"{MessageKey.FailedToSendAccessCode.ToString()}"); return this.ViewEnvelopeNotFound();
} }
} }
catch(Exception ex) catch(Exception ex)
{ {
_logger.LogError(ex, MessageKey.UnexpectedError.ToString()); _logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception:ex, message: MessageKey.UnexpectedError.TranslateWith(_translator));
return this.ViewInnerServiceError();
} }
return Redirect($"{envelopeReceiverId}/Locked"); return Redirect($"{envelopeReceiverId}/Locked");
} }
[HttpGet("EnvelopeKey/{envelopeReceiverId}/Locked")] [HttpGet("EnvelopeKey/{envelopeReceiverId}/Locked")]
public IActionResult EnvelopeLocked([FromRoute] string envelopeReceiverId) public async Task<IActionResult> EnvelopeLocked([FromRoute] string envelopeReceiverId)
{ {
(string? uuid, string? signature) = envelopeReceiverId.DecodeEnvelopeReceiverId(); try
if (uuid is null || signature is null)
{ {
return View("_Error", new ErrorViewModel() var result = await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId);
{ bool isExisting = result.Data;
Title = "404",
Subtitle = "Anfrage fehlgeschlagen!", if (result.HasFlag(EnvelopeFlag.NonDecodableEnvelopeReceiverId) || !isExisting)
Body = "Das angeforderte Umschlag wurde nicht gefunden." return this.ViewEnvelopeNotFound();
});
return View().WithData("EnvelopeKey", envelopeReceiverId);
}
catch(Exception ex)
{
_logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
} }
return View().WithData("EnvelopeKey", envelopeReceiverId);
} }
[HttpPost("/EnvelopeKey/{envelopeReceiverId}/Locked")] [HttpPost("/EnvelopeKey/{envelopeReceiverId}/Locked")]
@ -93,7 +107,7 @@ namespace EnvelopeGenerator.Web.Controllers
if(uuid is null || signature is null) if(uuid is null || signature is null)
{ {
_logger.LogWarning($"{MessageKey.WrongEnvelopeReceiverId.ToString()}"); _logger.LogEnvelopeError(uuid: uuid, signature: signature, message: MessageKey.WrongEnvelopeReceiverId.TranslateWith(_translator));
return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString())); return BadRequest(_envelopeService.CreateMessage(false, MessageKey.WrongEnvelopeReceiverId.ToString()));
} }
@ -117,7 +131,7 @@ namespace EnvelopeGenerator.Web.Controllers
{ {
if (envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id)) if (envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id))
{ {
return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Success"); return View("EnvelopeSigned");
} }
var envelope = await _envelopeService.ReadByUuidAsync(uuid: uuid, signature: signature, withAll: true); var envelope = await _envelopeService.ReadByUuidAsync(uuid: uuid, signature: signature, withAll: true);
@ -171,9 +185,29 @@ namespace EnvelopeGenerator.Web.Controllers
[HttpGet("/EnvelopeKey/{envelopeReceiverId}/Success")] [HttpGet("/EnvelopeKey/{envelopeReceiverId}/Success")]
public async Task<IActionResult> EnvelopeSigned(string envelopeReceiverId) public async Task<IActionResult> EnvelopeSigned(string envelopeReceiverId)
{ {
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); try
ViewData["EnvelopeKey"] = envelopeReceiverId; {
return View(); var result = await _envRcvService.IsExisting(envelopeReceiverId: envelopeReceiverId);
bool isExisting = result.Data;
if (result.HasFlag(EnvelopeFlag.NonDecodableEnvelopeReceiverId) || !isExisting)
return this.ViewEnvelopeNotFound();
EnvelopeResponse response = await envelopeOldService.LoadEnvelope(envelopeReceiverId);
if (!envelopeOldService.ReceiverAlreadySigned(response.Envelope, response.Receiver.Id))
{
return Redirect($"/EnvelopeKey/{envelopeReceiverId}/Locked");
}
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
ViewData["EnvelopeKey"] = envelopeReceiverId;
return View();
}
catch (Exception ex)
{
_logger.LogEnvelopeError(envelopeEeceiverId: envelopeReceiverId, exception: ex);
return this.ViewInnerServiceError();
}
} }
[Authorize] [Authorize]
@ -185,11 +219,6 @@ namespace EnvelopeGenerator.Web.Controllers
return Ok(new { EnvelopeUuid = envelopeUuid, ReceiverSignature = receiverSignature }); return Ok(new { EnvelopeUuid = envelopeUuid, ReceiverSignature = receiverSignature });
} }
[HttpGet("Error")] public IActionResult Error404() => this.ViewError404();
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View();
}
} }
} }

View File

@ -94,6 +94,9 @@
<Content Update="wwwroot\cookies-policy.en.html"> <Content Update="wwwroot\cookies-policy.en.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Update="wwwroot\favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\privacy-policy.en.html"> <Content Update="wwwroot\privacy-policy.en.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View File

@ -2,9 +2,11 @@
{ {
public enum MessageKey public enum MessageKey
{ {
ServiceOutputNullError,
UnexpectedError, UnexpectedError,
FailedToSendAccessCode, FailedToSendAccessCode,
WrongEnvelopeReceiverId, //the value should be about URL (like URL is not existing) as a part of security. WrongEnvelopeReceiverId, //the value should be about URL (like URL is not existing) as a part of security.
DataIntegrityError DataIntegrityError,
NonDecodableEnvelopeReceiverId
} }
} }

View File

@ -160,7 +160,7 @@ try
app.UseAuthorization(); app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.MapFallbackToController("Error404", "Home");
app.Run(); app.Run();
} }
catch(Exception ex) catch(Exception ex)

View File

@ -6,11 +6,16 @@ body {
background: linear-gradient(90deg, rgba(47, 54, 64, 1) 23%, rgba(24, 27, 32, 1) 100%); background: linear-gradient(90deg, rgba(47, 54, 64, 1) 23%, rgba(24, 27, 32, 1) 100%);
} }
:root {
--moon-left-position: 0px;
}
.moon { .moon {
background: linear-gradient(90deg, rgba(208, 208, 208, 1) 48%, rgba(145, 145, 145, 1) 100%); background: linear-gradient(90deg, rgba(208, 208, 208, 1) 48%, rgba(145, 145, 145, 1) 100%);
position: absolute; position: absolute;
top: -100px; top: -100px;
left: -300px; left: var(--moon-left-position);
width: 900px; width: 900px;
height: 900px; height: 900px;
content: ''; content: '';
@ -28,14 +33,14 @@ body {
.moon__crater1 { .moon__crater1 {
top: 250px; top: 250px;
left: 500px; left: calc(var(--moon-left-position) + 800px);
width: 60px; width: 60px;
height: 180px; height: 180px;
} }
.moon__crater2 { .moon__crater2 {
top: 650px; top: 650px;
left: 340px; left: calc(var(--moon-left-position) + 640px);
width: 40px; width: 40px;
height: 80px; height: 80px;
transform: rotate(55deg); transform: rotate(55deg);
@ -43,7 +48,7 @@ body {
.moon__crater3 { .moon__crater3 {
top: -20px; top: -20px;
left: 40px; left: calc(var(--moon-left-position) + 340px);
width: 65px; width: 65px;
height: 120px; height: 120px;
transform: rotate(250deg); transform: rotate(250deg);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 264 KiB