Auth and error handling components
This commit is contained in:
		| @@ -1,12 +1,16 @@ | ||||
| <Router AppAssembly="@typeof(App).Assembly"> | ||||
|     <Found Context="routeData"> | ||||
|         <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/> | ||||
|         <FocusOnNavigate RouteData="@routeData" Selector="h1"/> | ||||
|     </Found> | ||||
|     <NotFound> | ||||
|         <PageTitle>Not found</PageTitle> | ||||
|         <LayoutView Layout="@typeof(MainLayout)"> | ||||
|             <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|         </LayoutView> | ||||
|     </NotFound> | ||||
| </Router> | ||||
| @using BlazorWebAssemblyVisaApiClient.ErrorHandling | ||||
|  | ||||
| <GlobalErrorHandler > | ||||
|     <Router AppAssembly="@typeof(App).Assembly"> | ||||
|         <Found Context="routeData"> | ||||
|             <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/> | ||||
|             <FocusOnNavigate RouteData="@routeData" Selector="h1"/> | ||||
|         </Found> | ||||
|         <NotFound> | ||||
|             <PageTitle>Not found</PageTitle> | ||||
|             <LayoutView Layout="@typeof(MainLayout)"> | ||||
|                 <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|             </LayoutView> | ||||
|         </NotFound> | ||||
|     </Router> | ||||
| </GlobalErrorHandler> | ||||
|   | ||||
| @@ -0,0 +1,4 @@ | ||||
| namespace BlazorWebAssemblyVisaApiClient.Common | ||||
| { | ||||
|     public class BlazorClientException(string message) : Exception(message); | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| using BlazorWebAssemblyVisaApiClient.Common; | ||||
| using VisaApiClient; | ||||
|  | ||||
| namespace BlazorWebAssemblyVisaApiClient.Components.Auth.Exceptions | ||||
| { | ||||
|     public class NotLoggedInException() : BlazorClientException("User is not logged in"); | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| @using BlazorWebAssemblyVisaApiClient.ErrorHandling | ||||
| @using VisaApiClient | ||||
|  | ||||
| @code | ||||
| { | ||||
|     [CascadingParameter] protected GlobalErrorHandler ErrorHandler { get; set; } = null!; | ||||
|  | ||||
|     [Inject] protected Client Client { get; set; } = null!; | ||||
| } | ||||
| @@ -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(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| <CascadingValue Value="this"> | ||||
|     @ChildContent | ||||
| </CascadingValue> | ||||
|  | ||||
| @code | ||||
| { | ||||
|     [Parameter] | ||||
|     public RenderFragment? ChildContent { get; set; } | ||||
|  | ||||
|     public void Handle(Exception ex) | ||||
|     { | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,10 @@ | ||||
| @page "/" | ||||
| @using System.Net | ||||
| @using BlazorWebAssemblyVisaApiClient.Components.Auth | ||||
| @using VisaApiClient | ||||
| @using BlazorWebAssemblyVisaApiClient.Components.FormComponents.Applicants | ||||
| @using BlazorWebAssemblyVisaApiClient.Components | ||||
| @inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase | ||||
|  | ||||
| <PageTitle>Authentication</PageTitle> | ||||
|  | ||||
| @@ -13,30 +17,17 @@ | ||||
|         <input class="btn-outline-primary rounded" type="submit" value="Login"/> | ||||
|         or | ||||
|         <NavLink href="register">Register</NavLink > | ||||
|         <p>@loginResult</p> | ||||
|         <Status><AuthComponent @ref="auth"/></Status> | ||||
|     </EditForm> | ||||
| </div> | ||||
|  | ||||
| @code | ||||
| { | ||||
|     private AuthData loginData = new(); | ||||
|     private string loginResult = string.Empty; | ||||
|  | ||||
|     [Inject] private Client Client { get; set; } = null!; | ||||
|     private AuthComponent auth = null!; | ||||
|  | ||||
|     private async Task TryLogin() | ||||
|     { | ||||
|         loginResult = "Wait..."; | ||||
|         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!; | ||||
|         } | ||||
|         await auth.TryAuthorize(loginData); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,8 @@ | ||||
| @using global::FluentValidation | ||||
| @using Newtonsoft.Json | ||||
| @using Newtonsoft.Json.Linq | ||||
| @using BlazorWebAssemblyVisaApiClient.Components | ||||
| @inherits BlazorWebAssemblyVisaApiClient.Components.Base.VisaClientComponentBase | ||||
|  | ||||
| <PageTitle>Registration</PageTitle> | ||||
|  | ||||
| @@ -122,17 +124,14 @@ | ||||
|         </div><br/> | ||||
|  | ||||
|         <input type="submit" class="btn-outline-primary" value="Register"/> | ||||
|         <p class="@requestResultClass">@((MarkupString)requestResult)</p> | ||||
|         <Status @ref="status"/> | ||||
|     </EditForm> | ||||
| </div> | ||||
|  | ||||
| @code | ||||
| { | ||||
|     private RegisterApplicantRequestModel requestModel = new(); | ||||
|     private string requestResult = string.Empty; | ||||
|     private string requestResultClass = string.Empty; | ||||
|  | ||||
|     [Inject] public Client Client { get; set; } = null!; | ||||
|     private Status status = null!; | ||||
|  | ||||
|     [Inject] IValidator<RegisterApplicantRequestModel> RegisterApplicantRequestValidator { get; set; } = null!; | ||||
|  | ||||
| @@ -145,27 +144,6 @@ | ||||
|         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) | ||||
|     { | ||||
|         var stringBuilder = new StringBuilder(); | ||||
| @@ -183,18 +161,18 @@ | ||||
|         if (!validationResult.IsValid) | ||||
|         { | ||||
|             var errorsString = ErrorsToString(validationResult.Errors.Select(e => e.ErrorMessage)); | ||||
|             SetRequestResultError(errorsString); | ||||
|             status.SetError(errorsString); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         SetRequestResultMessage("Wait..."); | ||||
|         status.SetMessage("Wait..."); | ||||
|  | ||||
|         var request = Mapper.Map<RegisterApplicantRequest>(requestModel); | ||||
|         try | ||||
|         { | ||||
|             await Client.RegisterAsync(request); | ||||
|             SetRequestResultSuccess("Register successful. Now log in."); | ||||
|             status.SetSucces("Register successful. Now log in."); | ||||
|         } | ||||
|         catch (ApiException<ProblemDetails> e) | ||||
|         { | ||||
| @@ -204,15 +182,21 @@ | ||||
|                 var errorsList = ((JArray)errors).ToObject<List<string>>(); | ||||
|                 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 | ||||
|             { | ||||
|                 SetRequestResultError(e.Result.Detail!); | ||||
|                 ErrorHandler.Handle(e); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|             ErrorHandler.Handle(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user