You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core Razor组件HttpContext访问异常问题求助

ASP.NET Core Razor组件中HttpContext访问不一致问题

问题描述

我在ASP.NET Core的Razor组件页面中遇到HttpContext访问不一致的问题。需要从Razor组件页面访问HttpContext,当前在OnInitialized()事件中读取Cookie和Session字符串,但这个事件会被调用两次:第一次能正常读取Cookie与Session;第二次HttpContextAccessor.HttpContext返回null,无法访问Cookie和Session变量。该问题仅出现在预发布环境,开发环境运行正常。

我的_Host.cshtml使用了render-mode="ServerPrerendered",这导致OnInitialized()被两次调用;若改为render-mode="Server",则无法读取Cookie。我们采用智能卡进行用户认证。

Razor页面代码(/certcheck5)

@page "/certcheck5"

<PageTitle>Certificate Check</PageTitle>

@using System.Diagnostics;
@using Microsoft.Extensions.Options;
@using Microsoft.AspNetCore.Http;
@using Microsoft.Extensions.Logging;
@using System.Security.Cryptography.X509Certificates;
@using CIS_2.Helpers;

@inject ILogger<SearchParameters> Logger;
@inject IHttpContextAccessor HttpContextAccessor;

<h1>Certificate Check</h1>

<p>
    Retrieves HttpContext.Connection.ClientCertificate.<br>
    Enumerates Session Variable.<br><br>
    <label>
        <input type="checkbox" @bind="shouldRender" />
        ShouldRender?
    </label>
</p>

<p>
    <div>Counter: @iCnt.ToString()</div>
    <button @onclick="IncrementCount">Increment Counter</button>
</p>

<p>
    <h4>HttpContext</h4>
    <div>USER CERT Subject: @Subject</div>
    <div>USER CERT Thumbprint: @Thumbprint</div>
    <div>USER CERT Expiry Date: @Expires</div>
    <div>REQUEST COOKIE[AuthCookie]: @AuthCookie</div>
</p>

<p>
    <h4>Session Parameters</h4>
    <div>Status: @Status</div>
    <div>SessionVar: @SessionVar</div>
</p>

@code {

    private bool shouldRender = true;
    private bool bLoading = true;
    private X509Certificate2 UserCert;
    private string Subject = string.Empty;
    private string Thumbprint = string.Empty;
    private string Expires = string.Empty;
    private string AuthCookie = string.Empty;
    private string SessionVar = string.Empty;
    private string Status = string.Empty;
    private int iCnt = 0;

    protected override void OnInitialized()
    {
        Logger.LogWarning("***CertCheck5.OnInitialized() invoke");
        string auth_Cookie = string.Empty;
        try
        {
            auth_Cookie = HttpContextAccessor.HttpContext.Request.Cookies["FMLO-auth"];

            Status = "Starting";
            Status = HttpContextAccessor.HttpContext.ToString();
            Status = HttpContextAccessor.HttpContext.Session.ToString();
            SessionVar = HttpContextAccessor.HttpContext.Session.GetString("SearchParameters");
            Debug.WriteLine("***Session.Restore() worked");
            Logger.LogWarning("***Session.Restore() worked");
        }
        catch (Exception exc)
        {
            Logger.LogWarning("***Session.Restore() failed:" + exc.Message);
        }

        if (HttpContextAccessor.HttpContext is not null)
        {
            X509Certificate2 UserCert = HttpContextAccessor.HttpContext.Connection.ClientCertificate;

            try
            {
                // This is called more than once the cookie is null the first time.
                auth_Cookie = HttpContextAccessor.HttpContext.Request.Cookies["FM-auth"];
            }
            catch (Exception exc)
            { Logger.LogWarning("***HttpContextAccessor.HttpContext.Request.Cookies failed:" + exc.Message); }

            if (auth_Cookie is not null)
            {
                if (Subject.Length == 0) Subject = UserCert.Subject.ToString();
                if (Thumbprint.Length == 0) Thumbprint = UserCert.Thumbprint;
                if (Expires.Length == 0) Expires = UserCert.NotAfter.ToString();
                if (AuthCookie.Length == 0) AuthCookie = auth_Cookie;
            }
        }
    }

    protected override bool ShouldRender()
    {
        Debug.WriteLine("***ShouldRender");
        Logger.LogWarning("***ShouldRender");
        return shouldRender;
        //return base.ShouldRender();
    }

    private void IncrementCount()
    {
        iCnt++;
    }
}

