C# 在处理Blazor时,在WinForms中处理自定义标题的最佳方法是什么?

C# 在处理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获取。当用户在我们

我们目前正在为我们的客户创建一个使用SyncFusion的Blazor组件构建的web应用程序。此web应用程序的部分内容还打算用作WinForms应用程序中的可视化,WinForms应用程序本身非常有效。然而,就授权而言,我们目前面临一个大问题

当用户在Chrome/Firefox/etc中导航到“(我们的web应用)/客户端”时,他们会收到Auth0的登录屏幕,登录后会显示一个页面,该页面上有一个SyncFusion控件,显示与该用户相关的客户端列表,数据从我们的一个API获取。当用户在我们的WinForms应用程序中通过使用CefSharp浏览器打开显示同一页面的选项卡时,他们将自动登录,并跳过Auth0登录屏幕,因为我们已经知道令牌(因为启动WinForms应用程序时需要他们登录),并且在请求时将其作为标头传递

在WinForms应用程序中 Program.cs-启动WinForms应用程序时初始化Chrome

    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);
            }
        }