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

迁移至.NET6后IdentityServer4后续登录抛出空引用异常

问题描述

我们已迁移至.NET6,由于IdentityServer4移除了PublicOrigin,采用了如下临时解决方案:

var publicOrigin = Configuration.GetValue<string>("PublicOrigin");
app.Use(async (ctx, next) =>
{
    ctx.SetIdentityServerOrigin(publicOrigin);
    await next();
});

但首次登录成功后,后续登录会抛出如下异常:

执行请求时发生未处理的异常。
System.NullReferenceException: 未将对象引用设置到对象的实例。

at Core.WebApi.Common.Handlers.ApiLoggingMiddleware.InvokeAsync(HttpContext context, ApiLoggingSettings settings, IConfiguration configuration)
  at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)
  at IdentityServer4.Hosting.MutualTlsEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)
  at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
  at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
  at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
  at IdentityApp.Startup.<>c__DisplayClass10_0.<<Configure>b__0>d.MoveNext() in

完整的Startup.Configure方法如下:

public void Configure( IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();            
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
    app.UsePathBase("/identity");

    var publicOrigin = Configuration.GetValue<string>("PublicOrigin");
    app.Use(async (ctx, next) =>
    {
        ctx.SetIdentityServerOrigin(publicOrigin);
        await next();
    });

    app.UseCookiePolicy();
    app.UseStaticFiles();
    app.UseCors("CORSPolicy");
  
    app.UseAuthentication();
    app.UseIdentityServer();

    // Make system event logging explicit opt-in
    if (Configuration.GetValue<bool>("EnableSystemEventLogging") == true)
    {
        app.UseMiddleware<ApiLoggingMiddleware>();
    }

   
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    { 
        endpoints.MapDefaultControllerRoute();
    });

}

奇怪的是移除ApiLoggingMiddleware后问题消失,但不清楚原因及解决办法。ApiLoggingMiddleware代码如下:

