迁移至.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
相关产品推荐
相关产品推荐

