From 211d3fdc030d6a346cc1758773d19470f1a29847 Mon Sep 17 00:00:00 2001 From: YuukanOO Date: Tue, 29 Dec 2020 20:28:23 +0100 Subject: [PATCH] everything related to blazor! --- Apps/Api/Controllers/LinksController.cs | 2 +- Apps/Api/Startup.cs | 7 +- Apps/Client/App.razor | 22 +++--- Apps/Client/Client.csproj | 1 + Apps/Client/CustomAuthStateProvider.cs | 91 +++++++++++++++++++++++++ Apps/Client/NotificationManager.cs | 36 ++++++++++ Apps/Client/Pages/Container.razor | 11 ++- Apps/Client/Pages/Index.razor | 18 ++--- Apps/Client/Pages/Login.razor | 36 ++++++++++ Apps/Client/Pages/Show.razor | 46 ++++++++++++- Apps/Client/Program.cs | 5 ++ Apps/Client/Shared/Comment.razor | 7 ++ Apps/Client/Shared/CommentForm.razor | 35 ++++++++++ Apps/Client/Shared/EmptyLayout.razor | 5 ++ Apps/Client/Shared/Inside.razor | 7 ++ Apps/Client/Shared/MainLayout.razor | 25 ++++--- Apps/Client/Shared/MainLayout.razor.css | 70 ------------------- Apps/Client/Shared/Names.razor | 3 + Apps/Client/Shared/NavMenu.razor | 2 +- Apps/Client/Shared/NavMenu.razor.css | 62 ----------------- Apps/Client/Shared/Title.razor | 14 ++++ Apps/Client/Shared/Title.razor.css | 3 + Apps/Client/Shared/Toasts.razor | 20 ++++++ Apps/Client/_Imports.razor | 1 + Apps/Client/wwwroot/css/app.css | 59 ++++++---------- Apps/Client/wwwroot/index.html | 23 ++++--- README.md | 10 +++ 27 files changed, 404 insertions(+), 217 deletions(-) create mode 100644 Apps/Client/CustomAuthStateProvider.cs create mode 100644 Apps/Client/NotificationManager.cs create mode 100644 Apps/Client/Pages/Login.razor create mode 100644 Apps/Client/Shared/Comment.razor create mode 100644 Apps/Client/Shared/CommentForm.razor create mode 100644 Apps/Client/Shared/EmptyLayout.razor create mode 100644 Apps/Client/Shared/Inside.razor delete mode 100644 Apps/Client/Shared/MainLayout.razor.css delete mode 100644 Apps/Client/Shared/NavMenu.razor.css create mode 100644 Apps/Client/Shared/Title.razor create mode 100644 Apps/Client/Shared/Title.razor.css create mode 100644 Apps/Client/Shared/Toasts.razor diff --git a/Apps/Api/Controllers/LinksController.cs b/Apps/Api/Controllers/LinksController.cs index dd6a695..88e93c0 100644 --- a/Apps/Api/Controllers/LinksController.cs +++ b/Apps/Api/Controllers/LinksController.cs @@ -93,7 +93,7 @@ namespace Api.Controllers public async Task AddComment(Guid id, AddCommentViewModel command) { var commentId = await _bus.Send(new CommentLinkCommand(id, command.Content)); - return CreatedAtAction("", "", new { id = commentId }, null); + return Created($"comments/{commentId}", null); } /// diff --git a/Apps/Api/Startup.cs b/Apps/Api/Startup.cs index e8e8d66..7bc2a8c 100644 --- a/Apps/Api/Startup.cs +++ b/Apps/Api/Startup.cs @@ -114,7 +114,12 @@ namespace Api app.UseSwaggerUi3(); } - app.UseCors(o => o.AllowAnyOrigin()); + app.UseCors(o => + { + o.AllowAnyOrigin(); + o.AllowAnyMethod(); + o.WithHeaders("content-type", "authorization"); + }); app.UseRouting(); diff --git a/Apps/Client/App.razor b/Apps/Client/App.razor index 13d5e4f..692b43e 100644 --- a/Apps/Client/App.razor +++ b/Apps/Client/App.razor @@ -1,10 +1,12 @@ - - - - - - -

Sorry, there's nothing at this address.

-
-
-
+ + + + + + + +

Sorry, there's nothing at this address.

+
+
+
+
diff --git a/Apps/Client/Client.csproj b/Apps/Client/Client.csproj index d325646..b46f6f8 100644 --- a/Apps/Client/Client.csproj +++ b/Apps/Client/Client.csproj @@ -5,6 +5,7 @@ + diff --git a/Apps/Client/CustomAuthStateProvider.cs b/Apps/Client/CustomAuthStateProvider.cs new file mode 100644 index 0000000..9620f8f --- /dev/null +++ b/Apps/Client/CustomAuthStateProvider.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.Authorization; + +namespace Client +{ + public sealed class CustomAuthStateProvider : AuthenticationStateProvider + { + private static string _token; + + private readonly HttpClient _http; + + public CustomAuthStateProvider(HttpClient http) + { + _http = http; + } + + public override Task GetAuthenticationStateAsync() + { + return Task.FromResult(AuthenticationStateFromCurrentToken()); + } + + public void MarkUserAsAuthenticated(string token) + { + _token = token; + NotifyAuthenticationStateChanged(Task.FromResult(AuthenticationStateFromCurrentToken())); + } + + private AuthenticationState AuthenticationStateFromCurrentToken() + { + if (string.IsNullOrWhiteSpace(_token)) + { + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + } + + _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token); + + var principal = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(_token), "jwt", "unique_name", null)); + + return new AuthenticationState(principal); + } + + private IEnumerable ParseClaimsFromJwt(string jwt) + { + var claims = new List(); + var payload = jwt.Split('.')[1]; + var jsonBytes = ParseBase64WithoutPadding(payload); + var keyValuePairs = JsonSerializer.Deserialize>(jsonBytes); + + keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles); + + if (roles != null) + { + if (roles.ToString().Trim().StartsWith("[")) + { + var parsedRoles = JsonSerializer.Deserialize(roles.ToString()); + + foreach (var parsedRole in parsedRoles) + { + claims.Add(new Claim(ClaimTypes.Role, parsedRole)); + } + } + else + { + claims.Add(new Claim(ClaimTypes.Role, roles.ToString())); + } + + keyValuePairs.Remove(ClaimTypes.Role); + } + + claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()))); + return claims; + } + + private byte[] ParseBase64WithoutPadding(string base64) + { + switch (base64.Length % 4) + { + case 2: base64 += "=="; break; + case 3: base64 += "="; break; + } + return Convert.FromBase64String(base64); + } + } +} \ No newline at end of file diff --git a/Apps/Client/NotificationManager.cs b/Apps/Client/NotificationManager.cs new file mode 100644 index 0000000..f411c57 --- /dev/null +++ b/Apps/Client/NotificationManager.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Client +{ + public sealed class NotificationManager + { + private readonly ILogger _logger; + private Queue _messages = new Queue(); + + public IReadOnlyList Messages => _messages.ToArray(); + + public event Action OnChange; + + public NotificationManager(ILogger logger) + { + _logger = logger; + } + + public void Add(string message) + { + _messages.Enqueue(message); + + Task.Run(async () => + { + await Task.Delay(5000); + _messages.Dequeue(); + OnChange?.Invoke(); + }); + + OnChange?.Invoke(); + } + } +} \ No newline at end of file diff --git a/Apps/Client/Pages/Container.razor b/Apps/Client/Pages/Container.razor index 16c7768..9c0fa80 100644 --- a/Apps/Client/Pages/Container.razor +++ b/Apps/Client/Pages/Container.razor @@ -2,8 +2,10 @@ @inject MyState State @foreach (var name in State.Names) + { -

@name

+

@name

+ } @@ -12,11 +14,18 @@ @code { + private string CurrentValue; + + private void OnSend() + { + State.AddName(CurrentValue); + CurrentValue = ""; + } } \ No newline at end of file diff --git a/Apps/Client/Pages/Index.razor b/Apps/Client/Pages/Index.razor index bc5ee0d..7dd018a 100644 --- a/Apps/Client/Pages/Index.razor +++ b/Apps/Client/Pages/Index.razor @@ -1,9 +1,9 @@ @page "/" @inject LinksClient Links -

Latest links

