C# 在处理Blazor时,在WinForms中处理自定义标题的最佳方法是什么?
我们目前正在为我们的客户创建一个使用SyncFusion的Blazor组件构建的web应用程序。此web应用程序的部分内容还打算用作WinForms应用程序中的可视化,WinForms应用程序本身非常有效。然而,就授权而言,我们目前面临一个大问题 当用户在Chrome/Firefox/etc中导航到“(我们的web应用)/客户端”时,他们会收到Auth0的登录屏幕,登录后会显示一个页面,该页面上有一个SyncFusion控件,显示与该用户相关的客户端列表,数据从我们的一个API获取。当用户在我们的WinForms应用程序中通过使用CefSharp浏览器打开显示同一页面的选项卡时,他们将自动登录,并跳过Auth0登录屏幕,因为我们已经知道令牌(因为启动WinForms应用程序时需要他们登录),并且在请求时将其作为标头传递 在WinForms应用程序中 Program.cs-启动WinForms应用程序时初始化ChromeC# 在处理Blazor时,在WinForms中处理自定义标题的最佳方法是什么?,c#,winforms,cefsharp,blazor-server-side,syncfusion,C#,Winforms,Cefsharp,Blazor Server Side,Syncfusion,我们目前正在为我们的客户创建一个使用SyncFusion的Blazor组件构建的web应用程序。此web应用程序的部分内容还打算用作WinForms应用程序中的可视化,WinForms应用程序本身非常有效。然而,就授权而言,我们目前面临一个大问题 当用户在Chrome/Firefox/etc中导航到“(我们的web应用)/客户端”时,他们会收到Auth0的登录屏幕,登录后会显示一个页面,该页面上有一个SyncFusion控件,显示与该用户相关的客户端列表,数据从我们的一个API获取。当用户在我们
private static void Main(string[] args)
{
//Program start up code
...
ChromiumBrowser.InitChromium();
...
}
ChromiumBrowser.cs-抓取正确的组件并设置ChromiumBrowser浏览器的设置
public static class ChromiumBrowser
{
public static void InitChromium()
{
//Set resolver to load correct assembly
...
LoadApp();
}
public static ChromiumWebBrowser GetBrowser(string url)
{
return new ChromiumWebBrowser(url);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void LoadApp()
{
var settings = new CefSettings();
settings.CefCommandLineArgs.Add("--disable-gpu-compositing");
//Load correct assembly for x86/x64
...
Cef.Initialize(settings, false, browserProcessHandler: null);
}
...
}
UBrowser.cs-用于显示ChromiumWebBrowser的WinForms控件。添加自定义RequestHandler以添加带有授权令牌的标头
public UBrowser(string token)
{
InitializeComponent();
_chromiumBrowser = ChromiumBrowser.GetBrowser(null);
_chromiumBrowser.RequestHandler = new BearerAuthRequestHandler(token);
Controls.Add(_chromiumBrowser);
_chromiumBrowser.Dock = DockStyle.Fill;
}
public void NavigateTo(string url)
{
_chromiumBrowser.Load(url);
}
BearerAuthRequestHandler.cs-自定义RequestHandler。很遗憾,WebSocket请求未引发OnBeforeResourceLoad
public class BearerAuthRequestHandler : RequestHandler
{
private readonly string _token;
public BearerAuthRequestHandler(string token)
{
_token = token;
}
protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator,
ref bool disableDefaultHandling)
{
if (!string.IsNullOrEmpty(_token))
{
return new CustomResourceHandlerFactory(_token);
}
else return base.GetResourceRequestHandler(chromiumWebBrowser, browser, frame, request, isNavigation, isDownload, requestInitiator, ref disableDefaultHandling);
}
}
internal class CustomResourceHandlerFactory : ResourceRequestHandler
{
private readonly string _token;
public CustomResourceHandlerFactory(string token)
{
_token = token;
}
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
{
if (!string.IsNullOrEmpty(_token))
{
var headers = request.Headers;
headers["Authorization"] = $"Bearer {_token}";
request.Headers = headers;
return CefReturnValue.Continue;
}
else return base.OnBeforeResourceLoad(chromiumWebBrowser, browser, frame, request, callback);
}
}
在Web应用程序中:
客户端.razor-带有SyncFusion控件的razor页面。由于RenderMode.ServerPrerendered,OnInitializedAsync会触发两次。一次当页面在预呈现时以原始HTML显示,一次当SyncFusion控件触发Blazor websocket请求以完全呈现。如果页面的RenderMode设置为static,则仅会触发第一个OnInitializedAsync,但SyncFusion控件不会加载,因为Blazor未标记为运行。如果页面的RenderMode设置为server,则预呈现不会完成,因此用户的页面“加载”速度会变慢,并且只会触发第二个OnInitializedAsync
@page "/clients"
@attribute [Authorize]
@using Microsoft.AspNetCore.Routing
@using Microsoft.AspNetCore.Mvc.Localization
@using System.Text
@using OurWebApp.ViewModels
@using Syncfusion.Blazor.Grids
@using Syncfusion.Blazor.Navigations
@inject IApiConnector apiConnector
@inject NavigationManager navigationManager
@inject LinkGenerator linkGenerator
<div class="layout">
<div id="list-container">
<SfListView CssClass="e-list-template all" DataSource="@Clients">
<ListViewFieldSettings Id="ID" Text="Name"></ListViewFieldSettings>
<ListViewTemplates TValue="ClientViewModel">
<Template>
@{
<div class="e-list-wrapper e-list-avatar e-list-multi-line">
<span class="e-avatar sf-icon-customer" style="font-size:16px; background:none;color:black"></span>
<span class="e-list-item-header">@context.Name</span>
</div>
}
</Template>
</ListViewTemplates>
</SfListView>
</div>
</div>
//Style
...
@code {
IList<ClientViewModel> Clients = new IList<ClientViewModel>();
protected async override Task OnInitializedAsync()
{
Clients = (await apiConnector.GetClients()).Clients.ToList() ?? new List<ClientViewModel>();
}
}
CEF通常似乎没有一种方法来处理websocket请求开箱即用(),Blazor就是这样告诉页面在通过SyncFusion控件调用后呈现自己的。因此,报头丢失,似乎没有任何事件允许我在客户端截获此请求以重新提供报头。所说的线程建议了一个WebSoCultAgent服务器,但是CefServer是C++,而不是C语言,而且我认为如果你是自己托管WebSoSt的那个人,这是很有用的,除非我误解了套接字是如何工作的,否则我相信这不是事实。
无论哪种方式,我的问题都是显而易见的:我如何为Blazor的websocket请求重新提供头部,以便我们的API能够得到正确授权?如果这在任何方面都不可能,有什么可能的解决办法?任何涉及在初始HTML预呈现和websocket Blazor呈现之间通过某种服务保存api的令牌或返回值的操作都是可能的,但这感觉很粗略,并且不再是无状态的,这在处理api身份验证令牌时不太理想,不是吗
另一方面,这可以通过使用一个名为ModHeader的附加组件在Chrome中进行模拟。添加一个令牌并将过滤页面设置为仅向“(我们的web应用)/客户端”提供标题会导致如上所述的情况。这里有趣的是,不过滤页面允许ModHeader向任何请求提供标题。包括Blazor的websocket请求。很明显,Chrome必须有某种方法来创建一个标题,不管它是如何提供的,这样这个附加组件就可以像它那样工作,所以问题是Chrome也可以吗?如果是这样的话,如果认为没有解决办法,是否可以扩展CefSharp以公开该功能
我想勾勒出我们项目中正在发生的事情的全部范围,但是上面的代码仍然被大量删减。我希望我没有错过任何了解情况的必要信息。您需要使用标题吗?你能用饼干代替吗?您可以使用then以编程方式添加cookie,而不是读取blazor代码中的标头读取cookie值?是否需要使用标头?你能用饼干代替吗?您可以使用then以编程方式添加cookie,而不是读取blazor代码中的标头读取cookie值?
public ApiConnector(IHttpContextAccessor context, IAuthenticationSchemeProvider authenticationSchemeProvider)
{
bool bearerAuthentication = context.HttpContext.Request.Headers.Any(h => h.Key.Equals("Authorization"));
client.BaseAddress = new Uri(/*our API uri*/);
if (bearerAuthentication)
{
//If a "Bearer" scheme token exists in the headers, use it for authenticating the API. Auth0 was skipped.
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", context.HttpContext.GetTokenAsync("Bearer", "access_token").Result);
}
else
{
//Use the token supplied by a standard Auth0 log-in to authenticate the API.
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", context.HttpContext.GetTokenAsync("access_token").Result);
}
}