From 821bb180c572969a3dcbab173765e011ac1b94cc Mon Sep 17 00:00:00 2001 From: Benjamin Abt Date: Wed, 13 Nov 2024 14:05:47 +0100 Subject: [PATCH 1/3] add updates --- .github/workflows/{ci.yml => build.yml} | 43 ++++++++++++------ ...tchanet.sln => BenjaminAbt.HCaptchaNET.sln | 3 +- BenjaminAbt.HCaptchaNET.snk | Bin 0 -> 596 bytes Directory.Build.props | 34 ++++++++++++-- Directory.Packages.props | 12 +++++ global.json | 2 +- public.snk | Bin 0 -> 160 bytes .../sample.aspnetcore.csproj | 1 - .../HCaptcha.AspNetCore.csproj | 9 +--- src/HCaptcha/HCaptcha.csproj | 13 ++---- version.json | 2 +- 11 files changed, 80 insertions(+), 39 deletions(-) rename .github/workflows/{ci.yml => build.yml} (52%) rename hcaptchanet.sln => BenjaminAbt.HCaptchaNET.sln (96%) create mode 100644 BenjaminAbt.HCaptchaNET.snk create mode 100644 Directory.Packages.props create mode 100644 public.snk diff --git a/.github/workflows/ci.yml b/.github/workflows/build.yml similarity index 52% rename from .github/workflows/ci.yml rename to .github/workflows/build.yml index c2fd5ba..eeeaec5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/build.yml @@ -1,18 +1,17 @@ -name: NETCore +name: NET on: push: branches: - main pull_request: - types: [labeled] branches: - main + env: BuildConfig: Release DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_VERSION: '6.0.101' # https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/6.0/releases.json jobs: build: @@ -20,42 +19,60 @@ jobs: steps: - name: Cancel previous builds in PR - uses: styfle/cancel-workflow-action@0.9.1 + uses: styfle/cancel-workflow-action@0.12.1 with: access_token: ${{ github.token }} - - name: Checkout - uses: actions/checkout@v2 + - name: Checkout code + uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: 'Install .NET SDK' - uses: actions/setup-dotnet@v1 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 with: - dotnet-version: ${{ env.DOTNET_VERSION }} + global-json-file: ./global.json + + - name: Restore dependencies + run: dotnet restore - - name: Versioning + - name: Setup Git Versioning uses: dotnet/nbgv@master id: nbgv - - name: Version Info + - name: Show Version Info run: | echo 'SemVer2: ${{ steps.nbgv.outputs.SemVer2 }}' + - name: Build with dotnet run: dotnet build - --configuration ${{ env.BuildConfig }} + --no-restore --configuration ${{ env.BuildConfig }} /p:Version=${{ steps.nbgv.outputs.AssemblyVersion }} - name: Test with dotnet run: dotnet test + --no-build --configuration ${{ env.BuildConfig }} + --logger "trx;LogFileName=test-results.trx" --results-directory ./artifacts/testResults + continue-on-error: true + + - name: Test Report + uses: dorny/test-reporter@v1 + if: always() + with: + name: DotNET Tests + path: "./artifacts/testResults/test-results.trx" + reporter: dotnet-trx + fail-on-error: true - name: Pack NuGet run: dotnet pack --configuration ${{ env.BuildConfig }} + /p:ContinuousIntegrationBuild=true /p:Version=${{ steps.nbgv.outputs.NuGetPackageVersion }} - name: Push to NuGet run: dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_DEPLOY_KEY }} --source https://api.nuget.org/v3/index.json - --no-symbols 1 + --no-symbols + --skip-duplicate diff --git a/hcaptchanet.sln b/BenjaminAbt.HCaptchaNET.sln similarity index 96% rename from hcaptchanet.sln rename to BenjaminAbt.HCaptchaNET.sln index fe11afe..351ee25 100644 --- a/hcaptchanet.sln +++ b/BenjaminAbt.HCaptchaNET.sln @@ -16,8 +16,9 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{246E4F1E-C4B3-4180-906C-605E72C69097}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - .github\workflows\ci.yml = .github\workflows\ci.yml + .github\workflows\build.yml = .github\workflows\build.yml Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props global.json = global.json README.md = README.md version.json = version.json diff --git a/BenjaminAbt.HCaptchaNET.snk b/BenjaminAbt.HCaptchaNET.snk new file mode 100644 index 0000000000000000000000000000000000000000..f1183201df4b3166e33b5a73ea6ad9397edea700 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa500980x^B%{E=s8hQzEIl>)=^t+fN0*Bi|HP z{CfyMdDpWbqp-a7yC;pFX889Fem#LY>uQ#|_g?rgGo|3=RgL#_)pc2%+WY36|ivXTb0~< zPm|eyWYl7NN5!e;@N*rQx_wiZi27x}7I{d`hv{h9BWA+RSdUOF4s3l$!~aq%u2v4Z z#HiX?CNa(i%ZL0c(<)bO2|H@*RYywfWQ8k{M!afy^4*WbT|w{d(GLC(66+Zkaj{@A zL^}QvKBe&=K_tPkdHCBUa@w1EGo9c06=PBxE0nSDQa z`{tG&4q4_G|D=4ty7DYB<6YC`t8nbI^SI!8i|)O?C+9cqQx@X-nk#xeLcc&dE|6f$ z!mvP{8=I)4ewEtftv86SoNnvLZASQu&VoC&5>T)?dbZk-A*0^5f-dK3ELd=}n?Ax} z!>3{_r^M)v1Btdbk~NW&s*#z1*_EC1jPj{H81#)a2n6)@O)poc3Yih-F>_#=63^)^ zhiQaFl-TD*q045n3UhH(sHugsu6t}1z0jw`+`ZNe0);&_$a8er z*7cRh=9}ugE$7kcsyjkXyim)&2jlE^0z!tA762~#Aw#TiUl)ZgIaimSs_SO$xxmp| i#Z8yokVLeG7_QO$OU2$u{X9|xa$a~sRzI}mgOMG*rY}?g literal 0 HcmV?d00001 diff --git a/Directory.Build.props b/Directory.Build.props index 8f37e32..bcef4ef 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,19 +6,45 @@ https://github.com/BenjaminAbt/hcaptcha MIT en-US - true - preview - enable true true embedded + false + true + snupkg + + + $(MSBuildProjectName.Contains('Test')) $(MsBuildProjectName.Contains('Benchmark')) - false true + + net8.0 + + + + preview + enable + enable + true + + + + true + $(MSBuildThisFileDirectory)BenjaminAbt.HCaptchaNET.snk + + + 0024000004800000940000000602000000240000525341310004000001000100a9ba6ecd5a2e4a + a9095322a9baebe05966db4f05bf23df1457fc7b084079d7b320a3b0bcf5bb278d9e66f8f70d7e + 3d813aeb6a96baf75ef83033a5e0e5558df774d575599cdafbe6e047522db722b6244860a0d5b3 + b3bfbe24c50d698411c5d19e3d3765c9599809e2808ed0d6b2f9129395a95468484eac0815b070 + ea5b95dc + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..ff021a9 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,12 @@ + + + true + + + + + + + + + diff --git a/global.json b/global.json index 2a9ec01..76474f0 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "6.0.101" + "version": "9.0.100" } } diff --git a/public.snk b/public.snk new file mode 100644 index 0000000000000000000000000000000000000000..c708c4c904d51f76cbc6ba37a0b06050adf4ed39 GIT binary patch literal 160 zcmV;R0AK$ABme*efB*oL000060ssI2Bme+XQ$aBR1ONa500980x^B%{E=s8hQzEIl z>)=^t+fN0*Bi|HP{CfyMdDpWbqp-a7yC;pFX889Fem#LY>uQ#|_g?rgGo|3=RgL#_ z)pc2%+WY36|ivXTb0~^G)8Cu literal 0 HcmV?d00001 diff --git a/sample/sample.aspnetcore/sample.aspnetcore.csproj b/sample/sample.aspnetcore/sample.aspnetcore.csproj index 6a177a0..ba76029 100644 --- a/sample/sample.aspnetcore/sample.aspnetcore.csproj +++ b/sample/sample.aspnetcore/sample.aspnetcore.csproj @@ -1,7 +1,6 @@  - net6.0 BenjaminAbt.HCaptcha.Samples.AspNetCore BenjaminAbt.HCaptcha.Samples.AspNetCore diff --git a/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj b/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj index 6728da3..00d3da8 100644 --- a/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj +++ b/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj @@ -2,16 +2,9 @@ HCaptcha for ASP.NET Core - net6.0 BenjaminAbt.HCaptcha.AspNetCore BenjaminAbt.HCaptcha.AspNetCore - true embedded - - - - true - snupkg true @@ -34,7 +27,7 @@ - + diff --git a/src/HCaptcha/HCaptcha.csproj b/src/HCaptcha/HCaptcha.csproj index d4190ee..15986f6 100644 --- a/src/HCaptcha/HCaptcha.csproj +++ b/src/HCaptcha/HCaptcha.csproj @@ -4,14 +4,7 @@ HCaptcha for .NET BenjaminAbt.HCaptcha BenjaminAbt.HCaptcha - net6.0 - true embedded - - - - true - snupkg true @@ -30,9 +23,9 @@ - - - + + + diff --git a/version.json b/version.json index 1a05557..1dc6bc7 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.9", + "version": "1.0", "assemblyVersion": { "precision": "revision" // optional. Use when you want a more precise assembly version than the default major.minor. }, From cf7758da2c5bc6a46476584776f272ae45499afe Mon Sep 17 00:00:00 2001 From: Benjamin Abt Date: Wed, 13 Nov 2024 14:08:37 +0100 Subject: [PATCH 2/3] add fix --- .github/workflows/build.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eeeaec5..115bb0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,21 +49,6 @@ jobs: --no-restore --configuration ${{ env.BuildConfig }} /p:Version=${{ steps.nbgv.outputs.AssemblyVersion }} - - name: Test with dotnet - run: dotnet test - --no-build --configuration ${{ env.BuildConfig }} - --logger "trx;LogFileName=test-results.trx" --results-directory ./artifacts/testResults - continue-on-error: true - - - name: Test Report - uses: dorny/test-reporter@v1 - if: always() - with: - name: DotNET Tests - path: "./artifacts/testResults/test-results.trx" - reporter: dotnet-trx - fail-on-error: true - - name: Pack NuGet run: dotnet pack --configuration ${{ env.BuildConfig }} From 91d7a5f3db35f4eb403efc13c7330227a7db2c8a Mon Sep 17 00:00:00 2001 From: Benjamin Abt Date: Wed, 13 Nov 2024 14:16:34 +0100 Subject: [PATCH 3/3] add docs --- .editorconfig | 2 +- Directory.Build.props | 4 -- .../Controllers/HomeController.cs | 8 ++-- sample/sample.aspnetcore/Program.cs | 19 +++++++-- .../Properties/launchSettings.json | 9 +--- sample/sample.aspnetcore/Startup.cs | 41 +++++++++++++------ .../sample.aspnetcore/Views/Home/Index.cshtml | 2 - .../AuthorEntityBinderProvider.cs | 20 ++++++++- .../HCaptcha.AspNetCore.csproj | 1 + src/HCaptcha.AspNetCore/HCaptchaExtensions.cs | 26 ++++++++---- .../HCaptchaModelBinder.cs | 40 ++++++++++++++---- .../HCaptchaModelBinderExtensions.cs | 16 +++++++- .../HCaptchaModelBinderOptions.cs | 14 ++++++- src/HCaptcha/HCaptcha.csproj | 1 + src/HCaptcha/HCaptchaApiException.cs | 27 +++++------- src/HCaptcha/HCaptchaOptions.cs | 2 +- src/HCaptcha/HCaptchaProvider.cs | 4 +- src/HCaptcha/HCaptchaResponseExtensions.cs | 4 +- src/HCaptcha/HCaptchaVerifyErrorCode.cs | 2 +- src/HCaptcha/HCaptchaVerifyResponse.cs | 3 +- src/HCaptcha/IHCaptchaApi.cs | 4 +- src/HCaptcha/IHCaptchaProvider.cs | 5 +-- 22 files changed, 162 insertions(+), 92 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7fdbafc..a8fca2d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,7 +18,7 @@ trim_trailing_whitespace = true # C# files [*.cs] -file_header_template = Copyright © Benjamin Abt 2020-2021, all rights reserved +file_header_template = Copyright © Benjamin Abt 2020-2024, all rights reserved #### .NET Coding Conventions #### diff --git a/Directory.Build.props b/Directory.Build.props index bcef4ef..2b79e5d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -19,10 +19,6 @@ $(MsBuildProjectName.Contains('Benchmark')) - - true - - net8.0 diff --git a/sample/sample.aspnetcore/Controllers/HomeController.cs b/sample/sample.aspnetcore/Controllers/HomeController.cs index b3b464a..47b02c7 100644 --- a/sample/sample.aspnetcore/Controllers/HomeController.cs +++ b/sample/sample.aspnetcore/Controllers/HomeController.cs @@ -1,4 +1,4 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved using Microsoft.AspNetCore.Mvc; @@ -13,9 +13,7 @@ public class HomeController : Controller public IActionResult Index(HCaptchaVerifyResponse hCaptcha) => View(new IndexViewModel(hCaptcha)); } -public class IndexViewModel +public class IndexViewModel(HCaptchaVerifyResponse? response = null) { - public HCaptchaVerifyResponse? Response { get; } - - public IndexViewModel(HCaptchaVerifyResponse? response = null) => Response = response; + public HCaptchaVerifyResponse? Response { get; } = response; } diff --git a/sample/sample.aspnetcore/Program.cs b/sample/sample.aspnetcore/Program.cs index cfd8cbd..dc3fef1 100644 --- a/sample/sample.aspnetcore/Program.cs +++ b/sample/sample.aspnetcore/Program.cs @@ -1,21 +1,32 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved - -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; +// Copyright © Benjamin Abt 2020-2024, all rights reserved namespace BenjaminAbt.HCaptcha.Samples.AspNetCore; +/// +/// The entry point for the application, responsible for setting up and running the web host. +/// public class Program { + /// + /// The main entry point for the application. + /// + /// The command-line arguments passed to the application. public static void Main(string[] args) { + // Build and run the host CreateHostBuilder(args).Build().Run(); } + /// + /// Creates and configures the for the application. + /// + /// The command-line arguments passed to the application. + /// An that configures the web host. public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { + // Use the Startup class for configuring services and middleware webBuilder.UseStartup(); }); } diff --git a/sample/sample.aspnetcore/Properties/launchSettings.json b/sample/sample.aspnetcore/Properties/launchSettings.json index 4f49531..6ca3990 100644 --- a/sample/sample.aspnetcore/Properties/launchSettings.json +++ b/sample/sample.aspnetcore/Properties/launchSettings.json @@ -1,4 +1,4 @@ -{ +{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, @@ -8,13 +8,6 @@ } }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "hcaptcha.aspnetcore": { "commandName": "Project", "launchBrowser": true, diff --git a/sample/sample.aspnetcore/Startup.cs b/sample/sample.aspnetcore/Startup.cs index ef4423a..6cef1d0 100644 --- a/sample/sample.aspnetcore/Startup.cs +++ b/sample/sample.aspnetcore/Startup.cs @@ -1,47 +1,62 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved using BenjaminAbt.HCaptcha.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; namespace BenjaminAbt.HCaptcha.Samples.AspNetCore; +/// +/// Configures services and the HTTP request pipeline for the application. +/// public class Startup { + /// + /// Gets the configuration settings for the application. + /// + public IConfiguration Configuration { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration settings for the application. public Startup(IConfiguration configuration) { Configuration = configuration; } - public IConfiguration Configuration { get; } - + /// + /// Configures services for dependency injection in the application. + /// + /// The to which services are added. public void ConfigureServices(IServiceCollection services) { - // HCaptcha + // Add hCaptcha services using the configuration from the "HCaptcha" section services.AddHCaptcha(Configuration.GetSection("HCaptcha")); - // Mvc + // Add MVC controllers with views and configure the hCaptcha model binder services.AddControllersWithViews(mvcOptions => - // add model binder + // Add custom model binder for hCaptcha mvcOptions.AddHCaptchaModelBinder()); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// + /// Configures the HTTP request pipeline for the application. + /// + /// The used to configure the middleware pipeline. + /// The containing information about the hosting environment. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { + // Use the developer exception page in development app.UseDeveloperExceptionPage(); } + // Enable routing in the middleware pipeline app.UseRouting(); app.UseEndpoints(endpoints => { - // Mvc + // Map MVC controllers to their respective endpoints endpoints.MapControllers(); }); } diff --git a/sample/sample.aspnetcore/Views/Home/Index.cshtml b/sample/sample.aspnetcore/Views/Home/Index.cshtml index ebfcd7a..115ee56 100644 --- a/sample/sample.aspnetcore/Views/Home/Index.cshtml +++ b/sample/sample.aspnetcore/Views/Home/Index.cshtml @@ -8,7 +8,6 @@ } -

hCaptcha Demo

@@ -20,7 +19,6 @@
- @{ var response = Model?.Response; if (response != null) diff --git a/src/HCaptcha.AspNetCore/AuthorEntityBinderProvider.cs b/src/HCaptcha.AspNetCore/AuthorEntityBinderProvider.cs index 1dbcfd6..e47cbd3 100644 --- a/src/HCaptcha.AspNetCore/AuthorEntityBinderProvider.cs +++ b/src/HCaptcha.AspNetCore/AuthorEntityBinderProvider.cs @@ -1,22 +1,38 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; namespace BenjaminAbt.HCaptcha.AspNetCore; +/// +/// Provides a model binder for binding an to an action parameter. +/// public class AuthorEntityBinderProvider : IModelBinderProvider { + /// + /// Returns a model binder for the model type, if applicable. + /// + /// The context that provides information about the model being bound. + /// + /// An instance if the model type is , otherwise null. + /// + /// + /// Thrown when the is null. + /// public IModelBinder? GetBinder(ModelBinderProviderContext context) { + // Ensure the context is not null ArgumentNullException.ThrowIfNull(context); + // Check if the model type is HCaptchaVerifyResponse if (context.Metadata.ModelType == typeof(HCaptchaVerifyResponse)) { + // Return a binder for HCaptchaVerifyResponse return new BinderTypeModelBinder(typeof(HCaptchaModelBinder)); } + // Return null if no binder is required for the model type return null; } } diff --git a/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj b/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj index 00d3da8..36ccd07 100644 --- a/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj +++ b/src/HCaptcha.AspNetCore/HCaptcha.AspNetCore.csproj @@ -6,6 +6,7 @@ BenjaminAbt.HCaptcha.AspNetCore embedded true + true diff --git a/src/HCaptcha.AspNetCore/HCaptchaExtensions.cs b/src/HCaptcha.AspNetCore/HCaptchaExtensions.cs index 99ad7e8..50cbab9 100644 --- a/src/HCaptcha.AspNetCore/HCaptchaExtensions.cs +++ b/src/HCaptcha.AspNetCore/HCaptchaExtensions.cs @@ -1,32 +1,42 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Refit; namespace BenjaminAbt.HCaptcha.AspNetCore; +/// +/// Provides extension methods for registering hCaptcha services in the dependency injection container. +/// public static class HCaptchaExtensions { /// - /// Adds the HCaptcha configuration as . - /// Adds as Refit Client with given base address in . + /// Adds hCaptcha services to the using the provided configuration section. /// + /// The to which the hCaptcha services will be added. + /// The containing the hCaptcha configuration settings. + /// The with hCaptcha services added. + /// + /// Thrown when or is null. + /// public static IServiceCollection AddHCaptcha(this IServiceCollection services, IConfigurationSection section) { + // Bind configuration to HCaptchaOptions HCaptchaOptions captchaOptions = new(); section.Bind(captchaOptions); + // Configure options to be injected wherever needed services.Configure(section); + // Register the Refit client for IHCaptchaApi with the base URL from configuration services.AddRefitClient() .ConfigureHttpClient(c => - { - c.BaseAddress = new Uri(captchaOptions.ApiBaseUrl); - } - ); + { + c.BaseAddress = new Uri(captchaOptions.ApiBaseUrl); + }); + // Register the hCaptcha provider services.AddScoped(); return services; diff --git a/src/HCaptcha.AspNetCore/HCaptchaModelBinder.cs b/src/HCaptcha.AspNetCore/HCaptchaModelBinder.cs index 76b84c2..5005e0a 100644 --- a/src/HCaptcha.AspNetCore/HCaptchaModelBinder.cs +++ b/src/HCaptcha.AspNetCore/HCaptchaModelBinder.cs @@ -1,45 +1,67 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System; -using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; namespace BenjaminAbt.HCaptcha.AspNetCore; +/// +/// A model binder that binds an hCaptcha verification result to an action parameter. +/// public class HCaptchaModelBinder : IModelBinder { private readonly IHCaptchaProvider _captchaProvider; private readonly HCaptchaOptions _captchaOptions; + /// + /// Initializes a new instance of the class. + /// + /// The hCaptcha provider used to verify the captcha token. + /// The configuration options for hCaptcha. public HCaptchaModelBinder(IHCaptchaProvider captchaProvider, IOptions captchaOptionsAccessor) { _captchaProvider = captchaProvider; _captchaOptions = captchaOptionsAccessor.Value; } + /// + /// Binds the model by verifying the hCaptcha token from the HTTP request. + /// + /// The that contains the HTTP request data to bind. + /// A representing the asynchronous operation. + /// + /// Thrown when is null. + /// + /// + /// Thrown if the HTTP request method is not "POST", as hCaptcha validation is only allowed on POST requests. + /// public async Task BindModelAsync(ModelBindingContext bindingContext) { + // Ensure bindingContext is not null ArgumentNullException.ThrowIfNull(bindingContext); - // validate context - var httpContext = bindingContext.HttpContext; + // Validate that the request method is POST + HttpContext httpContext = bindingContext.HttpContext; if (httpContext.Request.Method != "POST") { throw new InvalidOperationException($"{nameof(HCaptchaModelBinder)} can only be used with HTTP Post."); } - // read token - var token = httpContext.Request.Form[_captchaOptions.HttpPostResponseKeyName]; + // Retrieve the token from the form data + StringValues token = httpContext.Request.Form[_captchaOptions.HttpPostResponseKeyName]; - // verify + // Verify the token with the hCaptcha provider try { - var result = await _captchaProvider.Verify(token, httpContext.Connection?.RemoteIpAddress?.ToString()); + HCaptchaVerifyResponse? result = await _captchaProvider + .Verify(token, httpContext.Connection?.RemoteIpAddress?.ToString()).ConfigureAwait(false); bindingContext.Result = ModelBindingResult.Success(result); } catch (HCaptchaApiException apiException) { + // If verification fails, add a model error and mark the binding result as failed bindingContext.ModelState.TryAddModelError(bindingContext.FieldName, apiException.Message); bindingContext.Result = ModelBindingResult.Failed(); } diff --git a/src/HCaptcha.AspNetCore/HCaptchaModelBinderExtensions.cs b/src/HCaptcha.AspNetCore/HCaptchaModelBinderExtensions.cs index 6be4611..229cf15 100644 --- a/src/HCaptcha.AspNetCore/HCaptchaModelBinderExtensions.cs +++ b/src/HCaptcha.AspNetCore/HCaptchaModelBinderExtensions.cs @@ -1,18 +1,30 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System; using Microsoft.AspNetCore.Mvc; namespace BenjaminAbt.HCaptcha.AspNetCore; +/// +/// Extension methods for configuring the hCaptcha model binder in MVC options. +/// public static class HCaptchaModelBinderExtensions { + /// + /// Adds the hCaptcha model binder to the with optional configuration. + /// + /// The to which the model binder will be added. + /// An optional configuration action for setting up the model binder options. + /// The updated with the hCaptcha model binder added. public static MvcOptions AddHCaptchaModelBinder(this MvcOptions mvcOptions, Action? captchaModelBinderOptions = null) { + // Create a default set of model binder options HCaptchaModelBinderOptions cmbo = new(); + + // Apply custom configuration if provided captchaModelBinderOptions?.Invoke(cmbo); + // Insert the custom AuthorEntityBinderProvider at the specified position mvcOptions.ModelBinderProviders.Insert(cmbo.BinderPosition, new AuthorEntityBinderProvider()); return mvcOptions; diff --git a/src/HCaptcha.AspNetCore/HCaptchaModelBinderOptions.cs b/src/HCaptcha.AspNetCore/HCaptchaModelBinderOptions.cs index a1655ed..575a933 100644 --- a/src/HCaptcha.AspNetCore/HCaptchaModelBinderOptions.cs +++ b/src/HCaptcha.AspNetCore/HCaptchaModelBinderOptions.cs @@ -1,8 +1,20 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved + +using Microsoft.AspNetCore.Mvc; namespace BenjaminAbt.HCaptcha.AspNetCore; +/// +/// Options for configuring the position of the hCaptcha model binder in the model binder provider list. +/// public class HCaptchaModelBinderOptions { + /// + /// Gets or sets the position of the hCaptcha model binder in the model binder provider list. + /// + /// + /// The binder is inserted at the specified position in the list. + /// A value of 0 means it will be inserted at the beginning of the list. + /// public int BinderPosition { get; set; } = 0; } diff --git a/src/HCaptcha/HCaptcha.csproj b/src/HCaptcha/HCaptcha.csproj index 15986f6..c863e3d 100644 --- a/src/HCaptcha/HCaptcha.csproj +++ b/src/HCaptcha/HCaptcha.csproj @@ -6,6 +6,7 @@ BenjaminAbt.HCaptcha embedded true + true diff --git a/src/HCaptcha/HCaptchaApiException.cs b/src/HCaptcha/HCaptchaApiException.cs index b83deb6..9cc8ec0 100644 --- a/src/HCaptcha/HCaptchaApiException.cs +++ b/src/HCaptcha/HCaptchaApiException.cs @@ -1,28 +1,23 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System; using System.Net; using Refit; namespace BenjaminAbt.HCaptcha; /// -/// hCapcha API Exception +/// Represents an exception that occurs when an hCaptcha API request fails. /// -/// Inner exception is a of type which is a part of Refit. -public class HCaptchaApiException : Exception +/// +/// Initializes a new instance of the class. +/// +/// The HTTP status code returned by the hCaptcha API. +/// The inner exception that contains details about the API error. +public class HCaptchaApiException(HttpStatusCode statusCode, ApiException apiException) + : Exception("hCaptcha API request failed. See inner exception for details.", apiException) { /// - /// Status Code + /// Gets the HTTP status code associated with the failed hCaptcha API request. /// - public HttpStatusCode StatusCode { get; } // Refit as Inner Exception - - /// - /// Creates an instance of with wrapped - /// - public HCaptchaApiException(HttpStatusCode statusCode, ApiException apiException) - : base("hCaptcha API request failed. See inner exception for details.", apiException) - { - StatusCode = statusCode; - } + public HttpStatusCode StatusCode { get; } = statusCode; } diff --git a/src/HCaptcha/HCaptchaOptions.cs b/src/HCaptcha/HCaptchaOptions.cs index 6a9528f..9c2035c 100644 --- a/src/HCaptcha/HCaptchaOptions.cs +++ b/src/HCaptcha/HCaptchaOptions.cs @@ -1,4 +1,4 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved namespace BenjaminAbt.HCaptcha; diff --git a/src/HCaptcha/HCaptchaProvider.cs b/src/HCaptcha/HCaptchaProvider.cs index ae85a79..7120c6d 100644 --- a/src/HCaptcha/HCaptchaProvider.cs +++ b/src/HCaptcha/HCaptchaProvider.cs @@ -1,7 +1,5 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Options; using Refit; diff --git a/src/HCaptcha/HCaptchaResponseExtensions.cs b/src/HCaptcha/HCaptchaResponseExtensions.cs index a5d83bb..fcaf449 100644 --- a/src/HCaptcha/HCaptchaResponseExtensions.cs +++ b/src/HCaptcha/HCaptchaResponseExtensions.cs @@ -1,6 +1,4 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved - -using System.Collections.Generic; +// Copyright © Benjamin Abt 2020-2024, all rights reserved namespace BenjaminAbt.HCaptcha; diff --git a/src/HCaptcha/HCaptchaVerifyErrorCode.cs b/src/HCaptcha/HCaptchaVerifyErrorCode.cs index a3a1028..5ff3506 100644 --- a/src/HCaptcha/HCaptchaVerifyErrorCode.cs +++ b/src/HCaptcha/HCaptchaVerifyErrorCode.cs @@ -1,4 +1,4 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved namespace BenjaminAbt.HCaptcha; diff --git a/src/HCaptcha/HCaptchaVerifyResponse.cs b/src/HCaptcha/HCaptchaVerifyResponse.cs index fd96f69..60dbe00 100644 --- a/src/HCaptcha/HCaptchaVerifyResponse.cs +++ b/src/HCaptcha/HCaptchaVerifyResponse.cs @@ -1,6 +1,5 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System; using System.Text.Json.Serialization; namespace BenjaminAbt.HCaptcha; diff --git a/src/HCaptcha/IHCaptchaApi.cs b/src/HCaptcha/IHCaptchaApi.cs index d1da735..e33a5d7 100644 --- a/src/HCaptcha/IHCaptchaApi.cs +++ b/src/HCaptcha/IHCaptchaApi.cs @@ -1,7 +1,5 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved +// Copyright © Benjamin Abt 2020-2024, all rights reserved -using System.Threading; -using System.Threading.Tasks; using Refit; namespace BenjaminAbt.HCaptcha; diff --git a/src/HCaptcha/IHCaptchaProvider.cs b/src/HCaptcha/IHCaptchaProvider.cs index 216d83b..a07b3c2 100644 --- a/src/HCaptcha/IHCaptchaProvider.cs +++ b/src/HCaptcha/IHCaptchaProvider.cs @@ -1,7 +1,4 @@ -// Copyright © Benjamin Abt 2020-2021, all rights reserved - -using System.Threading; -using System.Threading.Tasks; +// Copyright © Benjamin Abt 2020-2024, all rights reserved namespace BenjaminAbt.HCaptcha;