首页 > 解决方案 > HttpClient 在 Blazor Webassembly 应用程序中不包含带有请求的 cookie

问题描述

我有一个带有用户服务的 Blazor Webassembly 应用程序,该服务旨在通过 API 检索用户的详细信息。该服务如下所示:

public class UserDataService : IUserDataService
{
    public readonly HttpClient _HttpClient;

    public UserDataService(HttpClient httpClientDI)
    {
        _HttpClient = httpClientDI;
    }

    public async Task<User> GetUserInfo()
    {
        try
        {
            return await _HttpClient.GetFromJsonAsync<User>("api/users/MyUserInfo");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
    }
}

该 API 专门设计用于从客户端请求中读取加密的 cookie。此 cookie 包含用户的电子邮件地址,用户信息服务使用此 cookie 来检索更详细的用户信息集。

[HttpGet("MyUserInfo")]
public User MyUserInfo()
{
    var myCookie = HttpContext.Request.Cookies.FirstOrDefault(c => c.Key == "MyCookie");

    var userMask = JsonConvert.DeserializeObject<AuthUserMask>(Protector.Unprotect(myCookie.Value));

    var user = UserService.Find(userMask.Email).FirstOrDefault();

    return user;
}

当我运行 Web 应用程序时,我能够验证 cookie 是否存在于浏览器中,但是当应用程序向 API 发出请求时,cookie 不包括在内。事实上,该请求根本不包括来自客户端的任何 cookie。

在此处输入图像描述

我对 Blazor 完全陌生,我不确定这种场景是否存在任何约定,但目前我只是想让这个新的 Web 应用程序与我们现有的服务一起工作。有没有办法确保包含 cookie?我可能做错了什么?

在此先感谢您的帮助。

编辑

这是创建 cookie 的代码。它是验证用户是否经过身份验证的更大方法的一部分,但这是相关部分:

{
    var userJson = JsonConvert.SerializeObject(new AuthUserMask()
    {
        Email = user.Email,
        isActive = user.IsActive
    });

    var protectedContents = Protector.Protect(userJson);

    HttpContext.Response.Cookies.Append("MyCookie", protectedContents, new CookieOptions()
    {
        SameSite = SameSiteMode.None,
        Secure = true,
        Path = "/",
        Expires = DateTime.Now.AddMinutes(60)
    });

    HttpContext.Response.Redirect(returnUrl);
}

编辑 2

在 UserDataService 中尝试了以下操作,看看会发生什么:

public async Task<User> GetUserInfo()
{
    try
    {
        _HttpClient.DefaultRequestHeaders.Add("Test", "ABC123");
        return await _HttpClient.GetFromJsonAsync<User>("api/users/MyUserInfo");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw;
    }
}

不幸的是,结果是一样的——当它到达 API 时,RequestCookieCollection 是完全空的。

标签: c#asp.net-corecookiesdotnet-httpclientblazor-webassembly

解决方案


基于@Mihaimyh 的一些见解,我能够使用用户数据服务上的自定义委托处理程序来实现这一点。它是这样注册的:

builder.Services.AddHttpClient<IUserDataService, UserDataService>(client => client.BaseAddress = new Uri("https://localhost:44336/"))
                .AddHttpMessageHandler<CustomDelegatingHandler>();

在内部,它使用JSInterop运行 Javascript 函数来检索 cookie,然后将其附加到使用该SendAsync()方法的所有传出请求:

public class CustomDelegatingHandler : DelegatingHandler
{
    private IJSRuntime JSRuntime;

    public CustomDelegatingHandler(IJSRuntime jSRuntime) : base()
    {
        JSRuntime = jSRuntime;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var cookie = await JSRuntime.InvokeAsync<string>("blazorExtensions.GetCookie", new[] { "MyCookie" });
        Debug.WriteLine($"My cookie: {cookie}");
        request.Headers.Add("MyCookie", $"{cookie}");
        return await base.SendAsync(request, cancellationToken);
    }
}

Javascript 函数看起来像这样(从W3Schools几乎逐字删除):

window.blazorExtensions = { 
    GetCookie: function (cname) {
        var name = cname + "=";
        var decodedCookie = decodeURIComponent(document.cookie);
        var ca = decodedCookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) == 0) {
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }
}

我还修改了服务端的内容,以在标头中查找 cookie 而不是 cookie 集合。现在,而不是这个...

var myCookie = HttpContext.Request.Cookies.FirstOrDefault(c => c.Key == "MyCookie");

...我已经这样做了:

HttpContext.Request.Headers.TryGetValue("MyCookie", out var myCookie);

我诚然不知道这如何与 Blazor 应用程序中此类事物的约定保持一致,但它似乎足以满足我们的目的。再次感谢大家的帮助。


推荐阅读