Program.cs代码

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using System.IO;
using System.Diagnostics;
using Microsoft.AspNetCore.StaticFiles.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System;

WebApplicationBuilder builder;

builder = WebApplication.CreateBuilder(
    new WebApplicationOptions
    {
        ContentRootPath = Directory.GetCurrentDirectory(),
        WebRootPath = Directory.GetCurrentDirectory() + "\\wwwroot"
    }
    );
// Add services to the container.

builder.Services.AddAuthentication(
    CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.AllowedCertificateTypes = CertificateTypes.All;
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                //Leveraging the DP injected single instance of the CertificateValidation Service
                var validationService = context.HttpContext.RequestServices.GetService<ICertValidation>();
                // Here it validate and call the methode ValidateCertificate from CertificateValidationService class.
                if (validationService.ValidateCertificate(context.ClientCertificate))
                {
                    context.Success();
                }
                else
                {
                    context.Fail("Invalid certificate");
                }
                return Task.CompletedTask;
            },
            OnAuthenticationFailed = context =>
            {
                context.Fail("Invalid certificate");
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddRazorPages();
builder.Services.AddControllers();
builder.Services.AddServerSideBlazor();
builder.Services.AddSession(options =>
{
    options.Cookie.IsEssential = true;
});

builder.Services.AddDistributedMemoryCache();
builder.Services.AddMvc();

builder.Services.AddSingleton<ICertValidation, CertificateValidationService>();
builder.Services.AddHttpContextAccessor();

builder.Services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "X-SSL-CERT";

    options.HeaderConverter = headerValue =>
    {
        X509Certificate2? clientCertificate = null;

        if (!string.IsNullOrWhiteSpace(headerValue))
        {
            clientCertificate = new X509Certificate2(StringToByteArray(headerValue));
        }

        return clientCertificate!;

        static byte[] StringToByteArray(string hex)
        {
            var numberChars = hex.Length;
            var bytes = new byte[numberChars / 2];

            for (int i = 0; i < numberChars; i += 2)
            {
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            }

            return bytes;
        }
    };
});

builder.Services.Configure<CisSettings>(builder.Configuration.GetSection("Settings"));

var app = builder.Build();

app.UseAuthentication();
//app.UseAuthorization();

var sPDF_Physicialroot = builder.Configuration["Settings:PDF_Physicialroot"];
var sPDF_InvoiceBaseURL = builder.Configuration["Settings:PDF_InvoiceBaseURL"];


// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseStaticFiles();

// this adds a folder that will render static files from "/pdf_invoices (PDF_InvoiceBaseURL)
// wwwroot\cis\PDF_Invoices
SharedOptions sharedOptions = new SharedOptions();
sharedOptions.FileProvider = new PhysicalFileProvider(sPDF_Physicialroot);
sharedOptions.RequestPath = new PathString(sPDF_BaseURL);
StaticFileOptions staticFileOptions = new StaticFileOptions(sharedOptions);
app.UseStaticFiles(staticFileOptions);
app.Logger.LogWarning("Trace Message from Program.cs");

app.UseSession();

//begin SetString() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
    //this throwaway session variable will "prime" the SetString() method
    //to allow it to be called after the response has started
    var TempKey = Guid.NewGuid().ToString(); //create a random key
    Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
    Context.Session.Remove(TempKey); //remove the throwaway session variable
    string auth_Cookie = $"None";
    Context.Request.Cookies.TryGetValue("FM-auth", out auth_Cookie);
    string msg = "Program.cs auth_Cookie=" + auth_Cookie;
    app.Logger.LogWarning(msg);
    SessionParameters sessionParameters;
    ISearchParameters searchParameters;
    searchParameters = new SearchParameters(app.Logger);
    sessionParameters = new SessionParameters(app.Logger);
    if (!sessionParameters.Exists(Context.Session))
    {
        searchParameters.OnInitialized(Uics, auth_Cookie);
        sessionParameters.StoreMain(Context.Session, searchParameters);
    }
    await Next(); //continue on with the request
});
//end SetString() hack

Debug.WriteLine($"Content Root Path: {builder.Environment.ContentRootPath}");
Debug.WriteLine($"WebRootPath:  {builder.Environment.WebRootPath}");
Debug.WriteLine($"PDFRootPath:  {sPDF_Physicialroot}");

app.UseRouting();

app.UseEndpoints(endpoints =>
{
    app.MapControllers();
    app.MapBlazorHub();
    app.MapFallbackToPage("/_Host");
}
);

app.Run();

内容的提问来源于stack exchange,提问作者Remie Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 05:10:56