-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
816c20c
commit 44fa48d
Showing
11 changed files
with
565 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy | ||
# More GitHub Actions for Azure: https://github.com/Azure/actions | ||
|
||
name: Build and deploy ASP.Net Core app to Azure Web App - louishowe | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
- feature/* | ||
workflow_dispatch: | ||
|
||
jobs: | ||
build: | ||
runs-on: windows-latest | ||
timeout-minutes: 30 | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up .NET Core | ||
uses: actions/setup-dotnet@v1 | ||
with: | ||
dotnet-version: '8.x' | ||
include-prerelease: true | ||
|
||
- name: Build | ||
run: dotnet build .\TradeWindsBlazor\TradeWindsBlazor.csproj --configuration Release | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Trade Winds Studios (David Thielen) | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<img align="left" width="200" src="server_client.png"/> | ||
|
||
# TradeWindsBlazor | ||
|
||
These are some classes I created for my Blazor application. | ||
|
||
I will add to this when I identify additional components in my apps that might be of general use. | ||
|
||
> This is under the MIT license. If you find this useful I ask (not a requirement) that you consider reading my book [I DON’T KNOW WHAT I’M DOING!: How a Programmer Became a Successful Startup CEO](https://a.co/d/bEpDlJR). | ||
> | ||
> And if you like it, please review it on Amazon and/or GoodReads. The number of legitimate reviews helps a lot. Much appreciated. | ||
## ExComponentBase | ||
|
||
ExComponentBase.cs is included as an example, not to be used directly. That's why it is `internal` instead of `public`. I do recommend you copy this over to your application, extend it, and use it instead of `ComponentBase` for your components. | ||
|
||
To use the ScoppedLoggerEx, you must have the following in your component: | ||
|
||
```csharp | ||
[Inject] | ||
private ScopedLoggerFactoryEx ScopedLoggerFactoryEx { get; set; } = default!; | ||
|
||
protected ScopedLoggerEx LoggerEx { get; set; } = default!; | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
await base.OnInitializedAsync(); | ||
|
||
LoggerEx = await ScopedLoggerFactoryEx.GetLogger(GetType()); | ||
} | ||
``` | ||
|
||
## ExPageBase | ||
|
||
I also recommend you create the class ExPageBase, that is a subclass of ExComponentBase. Use this as your base class for any pages. In this class, add the following: | ||
|
||
```csharp | ||
protected override async Task OnInitializedAsync() | ||
{ | ||
|
||
await base.OnInitializedAsync(); | ||
|
||
if (LoggerEx.IsEnabled(LogLevel.Trace)) | ||
LoggerEx.LogTrace($"Entering {GetType().Name}.OnInitializedAsync(), IsPreRender={IsPreRender}"); | ||
} | ||
``` | ||
|
||
## ILogger | ||
|
||
After you have added the above to your `ComponentBase`, you now have the member variable `LoggerEx` that is an `ILogger` for your component. And it is scoped to the component adding username and aspNetId as scopes for all logging by the logger. | ||
|
||
## Inject everything | ||
|
||
I strongly recommend that you add an `[Inject]` for every service you use in any component to your `ExComponentBase`. And the same for every `[CascadingParameter]`. Saves you a lot of copy/paste to each new component. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.12.35209.166 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TradeWindsBlazor", "TradeWindsBlazor\TradeWindsBlazor.csproj", "{C93C0E3F-955E-4873-8694-BF1ADBC9669B}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{C93C0E3F-955E-4873-8694-BF1ADBC9669B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{C93C0E3F-955E-4873-8694-BF1ADBC9669B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{C93C0E3F-955E-4873-8694-BF1ADBC9669B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{C93C0E3F-955E-4873-8694-BF1ADBC9669B}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {193FE3C6-D807-4FDD-AD44-3B999395A0B7} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
| ||
// Copyright (c) 2024 Trade Winds Studios (David Thielen) | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
|
||
using System.Security.Claims; | ||
using Microsoft.AspNetCore.Components; | ||
using Microsoft.AspNetCore.Components.Authorization; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using TradeWindsBlazor.Extensions; | ||
using TradeWindsBlazor.Loggers; | ||
|
||
namespace TradeWindsBlazor | ||
{ | ||
/// <summary> | ||
/// The base class for any component other than a simple component. | ||
/// </summary> | ||
internal class ExComponentBase : ComponentBase | ||
{ | ||
[Inject] | ||
private ScopedLoggerFactoryEx ScopedLoggerFactoryEx { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// This logger is for use in the methods in this base class and not for the subclass. | ||
/// </summary> | ||
[Inject] | ||
private ILogger<ExComponentBase> Logger { get; set; } = default!; | ||
|
||
[CascadingParameter] | ||
protected Task<AuthenticationState> AuthenticationStateTask { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// THe HttpContext for the current session (circuit). Only valid in OnInitializedAsync, | ||
/// </summary> | ||
[CascadingParameter] | ||
private HttpContext? HttpContext { get; set; } | ||
|
||
/// <summary> | ||
/// Built from AuthenticationStateTask - the logged in Principal (user). | ||
/// </summary> | ||
protected ClaimsPrincipal Principal { get; set; } = ClaimsPrincipalExtensions.Anonymous; | ||
|
||
/// <summary> | ||
/// The logger for the subclass. This is for use in the subclass and not in this base class. | ||
/// </summary> | ||
protected ScopedLoggerEx LoggerEx { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// true if this is the pre-render call to OnInitializedAsync. Do <b>not</b> call this anywhere other | ||
/// than inside OnInitializedAsync! | ||
/// </summary> | ||
protected bool IsPreRender => HttpContext is not null; | ||
|
||
/// <summary> | ||
/// Returns the User-Agent from the request. This is only valid in OnInitializedAsync in the PreRender pass. Will return | ||
/// null for other calls. | ||
/// </summary> | ||
protected string? RequestUserAgent => HttpContext?.Request.Headers["User-Agent"]; | ||
|
||
/// <inheritdoc /> | ||
protected override async Task OnInitializedAsync() | ||
{ | ||
await base.OnInitializedAsync(); | ||
|
||
// get the Principal | ||
Principal = (await AuthenticationStateTask).User; | ||
|
||
// set up the logger for this component. Passes GetType() so it is a logger for | ||
// this object, not for the base class. | ||
LoggerEx = await ScopedLoggerFactoryEx.GetLogger(GetType()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
| ||
// Copyright (c) 2024 Trade Winds Studios (David Thielen) | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
|
||
using System.Security.Claims; | ||
|
||
namespace TradeWindsBlazor.Extensions | ||
{ | ||
public static class ClaimsPrincipalExtensions | ||
{ | ||
/// <summary> | ||
/// The name used for the anonymous user. | ||
/// </summary> | ||
public static string AnonymousName { get; } = "Anonymous"; | ||
|
||
/// <summary> | ||
/// Returns the IdentityUser.Id of the passed in principal. | ||
/// </summary> | ||
/// <param name="principal">Normally the logged in user.</param> | ||
/// <returns>The IdentityUser.Id of the passed in principal. null if the user is not logged in (anonymous).</returns> | ||
public static string? UserId (this ClaimsPrincipal principal) | ||
{ | ||
var claim = principal.FindFirst(u => u.Type.Contains("nameidentifier")); | ||
return claim?.Value; | ||
} | ||
|
||
public static List<Claim> AppClaims(this ClaimsPrincipal principal) | ||
{ | ||
var listClaims = new List<Claim>(); | ||
foreach (var claim in principal.Claims) | ||
{ | ||
if (claim.Type.StartsWith("http") || claim.Type.StartsWith("AspNet")) | ||
continue; | ||
listClaims.Add(claim); | ||
} | ||
|
||
return listClaims; | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if this Principal is an anonymous user. | ||
/// </summary> | ||
/// <param name="principal">Normally the logged in user.</param> | ||
/// <returns>true if this Principal is an anonymous user.</returns> | ||
public static bool IsAnonymous(this ClaimsPrincipal principal) | ||
{ | ||
return principal.Identity == null || principal.Identity.IsAuthenticated == false; | ||
} | ||
|
||
/// <summary> | ||
/// An anonymous user. | ||
/// </summary> | ||
public static ClaimsPrincipal Anonymous => new(new ClaimsIdentity()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
| ||
// Copyright (c) 2024 Trade Winds Studios (David Thielen) | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
|
||
using Microsoft.Extensions.Logging; | ||
|
||
namespace TradeWindsBlazor.Loggers | ||
{ | ||
|
||
/// <summary> | ||
/// An ILogger where each Log() call will be in a scope with the user information. | ||
/// </summary> | ||
/// <typeparam name="T">The class the logger is going to log in.</typeparam> | ||
public class ScopedLoggerEx : ILogger | ||
{ | ||
|
||
private readonly ILogger _logger; | ||
private readonly string? _aspNetId; | ||
private readonly string? _username; | ||
|
||
public ScopedLoggerEx(ILogger logger, string? aspNetId, string? username) | ||
{ | ||
_logger = logger; | ||
_aspNetId = aspNetId; | ||
_username = username; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, | ||
Func<TState, Exception?, string> formatter) | ||
{ | ||
using (_logger.BeginScope("User:{username}, {aspNetId}", _username, _aspNetId)) | ||
{ | ||
_logger.Log(logLevel, eventId, state, exception, formatter); | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull | ||
{ | ||
return _logger.BeginScope(state); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public bool IsEnabled(LogLevel logLevel) | ||
{ | ||
return _logger.IsEnabled(logLevel); | ||
} | ||
} | ||
} |
Oops, something went wrong.