+ -@if (_loading) +@if (_links == null) { <p>Loading...</p> } @@ -20,20 +20,10 @@ else } @code { - private LinkDto[] _links = new LinkDto[] { }; - private bool _loading = false; + private LinkDto[] _links; protected override async Task OnInitializedAsync() { - _loading = true; - - try - { - _links = (await Links.GetLinksAsync()).ToArray(); - } - finally - { - _loading = false; - } + _links = (await Links.GetLinksAsync()).ToArray(); } } \ No newline at end of file diff --git a/Apps/Client/Pages/Login.razor b/Apps/Client/Pages/Login.razor new file mode 100644 index 0000000..9c38d24 --- /dev/null +++ b/Apps/Client/Pages/Login.razor @@ -0,0 +1,36 @@ +@page "/login" +@inject AccountsClient Accounts +@inject NotificationManager Notifications +@inject NavigationManager Navigation +@inject AuthenticationStateProvider AuthStateProvider + +<Title Value="Sign in!" /> + +<EditForm Model="@_model" OnValidSubmit="TryLogin"> + <DataAnnotationsValidator /> + <ValidationSummary /> + + <InputText @bind-Value="_model.Username" /> + <InputText type="password" @bind-Value="_model.Password" /> + + <button type="submit">Log in!</button> +</EditForm> + +@code +{ + private LoginViewModel _model = new LoginViewModel(); + + private async Task TryLogin() + { + try + { + var token = await Accounts.LoginAsync(_model); + ((CustomAuthStateProvider)AuthStateProvider).MarkUserAsAuthenticated(token); + Navigation.NavigateTo("/"); + } + catch + { + Notifications.Add("login failed!"); + } + } +} \ No newline at end of file diff --git a/Apps/Client/Pages/Show.razor b/Apps/Client/Pages/Show.razor index 73701ac..0579718 100644 --- a/Apps/Client/Pages/Show.razor +++ b/Apps/Client/Pages/Show.razor @@ -1,5 +1,6 @@ @page "/links/{id:guid}" @inject LinksClient Links +@inject NotificationManager Notifications @if (Item == null) { @@ -7,7 +8,29 @@ } else { - <h1>Showing link @Item.Url with @Item.UpVotes / @Item.DownVotes</h1> + <h1>@Item.Url</h1> + <div> + 👍 @Item.UpVotes + 👎 @Item.DownVotes + </div> + + @if (Comments == null) + { + <p>Loading comments ...</p> + } else if(Comments.Count == 0) + { + <p>No comment yet!</p> + } else + { + foreach (var comment in Comments) + { + <Comment @key="comment.Id" Item="@comment" /> + } + } + + <AuthorizeView> + <CommentForm OnSubmit="@SubmitComment" Username="@context.User.Identity.Name" /> + </AuthorizeView> } @code { @@ -16,9 +39,30 @@ else private LinkDto Item; + private ICollection<CommentDto> Comments; + protected override async Task OnInitializedAsync() { Item = await Links.GetLinkByIdAsync(Id); + await FetchComments(); } + protected async Task FetchComments() + { + Comments = await Links.CommentsAsync(Id); + } + + private async Task SubmitComment(AddCommentViewModel command) + { + try + { + await Links.AddCommentAsync(Id, command); + await FetchComments(); + Notifications.Add("comment added!"); + } + catch + { + Notifications.Add("could not post a comment"); + } + } } \ No newline at end of file diff --git a/Apps/Client/Program.cs b/Apps/Client/Program.cs index be64455..c074ffd 100644 --- a/Apps/Client/Program.cs +++ b/Apps/Client/Program.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Components.Authorization; namespace Client { @@ -27,9 +28,13 @@ namespace Client var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); + builder.Services.AddAuthorizationCore(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://localhost:8888/") }); builder.Services.AddScoped<LinksClient>(); + builder.Services.AddScoped<AccountsClient>(); builder.Services.AddSingleton<MyState>(); + builder.Services.AddSingleton<NotificationManager>(); + builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>(); await builder.Build().RunAsync(); } diff --git a/Apps/Client/Shared/Comment.razor b/Apps/Client/Shared/Comment.razor new file mode 100644 index 0000000..54805cc --- /dev/null +++ b/Apps/Client/Shared/Comment.razor @@ -0,0 +1,7 @@ +<p>@Item.Content</p> +<p>- @Item.CreatedByName</p> + +@code { + [Parameter] + public CommentDto Item { get; set; } +} \ No newline at end of file diff --git a/Apps/Client/Shared/CommentForm.razor b/Apps/Client/Shared/CommentForm.razor new file mode 100644 index 0000000..8d539a2 --- /dev/null +++ b/Apps/Client/Shared/CommentForm.razor @@ -0,0 +1,35 @@ +<EditForm Model="@_model" OnValidSubmit="@OnValidSubmit"> + <DataAnnotationsValidator /> + + <InputText @bind-Value="_model.Content" /> + <ValidationMessage For=@(() => _model.Content) /> + + <button type="submit">Comment as @_username</button> +</EditForm> + +@code +{ + private AddCommentViewModel _model = new AddCommentViewModel(); + + [Parameter] + public string Username { get; set; } + + [CascadingParameter] + private Task<AuthenticationState> authenticationStateTask { get; set; } + + private string _username; + + [Parameter] + public EventCallback<AddCommentViewModel> OnSubmit { get; set; } + + protected async override Task OnInitializedAsync() + { + _username = (await authenticationStateTask).User.Identity.Name; + } + + private async Task OnValidSubmit() + { + await OnSubmit.InvokeAsync(_model); + _model = new AddCommentViewModel(); + } +} \ No newline at end of file diff --git a/Apps/Client/Shared/EmptyLayout.razor b/Apps/Client/Shared/EmptyLayout.razor new file mode 100644 index 0000000..58fe978 --- /dev/null +++ b/Apps/Client/Shared/EmptyLayout.razor @@ -0,0 +1,5 @@ +@inherits LayoutComponentBase + +<main> + @Body +</main> \ No newline at end of file diff --git a/Apps/Client/Shared/Inside.razor b/Apps/Client/Shared/Inside.razor new file mode 100644 index 0000000..6b657e6 --- /dev/null +++ b/Apps/Client/Shared/Inside.razor @@ -0,0 +1,7 @@ +<p>Title was @MyValue</p> + +@code +{ + [CascadingParameter] + private string MyValue { get; set; } +} \ No newline at end of file diff --git a/Apps/Client/Shared/MainLayout.razor b/Apps/Client/Shared/MainLayout.razor index 83d5006..6a10e2f 100644 --- a/Apps/Client/Shared/MainLayout.razor +++ b/Apps/Client/Shared/MainLayout.razor @@ -1,17 +1,22 @@ @inherits LayoutComponentBase <div class="page"> - <div class="sidebar"> - <NavMenu /> + <div class="topbar"> + <NavLink href="/">Home</NavLink> + + <AuthorizeView> + <Authorized> + Sign in as @context.User.Identity.Name + </Authorized> + <NotAuthorized> + You're not signed in <NavLink href="/login">Sign in</NavLink>. + </NotAuthorized> + </AuthorizeView> </div> - <div class="main"> - <div class="top-row px-4"> - <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a> - </div> + <main> + @Body + </main> - <div class="content px-4"> - @Body - </div> - </div> + <Toasts /> </div> diff --git a/Apps/Client/Shared/MainLayout.razor.css b/Apps/Client/Shared/MainLayout.razor.css deleted file mode 100644 index 5c663e0..0000000 --- a/Apps/Client/Shared/MainLayout.razor.css +++ /dev/null @@ -1,70 +0,0 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -.main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - } - - .top-row a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row:not(.auth) { - display: none; - } - - .top-row.auth { - justify-content: space-between; - } - - .top-row a, .top-row .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .main > div { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} diff --git a/Apps/Client/Shared/Names.razor b/Apps/Client/Shared/Names.razor index f1e1e71..3aa83b8 100644 --- a/Apps/Client/Shared/Names.razor +++ b/Apps/Client/Shared/Names.razor @@ -1,4 +1,6 @@ @inject MyState State +@implements IDisposable +@inject IJSRuntime JS; <p>Got @State.Names.Count</p> @@ -10,6 +12,7 @@ public void Dispose() { + JS.InvokeVoidAsync("alert", "Disposed"); State.OnChange -= StateHasChanged; } } \ No newline at end of file diff --git a/Apps/Client/Shared/NavMenu.razor b/Apps/Client/Shared/NavMenu.razor index f6d9400..6f7c538 100644 --- a/Apps/Client/Shared/NavMenu.razor +++ b/Apps/Client/Shared/NavMenu.razor @@ -5,7 +5,7 @@ </button> </div> -<div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> +<div @onclick="ToggleNavMenu"> <ul class="nav flex-column"> <li class="nav-item px-3"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All"> diff --git a/Apps/Client/Shared/NavMenu.razor.css b/Apps/Client/Shared/NavMenu.razor.css deleted file mode 100644 index e681f23..0000000 --- a/Apps/Client/Shared/NavMenu.razor.css +++ /dev/null @@ -1,62 +0,0 @@ -.navbar-toggler { - background-color: rgba(255, 255, 255, 0.1); -} - -.top-row { - height: 3.5rem; - background-color: rgba(0,0,0,0.4); -} - -.navbar-brand { - font-size: 1.1rem; -} - -.oi { - width: 2rem; - font-size: 1.1rem; - vertical-align: text-top; - top: -2px; -} - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item ::deep a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } - -.nav-item ::deep a.active { - background-color: rgba(255,255,255,0.25); - color: white; -} - -.nav-item ::deep a:hover { - background-color: rgba(255,255,255,0.1); - color: white; -} - -@media (min-width: 641px) { - .navbar-toggler { - display: none; - } - - .collapse { - /* Never collapse the sidebar for wide screens */ - display: block; - } -} diff --git a/Apps/Client/Shared/Title.razor b/Apps/Client/Shared/Title.razor new file mode 100644 index 0000000..c7cb4ac --- /dev/null +++ b/Apps/Client/Shared/Title.razor @@ -0,0 +1,14 @@ +@inject IJSRuntime JS + +<h1>@Value</h1> + +@code +{ + [Parameter] + public string Value { get; set; } + + protected override void OnInitialized() + { + JS.InvokeVoidAsync("setTitle", Value); + } +} \ No newline at end of file diff --git a/Apps/Client/Shared/Title.razor.css b/Apps/Client/Shared/Title.razor.css new file mode 100644 index 0000000..adc68fa --- /dev/null +++ b/Apps/Client/Shared/Title.razor.css @@ -0,0 +1,3 @@ +h1 { + color: red; +} diff --git a/Apps/Client/Shared/Toasts.razor b/Apps/Client/Shared/Toasts.razor new file mode 100644 index 0000000..60e3200 --- /dev/null +++ b/Apps/Client/Shared/Toasts.razor @@ -0,0 +1,20 @@ +@inject NotificationManager Notifications +@implements IDisposable + +@foreach (var message in Notifications.Messages) +{ + <p>⚡ @message</p> +} + +@code +{ + protected override void OnInitialized() + { + Notifications.OnChange += StateHasChanged; + } + + public void Dispose() + { + Notifications.OnChange -= StateHasChanged; + } +} \ No newline at end of file diff --git a/Apps/Client/_Imports.razor b/Apps/Client/_Imports.razor index 795b9d5..78999ae 100644 --- a/Apps/Client/_Imports.razor +++ b/Apps/Client/_Imports.razor @@ -5,6 +5,7 @@ @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.JSInterop @using Client @using Client.Shared diff --git a/Apps/Client/wwwroot/css/app.css b/Apps/Client/wwwroot/css/app.css index 06ab3db..eba8e32 100644 --- a/Apps/Client/wwwroot/css/app.css +++ b/Apps/Client/wwwroot/css/app.css @@ -1,50 +1,35 @@ -@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); - -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +html, +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } -a, .btn-link { - color: #0366d6; -} - -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} - -.content { - padding-top: 1.1rem; -} - -.valid.modified:not([type=checkbox]) { - outline: 1px solid #26b050; +.valid.modified:not([type="checkbox"]) { + outline: 1px solid #26b050; } .invalid { - outline: 1px solid red; + outline: 1px solid red; } .validation-message { - color: red; + color: red; } #blazor-error-ui { - background: lightyellow; - bottom: 0; - box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} diff --git a/Apps/Client/wwwroot/index.html b/Apps/Client/wwwroot/index.html index 1132395..57509cc 100644 --- a/Apps/Client/wwwroot/index.html +++ b/Apps/Client/wwwroot/index.html @@ -1,25 +1,30 @@ <!DOCTYPE html> <html> - -<head> + <head> <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> + <meta + name="viewport" + content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" + /> <title>Client - + - +
Loading...
- An unhandled error has occurred. - Reload - 🗙 + An unhandled error has occurred. + Reload + 🗙
- + + diff --git a/README.md b/README.md index 84cb22b..5dfd0d6 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,16 @@ project.csproj $(NoWarn);1591 +## Blazor + +Ajout de l'authentification : + +Ajout du paquet `Microsoft.AspNetCore.Components.Authorization` et du using dans `_Imports.razor`. + +Ensuite, Passage par `AuthorizeRouteView` dans le fichier `App.razor`. + +Ensuite, ajout d'un `AuthenticationStateProvider` custom pour déterminer si l'utilisateur est connecté ou non. + ## Docker On build à la racine de la solution avec `docker build -f .\Apps\Website\Dockerfile -t hn .`.