【问题标题】:ASP .NET Core webapi set cookie in middlewareASP .NET Core webapi 在中间件中设置 cookie
【发布时间】:2019-10-21 19:15:57
【问题描述】:

我正在尝试在操作执行后设置一个 cookie,努力让它发挥作用。如果我从控制器而不是从中间件设置它,我设法看到了 cookie。 我玩过配置的顺序,什么都没有。 代码示例来自一个干净的 webapi 创建项目,所以如果有人想玩它很简单,只需创建一个空 webapi,添加 CookieSet 类并将 Startup 类替换为下面的类(仅添加了 cookie 策略选项)

这是我的中间件

public class CookieSet
{
    private readonly RequestDelegate _next;

    public CookieSet(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        await _next.Invoke(context);
        var cookieOptions = new CookieOptions()
        {
            Path = "/",
            Expires = DateTimeOffset.UtcNow.AddHours(1),
            IsEssential = true,
            HttpOnly = false,
            Secure = false,
        };
        context.Response.Cookies.Append("test", "cookie", cookieOptions);
    }
}

我已经添加了 p 赋值并检查了执行是否永远不会到达那里,在 Cookies.Append 行它会停止执行,所以有些事情我无法弄清楚。

这是我的 Startup 课程

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    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)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            options.CheckConsentNeeded = context => false;
            options.MinimumSameSitePolicy = SameSiteMode.None;
            options.HttpOnly = HttpOnlyPolicy.None;
            options.Secure = CookieSecurePolicy.None;
            // you can add more options here and they will be applied to all cookies (middleware and manually created cookies)
        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseCookiePolicy(new CookiePolicyOptions
        {
            CheckConsentNeeded = c => false,
            HttpOnly = HttpOnlyPolicy.None,
            Secure = CookieSecurePolicy.None,
            MinimumSameSitePolicy = SameSiteMode.None,
        });

        app.UseMiddleware<CookieSet>();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMvc();
    }
}

我已将所有选项设置为最低要求,并使用 chrome 和 fiddler 进行了测试。

【问题讨论】:

    标签: asp.net-core cookies middleware


    【解决方案1】:

    好的,我是在自言自语,但这是为了社区...

    在深入研究 AspNetCore 代码后得到了这个工作。 基本上,cookie 必须在上下文响应的回调 OnStarting 上设置。 这是制造技巧的中间件的代码

    public class CookieSet
    {
        private readonly RequestDelegate _next;
        private readonly ASessionOptions _options;
        private HttpContext _context;
        public CookieSet(RequestDelegate next, IOptions<ASessionOptions> options)
        {
            _next = next;
            _options = options.Value;
        }
    
        public async Task Invoke(HttpContext context)
        {
            _context = context;
            context.Response.OnStarting(OnStartingCallBack);
            await _next.Invoke(context);
        }
    
        private Task OnStartingCallBack()
        {
            var cookieOptions = new CookieOptions()
            {
                Path = "/",
                Expires = DateTimeOffset.UtcNow.AddHours(1),
                IsEssential = true,
                HttpOnly = false,
                Secure = false,
            };
            _context.Response.Cookies.Append("MyCookie", "TheValue", cookieOptions);
            return Task.FromResult(0);
        }
    }
    

    AspNetCore 团队为此使用了一个内部类。

    查看SessionMiddleware类,部分代码如下(为了答题去掉了很多东西):

    public class SessionMiddleware
    {
        public async Task Invoke(HttpContext context)
        {
            // Removed code here
    
            if (string.IsNullOrWhiteSpace(sessionKey) || sessionKey.Length != SessionKeyLength)
            {
                            // Removed code here
                var establisher = new SessionEstablisher(context, cookieValue, _options);
                tryEstablishSession = establisher.TryEstablishSession;
                isNewSessionKey = true;
            }
    
            // Removed code here
    
            try
            {
                await _next(context);
            }
    
            // Removed code here
        }
    
        //Now the inner class
    
        private class SessionEstablisher
        {
            private readonly HttpContext _context;
            private readonly string _cookieValue;
            private readonly SessionOptions _options;
            private bool _shouldEstablishSession;
    
            public SessionEstablisher(HttpContext context, string cookieValue, SessionOptions options)
            {
                _context = context;
                _cookieValue = cookieValue;
                _options = options;
                context.Response.OnStarting(OnStartingCallback, state: this);
            }
    
            private static Task OnStartingCallback(object state)
            {
                var establisher = (SessionEstablisher)state;
                if (establisher._shouldEstablishSession)
                {
                    establisher.SetCookie();
                }
                return Task.FromResult(0);
            }
    
            private void SetCookie()
            {
                var cookieOptions = _options.Cookie.Build(_context);
    
                var response = _context.Response;
                response.Cookies.Append(_options.Cookie.Name, _cookieValue, cookieOptions);
    
                var responseHeaders = response.Headers;
                responseHeaders[HeaderNames.CacheControl] = "no-cache";
                responseHeaders[HeaderNames.Pragma] = "no-cache";
                responseHeaders[HeaderNames.Expires] = "-1";
            }
    
            // Returns true if the session has already been established, or if it still can be because the response has not been sent.
            internal bool TryEstablishSession()
            {
                return (_shouldEstablishSession |= !_context.Response.HasStarted);
            }
        }
    }
    

    【讨论】:

    • 这很有帮助。花几个小时试图了解为什么没有添加 cookie。谢谢!
    • 是的,我也花了很多时间,很高兴能帮上忙;)
    • 谢谢。我认为使用匿名函数而不是在字段中存储 http 上下文可能会更好。存储上下文可能会导致 ObjectDisposedException
    【解决方案2】:

    .NET 5

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // ........
        app.Use(async (context, next) =>
        {
            var cookieOptions = new CookieOptions()
            {
                Path = "/",
                Expires = DateTimeOffset.UtcNow.AddHours(1),
                IsEssential = true,
                HttpOnly = false,
                Secure = false,
            };
            context.Response.Cookies.Append("MyCookie", "TheValue", cookieOptions);
    
            await next();
        });
        // ........
    }
    

    【讨论】:

    • 如果您可以在控制器执行之前设置 cookie,则此方法有效。同样适用于 .net core 3.1。我需要一种在执行操作后立即设置 cookie 的方法,同时注入一些东西来获取一些数据
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 2019-09-27
    • 2018-06-19
    • 2020-03-09
    • 2019-02-26
    • 1970-01-01
    相关资源
    最近更新 更多