I am currently working on a migration project to migrate from .NET Framework to .NET 8. I am trying to migrate a custom attribute, but I'm getting some error. Can someone help me fix it?
Here's my code:
ValidateAntiForgeryHeader.cs
:
public sealed class ValidateAntiForgeryHeader : Attribute, IAuthorizationFilter
{
private const string KEY_NAME = "__RequestVerificationToken";
private readonly IAntiforgery _antiForgery;
private readonly ILogger<ValidateAntiForgeryHeader> _logger;
// Inject the IAntiforgery and ILogger
public ValidateAntiForgeryHeader(IAntiforgery antiForgery, ILogger<ValidateAntiForgeryHeader> logger)
{
_antiForgery = antiForgery ?? throw new ArgumentNullException(nameof(antiForgery));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
var clientToken = filterContext.HttpContext.Request.Headers[KEY_NAME].FirstOrDefault();
string userId = SessionFacade.UserID(filterContext.HttpContext.RequestServices.GetService<IHttpContextAccessor>());
if (string.IsNullOrEmpty(clientToken))
{
Logger.LogException("ValidateAntiForgeryHeader", "OnAuthorization", clientToken, userId, Priority.Error, "ClientToken");
}
string serverToken = string.Empty;
foreach (var cookie in filterContext.HttpContext.Request.Cookies)
{
if (cookie.Key.Contains(KEY_NAME))
{
serverToken = cookie.Value;
break;
}
}
if (string.IsNullOrEmpty(serverToken))
{
Logger.LogException("ValidateAntiForgeryHeader", "OnAuthorization", serverToken, userId, Priority.Error, "ServerToken");
}
// System.Web.Helpers.AntiForgery.Validate(serverToken, clientToken);
// Perform AntiForgery validation if both tokens are present
if (!string.IsNullOrEmpty(clientToken) && !string.IsNullOrEmpty(serverToken))
{
try
{
// Validate the anti-forgery tokens
_antiForgery.ValidateRequestAsync(filterContext.HttpContext).Wait();
}
catch (Exception ex)
{
_logger.LogError(ex, "Anti-forgery token validation failed.");
filterContext.Result = new Microsoft.AspNetCore.Mvc.BadRequestObjectResult("Invalid anti-forgery token.");
}
}
else
{
filterContext.Result = new Microsoft.AspNetCore.Mvc.BadRequestObjectResult("Missing tokens.");
}
}
// private const string KEY_NAME = "__RequestVerificationToken";
}
Program.cs
:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
IConfiguration configuration = builder.Configuration;
builder.Services.AddScoped<ValidateAntiForgeryHeader>(); // Register custom filter
builder.Services.AddScoped<POSSExceptionHandlerAttribute>(); // Register POSSExceptionHandler
builder.Services.AddLogging();
builder.Services.AddAntiforgery(options =>
{
options.Cookie.Name = "X-CSRF-TOKEN";
options.FormFieldName = "__RequestVerificationToken";
options.SuppressXFrameOptionsHeader = false;
});
// builder.Services.AddControllersWithViews().AddDataAnnotationsLocalization();
builder.Services.AddControllersWithViews()
.AddJsonOptions(options =>
options.JsonSerializerOptions.PropertyNamingPolicy = null); // Disable camel case for JSON
//
builder.Services.AddControllersWithViews(options =>
{
// Register the global filters here.
//options.Filters.Add(new POSSExceptionHandlerAttribute(builder.Services.BuildServiceProvider().GetRequiredService<ILogger<POSSExceptionHandlerAttribute>>()));
options.Filters.AddService<POSSExceptionHandlerAttribute>();
options.Filters.AddService<ValidateAntiForgeryHeader>();
});
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/AccessDenied";
});
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
// Add authorization policies
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
builder.Services.AddKendo();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSession();
var app = builder.Build();
// Configure middleware pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// Enable AntiForgery middleware
app.UseAntiforgery();
app.UseAuthentication();
app.UseSession();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Login}/{action=Index}/{id?}");
});
app.Run()
Razor view file:
@Html.AntiForgeryToken()
<div class="row">
Controller file:
[HttpPost]
[ValidateAntiForgeryHeader]
public ActionResult SaveCommentsForVisitorTransaction(string newComments, string oldComments, string visitorPass)
{
// .....
}
Error
There is no argument given that corresponds to the required parameter 'antiForgery' of 'ValidateAntiForgeryHeader.ValidateAntiForgeryHeader(IAntiforgery, ILogger)'
The error occurs because the constructor of the ValidateAntiForgeryHeader
class requires two arguments, IAntiforgery
and ILogger<ValidateAntiForgeryHeader>
, but I am passing [ValidateAntiForgeryHeader]
attribute without any argument.
Please help me to fix this issue and let me know if I am doing anything wrong here or missing anything.
Please help me to fix this issue and let me know if I am doing anything wrong here or missing anything.
The attribute couldn't get the injected service via the constructor parameter, to solve this issue, you should use the HttpContext.RequestServices.GetRequiredService method to get the required service and then use it inside the attribute method.
More details, you could refer to below codes:
public sealed class ValidateAntiForgeryHeader : Attribute, IAuthorizationFilter
{
private const string KEY_NAME = "__RequestVerificationToken";
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
// Resolve services from the DI container
var antiForgery = filterContext.HttpContext.RequestServices.GetRequiredService<IAntiforgery>();
var logger = filterContext.HttpContext.RequestServices.GetRequiredService<ILogger<ValidateAntiForgeryHeader>>();
var clientToken = filterContext.HttpContext.Request.Headers[KEY_NAME].FirstOrDefault();
if (string.IsNullOrEmpty(clientToken))
{
logger.LogError("ClientToken is missing.");
}
string serverToken = string.Empty;
foreach (var cookie in filterContext.HttpContext.Request.Cookies)
{
if (cookie.Key.Contains(KEY_NAME))
{
serverToken = cookie.Value;
break;
}
}
if (string.IsNullOrEmpty(serverToken))
{
logger.LogError("ServerToken is missing.");
}
// Perform AntiForgery validation if both tokens are present
if (!string.IsNullOrEmpty(clientToken) && !string.IsNullOrEmpty(serverToken))
{
try
{
// Validate the anti-forgery tokens
antiForgery.ValidateRequestAsync(filterContext.HttpContext).Wait();
}
catch (Exception ex)
{
logger.LogError(ex, "Anti-forgery token validation failed.");
filterContext.Result = new BadRequestObjectResult("Invalid anti-forgery token.");
}
}
else
{
filterContext.Result = new BadRequestObjectResult("Missing tokens.");
}
}
}
Result: