微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 ASP.NET Core 时,为什么 HttpContext.User.Identity.Name 对于相当于工作同步控制器的异步控制器显示为空?

如何解决使用 ASP.NET Core 时,为什么 HttpContext.User.Identity.Name 对于相当于工作同步控制器的异步控制器显示为空?

TLDR;我有几乎相同的控制器,它们仅通过使用 await/Task(当然还有 async)而大不相同。非异步版本按预期返回当前用户用户名,但等效的 Set-ExecutionPolicy 不会。

我想发现的是

  1. 这可能适用的情况
  2. 我可以使用这些工具(除了 fiddler)来调查这个问题

注意:我无法针对 ASP.NET Core 源进行调试,因为尚未授予运行 using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Baffled.API.Controllers { [Authorize] [ApiController] [Route("[controller]")] public class IdentityController : ControllerBase { private readonly string userName; public IdentityController(IHttpContextAccessor httpContextAccessor) { userName = httpContextAccessor?.HttpContext?.User.Identity?.Name; } [HttpGet] public IActionResultGetCurrentUser() { return Ok(new { userName }); } } } 的权限(这似乎是构建解决方案所必需的) >

这有效:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Baffled.Services;

namespace Baffled.API.Controllers
{
    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class IssueReproductionController : ControllerBase
    {
        private readonly HeartbeatService heartbeatService;
        private readonly ILogger<IssueReproductionController> logger;
        private readonly string userName;

        public IssueReproductionController(
            HeartbeatService heartbeatService,ILogger<IssueReproductionController> logger,IHttpContextAccessor httpContextAccessor)
        {
            this.heartbeatService = heartbeatService;
            this.logger = logger;

            userName = httpContextAccessor?.HttpContext?.User?.Identity?.Name;
        }

        [HttpGet]
        public async Task<IActionResult> ShowProblem()
        {
            var heartbeats = await heartbeatService.GetLatest();

            logger.Loginformation("Issue reproduction",new { heartbeats });

            var currentUser = userName ?? HttpContext?.User.Identity?.Name;

            return Ok(new { currentUser,heartbeats.Data });
        }
    }
}

不起作用

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.httpsys;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace Baffled.API
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((_,config) =>
                {
                    config.AddJsonFile("appsettings.json",true,true);
                    config.AddEnvironmentvariables();
                })
                .UseWindowsService()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseUrls("http://*:5002");
                    webBuilder.UseStartup<Startup>().Usehttpsys(Options);
                });

        private static void Options(httpsysOptions options)
        {
            options.Authentication.AllowAnonymous = true;
            options.Authentication.Schemes =
                AuthenticationSchemes.NTLM | 
                AuthenticationSchemes.Negotiate | 
                AuthenticationSchemes.None;
        }
    }
}

我认为配置是正确的,但为了完整起见,它包含在下面:

Program.cs

using System;
using System.Net.Http;
using Baffled.API.Configuration;
using Baffled.API.Hubs;
using Baffled.API.Services;
using Baffled.EF;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.httpsys;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Serialization;
using Serilog;
using Serilog.Events;

namespace Baffled.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<HeartbeatService,HeartbeatService>();

            services.AddHttpContextAccessor();
            services.AddAuthentication(httpsysDefaults.AuthenticationScheme).AddNegotiate();
            services.AddHttpClient("Default").ConfigurePrimaryHttpMessageHandler(() => new httpclienthandler { UseDefaultCredentials = true });
            services.AddControllers();

            var corsOrigins = Configuration.GetSection("CorsAllowedOrigins").Get<string[]>();

            services.AddCors(_ => _.AddPolicy("AllOriginPolicy",builder =>
            {
                builder.WithOrigins(corsOrigins)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
            }));

            services.AddSwagger();
            services.AddSignalR();
            services.AddHostedService<Worker>();
            services.AddResponseCompression();

            // Logging correctly configured here
        }

        public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
        {
            app.UseRouting();
            app.UseSwagger();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseResponseCompression();
            app.UseCors("AllOriginPolicy");

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<UpdateHub>("/updatehub");
            });
        }
    }
}

Startup.cs

PImage src;

int[][] grid = new int[2][2];
PGraphics[] pgs = new PGraphics[4];

void setup() {
  size(1080,1080);
  frameRate(5);
  src = loadImage("dog.jpg");
  src.resize(width,height);
  
          for (int i = 0; i < pgs.length; i++) { 
    pgs[i] = createGraphics(width,height);
  }
}
void draw() {
  background(255);
  //image(src,width,height);

  // Variables for the grid  
  int tilesX = grid.length;
  int tilesY = grid.length;  
  int tileW = int(width/tilesX);
  int tileH = int(width/tilesY);

  // Build the grid
  for (int y = 0; y < tilesY; y++) {
    for (int x = 0; x < tilesX; x++) {


      // These build the coordinates we copy from 
      int sx = x * tileW;
      int sy = y * tileH;
      int sw = tileW;
      int sh = tileH;

      // These build the coordinates we copy to

      int dx = grid[x][y];
      int dy = grid[x][y];
      int dw = tileW;
      int dh = tileH;       

      for (int i = 0; i < pgs.length; i++) {    
        pgs[i].beginDraw();
        pgs[i].copy(src,sx,sy,sw,sh,dx,dy,dw,dh);
        pgs[i].endDraw();
      }

      int selector = int(random(pgs.length));
      grid[x][y] = selector;
      push();
      translate(selector*tileW,selector*tileH);
      image(pgs[grid[x][y]],0);
      pop();
    }
  }
}

我已经坐了几个星期了,希望可能会出现解决方案。没有一个。请帮忙。

编辑

我通过 Windows 服务托管了这个。奇怪的行为似乎是不一致和不确定的。有时我会看到我的用户名被返回,但我的同事在遇到有问题的端点时从未这样做。

解决方法

您不应尝试在控制器构造函数中访问特定于 HttpContext 的数据。该框架不保证何时构造构造函数,因此很可能在 IHttpContextAccessor 访问当前 HttpContext 之前在此处创建构造函数。相反,您应该仅在您的控制器操作本身内访问特定于上下文的数据,因为它保证作为请求的一部分运行。

在使用 IHttpContextAcccessor 时,您应该始终仅在需要查看上下文的那一刻访问其 HttpContext。否则,很可能您正在使用一个过时的状态,这会导致各种问题。

然而,在使用控制器时,您实际上根本不需要使用 IHttpContextAccessor。相反,框架将为您提供多个属性,以便您可以直接在控制器上访问 HttpContext 甚至用户主体(在 Razor 页面和 Razor 视图中,有类似的属性可供使用):

请注意,这些属性在控制器操作中可用,而不是在构造函数中可用。有关详细信息,请参阅 this related post


以您的示例为例,您的控制器操作应该像这样工作:

[HttpGet]
public async Task<IActionResult> ShowProblem()
{
    var heartbeats = await heartbeatService.GetLatest();
    logger.LogInformation("Issue reproduction",new { heartbeats });

    var currentUser = User.Identity.Name;

    return Ok(new { currentUser,heartbeats.Data });
}
,

在 .NET Core 中,您无法直接从 HttpContext 检索用户身份。 您还需要使用依赖注入来获取对 http 上下文实例的引用。

这一直对我有用:

public class IssueReproductionController : ControllerBase
{
    private readonly HeartbeatService heartbeatService;
    private readonly ILogger<IssueReproductionController> logger;
    private readonly string userName;

    private readonly HttpContext context;

    public IssueReproductionController(
        HeartbeatService heartbeatService,ILogger<IssueReproductionController> logger,IHttpContextAccessor httpContextAccessor)
    {
        this.heartbeatService = heartbeatService;
        this.logger = logger;
        this.context = httpContextAccessor.HttpContext;
    }

    [HttpGet]
    public async Task<IActionResult> ShowProblem()
    {
       ...
       var currentUser = this.context.Request.HttpContext.User.Identity.Name;
       ...
    }
    ...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。