简体   繁体   English

无法创建 cookie,在 asp.net 核心(剃须刀页面)中获取“标题是只读的,响应已经开始”

[英]Can't create cookie, getting "Headers are read-only, response has already started" in asp.net core (razor pages)

I'm trying to set a cookie, but I get "Headers are read-only, response has already started."我正在尝试设置一个 cookie,但我得到“标题是只读的,响应已经开始。”

The method that write the cookie is in a property returning an User object in Utility.cs and Gallery.cshtml invokes the property ( img.UserVoteExists(_util.User) ) through the Utility object thats defined in ViewImports写入 cookie 的方法是在 Utility.cs 和 Gallery.cshtml 中返回 User 对象的属性中通过 ViewImports 中定义的 Utility 对象调用属性 ( img.UserVoteExists(_util.User) )

Utility.cs :实用程序.cs

public User User
{
    get
    {
        User result = new User();

        if (this.Context.Request.Cookies.ContainsKey(UserCookieName))
        {
            result = JsonConvert.DeserializeObject<User>(this.Context.Request.Cookies[UserCookieName]);
        }
        else
        {
            result = JsonConvert.DeserializeObject<User>(InvokePost("CreateUser", new CreateUserContextModel() { User = result }).Result);
        }

        this.Context.Response.Cookies.Append(UserCookieName, JsonConvert.SerializeObject(result), new CookieOptions() { Expires = DateTime.Now.AddDays(120) });

        return result;
    }
}

_ViewImports.cshtml : _ViewImports.cshtml

@inject IUtility _util

Gallery.cshtml :图库.cshtml

<div id="images" class="images-wrap list-small">
    @foreach (Image img in Model.CurrentItem.Images)
    {
        <div class="item-wrap" data-index="@img.Index" data-filename="@img.Filename">
            <img src="/IMAGES/Gallery/Items/@Html.Raw(Model.Name.ToCamelCase() + "/Thumb/" + img.Filename)" class="img-fluid item" />
            <div class="info-wrap">
                <div class="header-wrap">
                    <div class="vote-wrap">
                        <div class="circle clickable @Html.Raw(img.UserVoteExists(_util.User).AppendIfTrue("d-none"))">
                            <lc:fa name="thumbs-up" prefix="far" class="icon" />
                        </div>
                        <div class="circle @Html.Raw(img.UserVoteExists(_util.User).AppendIfFalse("d-none"))">
                            <lc:fa name="thumbs-up" prefix="fas" class="icon" />
                        </div>
                    </div>
                    <div class="caption-wrap">
                        <h2 class="caption">@img.Name</h2>
                    </div>
                </div>

                <p class="description-wrap">
                    <span class="caption">@Html.Raw(img.Description)</span>
                </p>
            </div>
        </div>
    }
</div>

By the time you have started rendering the view, it is probably too late, as the HTML for the view has to be rendered into the body, and the body comes after the headers in the HTTP stream.当您开始渲染视图时,可能为时已晚,因为视图的 HTML 必须渲染到正文中,并且正文位于 HTTP 流中的标头之后。 So if you're going to set something, probably best to do it in the controller before returning the view.因此,如果您要设置某些内容,最好在返回视图之前在控制器中进行设置。 One way to do it is to perform the check and set the cookie in a callback function, then provide the callback function to the HttpResponse's AddOnSendingHeaders method.一种方法是执行检查并在回调函数中设置 cookie,然后将回调函数提供给 HttpResponse 的AddOnSendingHeaders方法。

If you can't restructure your code, you can work around the issue by turning on response buffering , which allows random access to the HTTP response, hence allowing the headers to be written and rewritten as much as needed.如果您无法重组代码,您可以通过打开响应缓冲来解决此问题,它允许随机访问 HTTP 响应,从而允许根据需要写入和重写标头。 However, this isn't recommended;但是,不建议这样做; you really should be deciding whether to buffer your output based on other factors, such as performance.您确实应该根据其他因素(例如性能)来决定是否缓冲输出。

On a side note, retrieving a property's value should not have side effects-- certainly should not be setting a cookie (see Microsoft's guidance on the topic).附带说明一下,检索属性的值不应该有副作用——当然应该设置 cookie(请参阅Microsoft 关于该主题的指南)。

My problem here was that I was caching the HttpContext itself:我的问题是我正在缓存 HttpContext 本身:

private readonly HttpContext mContext;

public MyClass(IHttpContextAccessor contextAccessor)
{
    mContext = contextAccessor.HttpContext;
}

Since my DI container cached this class as a singleton, I was using the same HttpContext for every request.由于我的 DI 容器将此类缓存为单例,因此我对每个请求都使用相同的 HttpContext。 Oopsie.哎呀。

I changed my code to cache the accessor instead of the context itself, and added a wrapper property:我更改了代码以缓存访问器而不是上下文本身,并添加了一个包装器属性:

private readonly IHttpContextAccessor mContextAccessor;

public MyClass(IHttpContextAccessor contextAccessor)
{
    mContextAccessor = contextAccessor;
}

private HttpContext Context => mContextAccessor.HttpContext;

Probably obvious to most people, but I hope it helps someone!对大多数人来说可能很明显,但我希望它对某人有所帮助!

I added a method for reading and writing the cookie that gets called in the constructor of the Utility object.我添加了一个读取和写入在 Utility 对象的构造函数中调用的 cookie 的方法。

Utility.cs :实用程序.cs

private void SetUser()
{
    User user = new User();

    if (this.Context.Request.Cookies.ContainsKey(UserCookieName))
    {
        user = JsonConvert.DeserializeObject<User>(this.Context.Request.Cookies[UserCookieName]);
    }
    else
    {
        user = JsonConvert.DeserializeObject<User>(InvokePost("CreateUser", new CreateUserContextModel() { User = user }).Result);
    }

    this.Context.Response.Cookies.Append(UserCookieName, JsonConvert.SerializeObject(user), new CookieOptions() { Expires = DateTime.Now.AddDays(120) });
    this.User = user;
}

public User User { get; set; }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 blazor SignInAsync 有此错误“标头是只读的,响应已经开始。” - blazor SignInAsync have This Error 'Headers are read-only, response has already started.' ASP.Net Core 中间件无法设置异常状态码,因为“响应已经开始” - ASP.Net Core middleware cannot set status code on exception because "response has already started" 准ASP.NET Core中间件项目中的“响应已经开始”异常 - “Response has already started” exception in a bare-bones ASP.NET Core middleware project ASP.Net core JWT Bearer authentication with Keycloak StatusCode 无法设置,因为响应已经开始 - ASP.Net core JWT Bearer authentication with Keycloak StatusCode cannot be set because the response has already started 无法在选择列表中保存多个选择 - asp.net core web app razor pages - Can't save multiple selections in select list - asp.net core web app razor pages asp.net core razor pages project in visual code localhost:5001 return 这个站点无法访问 - asp.net core razor pages project in visual code localhost:5001 return This site can’t be reached 带有Razor页面的ASP.NET Core MVC(无法从其他模型返回对象) - ASP.NET Core MVC with Razor Pages (can't return object from a different model) ASP.NET Core Razor页面的路由 - Routing for ASP.NET Core Razor Pages Razor Pages 中的 Asp.net 核心本地化 - Asp.net core Localization in Razor Pages 将 ASP.NET Core Identity db 与 Razor Pages 应用程序中已创建的数据库集成 - Integrating ASP.NET Core Identity db with an already created database in a Razor Pages app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM