【问题标题】:Task Status always WaitForActivation using .NET Core使用 .NET Core 的任务状态始终 WaitForActivation
【发布时间】:2018-12-24 19:15:17
【问题描述】:

我知道这个问题可能已经被问过好几次了,但所提出的解决方案都没有奏效。

基本上,我有一个 .NET Core 解决方案,我想用它来实现 Identity Core。我有3个项目

  • 处理请求的 API 项目 (Web Api)
  • 所有业务逻辑的核心项目(类库)
  • 用于与 DB(类库)通信的 Dal 项目

所有设置都已正确完成,我已创建数据库并且初始种子已完成。我的问题是我无法登录,因为我的 LoginService 任务始终是 WaitForActivation。我的理解是我的代码中某处存在死锁,但我无法找到它。

LoginController 代码

[Route("api/[controller]")]
public class LoginController : Controller
{
    private readonly ILoginService _loginService;

    public LoginController(ILoginService loginService)
    {
        _loginService = loginService;
    }

    [AllowAnonymous]
    [HttpPost]
    public async Task<IActionResult> Login([FromBody] LoginDto login)
    {
        if (!ModelState.IsValid)
            return BadRequest("Email or password missing");

        var loginModel = Map(login);

        var result = await _loginService.SignInAsync(loginModel);

        return Ok();
    }

    private static LoginModel Map(LoginDto loginDto)
    {
        return new LoginModel
        {
            Email = loginDto.Email,
            Password = loginDto.Password,
            IsPersistent = loginDto.RememberMe
        };
    }
}

LoginService 接口

public interface ILoginService
{
    Task<SignInResult> SignInAsync(LoginModel model);
}

LoginService接口的实现

public class LoginService : SignInManager<User>, ILoginService
{
    public LoginService(UserManager<User> userManager,
        IHttpContextAccessor contextAccessor,
        IUserClaimsPrincipalFactory<User> claimsFactory,
        IOptions<IdentityOptions> optionsAccessor,
        ILogger<SignInManager<User>> logger,
        IAuthenticationSchemeProvider schemes)
        : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes)
    {
    }

    public async Task<SignInResult> SignInAsync(LoginModel model)
    {
        var result = await PasswordSignInAsync(model.Email, model.Password, model.IsPersistent, true);

        return result;
    }
}

我的创业班

public class Startup
{
    private const string CORS_POLICY_NAME = "CORS";

    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        Configuration = builder.Build();
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        var connectionString = Configuration.GetConnectionString("IdentityServerConnectionString");
        services.AddDbContext<IdentityServerDbContext>(options =>
        {
            options.UseSqlServer(connectionString);
            options.UseOpenIddict();
        });

        ConfigureCors(services);

        services.AddIdentity<User, IdentityRole>(o =>
            {
                o.Password.RequireDigit = true;
                o.Password.RequireLowercase = true;
                o.Password.RequireUppercase = true;
                o.Password.RequireNonAlphanumeric = true;
                o.Password.RequiredLength = 6;
                o.Lockout.MaxFailedAccessAttempts = 3;
                o.SignIn.RequireConfirmedEmail = true;
                o.User.RequireUniqueEmail = true;
            })
            .AddEntityFrameworkStores<IdentityServerDbContext>()
            .AddDefaultTokenProviders();

        // Register the OpenIddict services.
        services.AddOpenIddict()
            .AddCore(options =>
            {
                // Configure OpenIddict to use the Entity Framework Core stores and entities.
                options.UseEntityFrameworkCore()
                    .UseDbContext<IdentityServerDbContext>();
            })

            .AddServer(options =>
            {
                // Register the ASP.NET Core MVC binder used by OpenIddict.
                // Note: if you don't call this method, you won't be able to
                // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
                options.UseMvc();

                // Enable the token endpoint (required to use the password flow).
                options.EnableTokenEndpoint("/connect/token");

                // Allow client applications to use the grant_type=password flow.
                options.AllowPasswordFlow();

                // During development, you can disable the HTTPS requirement.
                options.DisableHttpsRequirement();

                // Accept token requests that don't specify a client_id.
                options.AcceptAnonymousClients();
            })

            .AddValidation();

        services.AddMvc(options =>
        {
            options.Filters.AddService(typeof(GlobalExceptionFilterAttribute));
            options.Filters.Add(new CorsAuthorizationFilterFactory(CORS_POLICY_NAME));
        });

        services.AddSingleton<GlobalExceptionFilterAttribute>();


        services.AddScoped<ILoginService, LoginService>();
        services.AddScoped<UserManager<User>>();
        services.AddScoped<UserStore<User>>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseCors(CORS_POLICY_NAME);
        app.UseAuthentication();
        app.UseMvc();

        SeedData.Initialize(serviceProvider);
    }

    private void ConfigureCors(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(CORS_POLICY_NAME, policy =>
            {
                policy.AllowAnyHeader();
                policy.AllowAnyMethod();
                policy.AllowAnyOrigin();

                if (Convert.ToBoolean(Configuration["AccessControlSettings:AllowCredentials"]))
                {
                    policy.AllowCredentials();
                }
                else
                {
                    policy.DisallowCredentials();
                }
            });
        });
    }
}

我希望这能提供足够的信息来找到解决方案。

【问题讨论】:

  • 您的PasswordSignInAsync 实现是什么样的?
  • 这不是我的实现,是SignInManager中实现的Identity方法

标签: c# asynchronous async-await .net-core task


【解决方案1】:

经过进一步调查,我终于找到了我的问题。代码没有问题。问题来自用户。我查看了数据库,创建的用户的 securityStamp 为 NULL,这似乎对身份有问题

对于那些对我的推理过程感兴趣的人来说,就是这样。我很确定在幕后的某个时候会发生异常,这对我能够访问堆栈跟踪有很大帮助。然后我稍微更改了代码,以便能够捕获我感兴趣的异常

try
        {
            var result = await Task.Run(() => _signInManager.PasswordSignInAsync(userName, password, isPersistent, true));

            return result;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }

实现的堆栈跟踪是不言自明的,很容易找到解决方案。我希望它对遇到同样问题的人有所帮助:)

【讨论】:

    猜你喜欢
    • 2018-05-18
    • 1970-01-01
    • 2017-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2017-01-15
    相关资源
    最近更新 更多