c# - 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 是完全空的。
解决方案
基于@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 应用程序中此类事物的约定保持一致,但它似乎足以满足我们的目的。再次感谢大家的帮助。
推荐阅读
- angular - 刷新后角度路由数据为空或在浏览器中输入直接 url
- json - 将 JSON 从 Unity 应用程序传递到 Firebase 函数,onCall 结果未定义
- reactjs - Redux saga 在 DOM 事件中按顺序调度多个动作
- android - 自 9 月 1 日以来的 android 10 `SSL 握手超时`
- android-layout - Xamarin.Android 我如何获得从代码生成的元素的位置和边距?
- java - 在 Android 9 中不播放声音
- go - 使用指针算法访问数组元素
- html - 如何根据每个无序列表中的列表项的数量在 Jquery 中隐藏按钮元素
- python - 在 Pandas 数据框中实现队列
- python - 第 26 行,在
press = pygame.key.get_pressed() pygame.error: 视频系统未初始化