Auth and error handling components

This commit is contained in:
2024-09-01 19:30:28 +03:00
parent 65876be551
commit 1f050a6fd4
9 changed files with 174 additions and 60 deletions

View File

@@ -1,4 +1,7 @@
<Router AppAssembly="@typeof(App).Assembly"> @using BlazorWebAssemblyVisaApiClient.ErrorHandling
<GlobalErrorHandler >
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData"> <Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/> <FocusOnNavigate RouteData="@routeData" Selector="h1"/>
@@ -10,3 +13,4 @@
</LayoutView> </LayoutView>
</NotFound> </NotFound>
</Router> </Router>
</GlobalErrorHandler>

View File

@@ -0,0 +1,4 @@
namespace BlazorWebAssemblyVisaApiClient.Common
{
public class BlazorClientException(string message) : Exception(message);
}

View File

@@ -0,0 +1,67 @@
@using System.Net
@using BlazorWebAssemblyVisaApiClient.Components.Auth.Exceptions
@using BlazorWebAssemblyVisaApiClient.ErrorHandling
@using VisaApiClient
@code {
public static bool LoggedIn;
private static AuthData savedData = null!;
[CascadingParameter] private GlobalErrorHandler ErrorHandler { get; set; } = null!;
[CascadingParameter] private Status? Status { get; set; }
[Inject] private Client Client { get; set; } = null!;
[Inject] private NavigationManager Nav { get; set; } = null!;
///Authorize with email and password
/// <returns>Message to user</returns>
public async Task TryAuthorize(AuthData authData)
{
Status?.SetMessage("Wait...");
try
{
var token = await Client.LoginAsync(authData.Email, authData.Password);
Client.SetAuthToken(token);
savedData = authData;
Status?.SetSucces("Logged in successfully.");
}
catch (ApiException<ProblemDetails> e)
{
if (e.Result.Status == (int)HttpStatusCode.Forbidden)
{
Status?.SetError(e.Result.Detail!);
}
else
{
Status?.SetError("Error occured");
ErrorHandler.Handle(e);
}
}
catch (Exception e)
{
Status?.SetError("Error occured");
ErrorHandler.Handle(e);
}
}
///Re-auth if token expired or something
public async Task ReAuthenticate(bool redirectOnFailure = true)
{
if (!LoggedIn)
{
if (redirectOnFailure)
{
Nav.NavigateTo("/");
return;
}
throw new NotLoggedInException();
}
await TryAuthorize(savedData);
}
}

View File

@@ -0,0 +1,7 @@
using BlazorWebAssemblyVisaApiClient.Common;
using VisaApiClient;
namespace BlazorWebAssemblyVisaApiClient.Components.Auth.Exceptions
{
public class NotLoggedInException() : BlazorClientException("User is not logged in");
}

View File

@@ -0,0 +1,9 @@
@using BlazorWebAssemblyVisaApiClient.ErrorHandling
@using VisaApiClient
@code
{
[CascadingParameter] protected GlobalErrorHandler ErrorHandler { get; set; } = null!;
[Inject] protected Client Client { get; set; } = null!;
}

View File

@@ -0,0 +1,34 @@
<p class="@statusClass">@((MarkupString)StatusText)</p>
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@code {
private string statusClass = string.Empty;
[Parameter]
public RenderFragment? ChildContent { get; set; }
public string StatusText { get; set; } = string.Empty;
public void SetMessage(string message)
{
statusClass = string.Empty;
StatusText = message;
StateHasChanged();
}
public void SetError(string message)
{
statusClass = "validation-message";
StatusText = message;
StateHasChanged();
}
public void SetSucces(string message)
{
statusClass = "text-success";
StatusText = message;
StateHasChanged();
}
}

View File

@@ -0,0 +1,14 @@
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@code
{
[Parameter]
public RenderFragment? ChildContent { get; set; }
public void Handle(Exception ex)
{
}
}

View File

@@ -1,6 +1,10 @@
@page "/" @page "/"
@using System.Net
@using BlazorWebAssemblyVisaApiClient.Components.Auth
@using VisaApiClient @using VisaApiClient
@using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants @using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants
@using BlazorWebAssemblyVisaApiClient.Components
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Authentication</PageTitle> <PageTitle>Authentication</PageTitle>
@@ -13,30 +17,17 @@
<input class="btn-outline-primary rounded" type="submit" value="Login"/> <input class="btn-outline-primary rounded" type="submit" value="Login"/>
or or
<NavLink href="register">Register</NavLink > <NavLink href="register">Register</NavLink >
<p>@loginResult</p> <Status><AuthComponent @ref="auth"/></Status>
</EditForm> </EditForm>
</div> </div>
@code @code
{ {
private AuthData loginData = new(); private AuthData loginData = new();
private string loginResult = string.Empty; private AuthComponent auth = null!;
[Inject] private Client Client { get; set; } = null!;
private async Task TryLogin() private async Task TryLogin()
{ {
loginResult = "Wait..."; await auth.TryAuthorize(loginData);
StateHasChanged();
try
{
var token = await Client.LoginAsync(loginData.Email, loginData.Password);
Client.SetAuthToken(token);
loginResult = "Logged in successfully.";
}
catch (ApiException<ProblemDetails> e)
{
loginResult = e.Result.Detail!;
}
} }
} }

View File

@@ -9,6 +9,8 @@
@using global::FluentValidation @using global::FluentValidation
@using Newtonsoft.Json @using Newtonsoft.Json
@using Newtonsoft.Json.Linq @using Newtonsoft.Json.Linq
@using BlazorWebAssemblyVisaApiClient.Components
@inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase
<PageTitle>Registration</PageTitle> <PageTitle>Registration</PageTitle>
@@ -122,17 +124,14 @@
</div><br/> </div><br/>
<input type="submit" class="btn-outline-primary" value="Register"/> <input type="submit" class="btn-outline-primary" value="Register"/>
<p class="@requestResultClass">@((MarkupString)requestResult)</p> <Status @ref="status"/>
</EditForm> </EditForm>
</div> </div>
@code @code
{ {
private RegisterApplicantRequestModel requestModel = new(); private RegisterApplicantRequestModel requestModel = new();
private string requestResult = string.Empty; private Status status = null!;
private string requestResultClass = string.Empty;
[Inject] public Client Client { get; set; } = null!;
[Inject] IValidator<RegisterApplicantRequestModel> RegisterApplicantRequestValidator { get; set; } = null!; [Inject] IValidator<RegisterApplicantRequestModel> RegisterApplicantRequestValidator { get; set; } = null!;
@@ -145,27 +144,6 @@
requestModel.BirthDate = DateTime.Now; requestModel.BirthDate = DateTime.Now;
} }
private void SetRequestResultMessage(string message)
{
requestResult = message;
requestResultClass = string.Empty;
StateHasChanged();
}
private void SetRequestResultSuccess(string message)
{
requestResult = message;
requestResultClass = "text-success";
StateHasChanged();
}
private void SetRequestResultError(string message)
{
requestResult = message;
requestResultClass = "validation-message";
StateHasChanged();
}
private string ErrorsToString(IEnumerable<string> errors) private string ErrorsToString(IEnumerable<string> errors)
{ {
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
@@ -183,18 +161,18 @@
if (!validationResult.IsValid) if (!validationResult.IsValid)
{ {
var errorsString = ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage)); var errorsString = ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage));
SetRequestResultError(errorsString); status.SetError(errorsString);
return; return;
} }
SetRequestResultMessage("Wait..."); status.SetMessage("Wait...");
var request = Mapper.Map<RegisterApplicantRequest>(requestModel); var request = Mapper.Map<RegisterApplicantRequest>(requestModel);
try try
{ {
await Client.RegisterAsync(request); await Client.RegisterAsync(request);
SetRequestResultSuccess("Register successful. Now log in."); status.SetSucces("Register successful. Now log in.");
} }
catch (ApiException<ProblemDetails> e) catch (ApiException<ProblemDetails> e)
{ {
@@ -204,15 +182,21 @@
var errorsList = ((JArray)errors).ToObject<List<string>>(); var errorsList = ((JArray)errors).ToObject<List<string>>();
if (errorsList is null) if (errorsList is null)
{ {
throw new JsonException(); ErrorHandler.Handle(new JsonException("Can't convert validation errors to list"));
return;
} }
SetRequestResultError(ErrorsToString(errorsList)); status.SetError(ErrorsToString(errorsList));
} }
else else
{ {
SetRequestResultError(e.Result.Detail!); ErrorHandler.Handle(e);
} }
}
catch (Exception e)
{
ErrorHandler.Handle(e);
} }
} }
} }