public class ApiLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILoggerWrapper<ApiLoggingMiddleware> _logger;
    private readonly IHostingEnvironment _env;
    private JsonSerializerSettings _serializationSettings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() };
    
    public ApiLoggingMiddleware(RequestDelegate next, ILogger<ApiLoggingMiddleware> logger, IHostingEnvironment env)
    {
        _next = next;
        _logger = new LoggerWrapper<ApiLoggingMiddleware>(logger);
        _env = env;
    }

    public async Task InvokeAsync(HttpContext context, ApiLoggingSettings settings, IConfiguration configuration)
    {
        DateTime startTime = DateTime.UtcNow;

        Exception requestProcessingException = null;

        try
        {
            // ensure the logging context is clear
            ApiLoggingContext.DisposeCurrent();

            ApiLoggingContext.Current.Message = $"API operation {context.Request.Path.ToString()}";
            ApiLoggingContext.Current.TechnicalMessage = $"{context.Request.Method}  {context.Request.Path.ToString()}{context.Request.QueryString.ToString()}";
            ApiLoggingContext.Current.InitialiseForRequest(context.Request, settings);
            ApiLoggingContext.Current.ProductUserId = Controllers.ControllerHelper.GetUserId(context.User, configuration, raiseExceptionIfNotIdentified: false);
            ApiLoggingContext.Current.ProductUserName = Controllers.ControllerHelper.GetUserName(context.User, configuration);

            // process the request
            await _next(context);
        }
        catch (Exception ex)
        {
            // this will occur if the handler itself threw an error
            // store the exception for rethrow after suitable logging has occured
            requestProcessingException = ex;
            ApiLoggingContext.Current.Errors.Add(ex);
        }
        finally
        {
            DateTime endTime = DateTime.UtcNow;
            ApiLoggingContext.Current.InitialiseForResponse(context.Response, settings);
            ApiLoggingContext.Current.SetTimingInformation(startTime, endTime);
        }

        ApiLoggingContext contextToLog = ApiLoggingContext.Current;

        var responseCode = context.Response.StatusCode;
        if (!context.Response.HasStarted && requestProcessingException != null)
        {
            // Treat all unhandled exceptions thrown in handlers as error responses
            responseCode = StatusCodes.Status500InternalServerError;
            contextToLog.StatusCode = responseCode;
            contextToLog.Severity = ApiLoggingContext.SEVERITY_CODE_ERROR;
        }

        // The following modifications to the logging context cannot occur earlier because they
        // depend on members which the system event context attribute supplies

        if (contextToLog.OmitQueryParameters)
        {
            contextToLog.TechnicalMessage = contextToLog.TechnicalMessage.Split('?').FirstOrDefault();
            contextToLog.Unset("Query", startsWith: true);
        }

        // Discard any key-value pairs that might trigger exceptions when recording the system
        // event information to the database (possibly something to revisit in the future)
        var pairs = contextToLog.KeyStringPairs.Where(p => p.Value.Length > 200).ToDictionary(p => p.Key, p => p.Value);
        foreach (var pair in pairs)
        {
            _logger.LogWarning($"Discarding key-value pair for {contextToLog.SystemEventTypeId ?? "request"} due to data truncation ({pair.Key}: {pair.Value}).");
            contextToLog.KeyStringPairs.Remove(pair.Key);
        }

        string errorId = null;

        // fire and forget logging operation.. don't want to slow down response
        #pragma warning disable 4014
        if (!contextToLog.SkipWrite)
        {
            errorId = LogResponse(contextToLog, responseCode);
            Task.Run(() =>
            {
                PerformLogForContext(contextToLog, settings);
            }).ConfigureAwait(false);
        }
        #pragma warning restore 4014

        // Only return the error code response for unhandled exceptions
        if (!context.Response.HasStarted && errorId != null)
        {
            var errorPayload = JsonConvert.SerializeObject(new Response<string>(errorId), _serializationSettings);
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(errorPayload);
            return;
        }

        if (requestProcessingException != null)
        {
            // this was the exception thrown by the next handler
            // rethrow it now
            throw requestProcessingException;
        }
    }

    private string LogResponse(ApiLoggingContext loggingContext, int responseStatusCode)
    {
        // Do not assume every exposed handler will have the system event context annotation,
        // and note that system event types are only available after the execution of handlers
        var systemEventTypeId = loggingContext.SystemEventTypeId;

        var lastError = loggingContext.Errors.LastOrDefault();
        if (lastError != null || responseStatusCode >= ApiLoggingContext.MINIMUM_HTTP_STATUS_ERROR_CODE)
        {
            // Endpoints that fail with a result may put useful information about the cause of
            // failure so attempt to extract and log this if possible
            string reason = null;
            if (loggingContext.Result is ObjectResult objectResult)
            {
                if (objectResult.Value is ApiErrorResponse errorResponse)
                {
                    var validations = errorResponse.Data.Validations != null ? string.Join(", ", errorResponse.Data.Validations) : string.Empty;
                    // Prefer logging validations as they should give better insight into why the request failed
                    reason = !string.IsNullOrEmpty(validations) ? validations : errorResponse.Data.Message;
                }
                else if (objectResult.Value is string message)
                {
                    // Handles those endpoints not following the standard error payload response format
                    reason = message;
                }
            }
            var errorMessage = $"{systemEventTypeId ?? "Request"} failed ({responseStatusCode}){(reason != null ? $": {reason}" : string.Empty)}";
            if (!errorMessage.EndsWith(".")) errorMessage += ".";
            return _logger.LogError(lastError, errorMessage);
        }
        else if (systemEventTypeId != null)
        {
            // Ignore logging successful completions of requests not mapped to a system event type
            _logger.LogInformation($"{systemEventTypeId} completed.");
        }

        return null;
    }

    private void PerformLogForContext(ApiLoggingContext contextToLog, ApiLoggingSettings settings)
    {
        if (contextToLog != null)
        {
            // the request, response has actually finished
            // log the results now
            if (settings?.LogToDatabase == true && !string.IsNullOrEmpty(settings?.ConnectionString))
            {
                Stopwatch databaseLoggingStopwatch = new Stopwatch();
                databaseLoggingStopwatch.Start();

                try
                {

                    var writer = new ApiLoggingDatabaseWriter();

                    writer.Log(settings.ConnectionString, contextToLog);
                }
                catch (Exception ex)
                {
                    contextToLog.AdditionalMessages.Add("An exception occured while logging the API operation to the database.");
                    contextToLog.Errors.Add(ex);
                    _logger.LogError(ex, "Error while logging API call to the database");
                }
                finally
                {
                    databaseLoggingStopwatch.Stop();
                    contextToLog.TimeToWriteDatabaseLogMilliseconds = databaseLoggingStopwatch.ElapsedMilliseconds;
                }
            }
        }
    }
}

恳请各位提供帮助。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 13:50:54