【问题标题】:How do I get client IP and browser info in Blazor?如何在 Blazor 中获取客户端 IP 和浏览器信息?
【发布时间】:2020-01-18 19:14:32
【问题描述】:

如何在 Blazor 服务器端获取客户端信息,例如 IP 地址和浏览器名称/版本?

【问题讨论】:

    标签: blazor blazor-server-side


    【解决方案1】:

    好吧,我今天早上遇到了这个问题,我为服务器端 Blazor 解决它的方法是创建一个类,然后您可以将其作为范围服务注入到您的 _host.cshtml 上,然后在您的任何位置访问它Blazor 组件,因为 Razor 页面已经支持此功能。

        public class BlazorAppContext
        {
            /// <summary>
            /// The IP for the current session
            /// </summary>
            public string CurrentUserIP { get; set; }
        }
    

    Startup.cs:

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
          ...
    
          services.AddScoped<BlazorAppContext>();
    
          ...
        }
    

    _host.cshtml:

    @inject IHttpContextAccessor httpContextAccessor
    @{
        BlazorAppContext.CurrentUserIP =   httpContextAccessor.HttpContext.Connection?.RemoteIpAddress.ToString();
    }
    

    您还可以尝试一种 Scoped 方法,然后您可以通过 DI 使用该方法。

    注释:

    如文档中所述,“Blazor WebAssembly 应用目前没有 DI 范围的概念。范围注册的服务的行为类似于单例服务。但是,Blazor 服务器托管模型支持范围生命周期。在 Blazor 服务器应用中,作用域服务注册的作用域是连接。因此,对于应该作用于当前用户的服务,最好使用作用域服务,即使当前意图是在浏览器中运行客户端。"

    希望对你有帮助。

    【讨论】:

    • static 字段如何处理多个用户?
    • 不能。在负载测试的一些不稳定的功能之后,我意识到这一点。我一直在为客户端 Blazor 使用静态类 sn-p,它可以正常工作,但是与服务器端相同的问题,虽然它确实适用于小型应用程序,但在使用时可能会导致不稳定的 IP 分配同时,要求 DI 为每个连接提供一个非静态字段到服务的新实例中。
    • 即使没有静态字段,这种方法对我也不起作用。该服务被初始化两次,即使它被注册为作用域。因此 _host.cshtml 在实例 A 上设置 IP,但随后任何后续组件都会获得一个带有空字段的新实例 (B)。 @Dmitry 的方法奏效了。
    • 这在 .NET 6 上对我不起作用。似乎 _host 使用的作用域对象与 blazor 使用的作用域对象不同。
    【解决方案2】:

    在 aspnetcore3.1 中这对我有用:

    1. 为保存所需信息制作特殊课程:
    public class ConnectionInfo
    {
        public string RemoteIpAddress { get; set; } = "-none-";
    }
    
    1. _Host.cshtml中创建实例并作为参数传递给App组件:
    @{
        var connectionInfo = new ConnectionInfo() 
        { 
            RemoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString() 
        };
    }
    ...
    <component type="typeof(App)" 
               render-mode="ServerPrerendered"
               param-ConnectionInfo="connectionInfo" />
    
    1. App.razor 中捕获并重新发布为CascadingValue
    <CascadingValue Value="connectionInfo">
      <Router AppAssembly="typeof(Program).Assembly">
          ...
      </Router>
    </CascadingValue>
    
    @code {
        [Parameter]
        public ConnectionInfo? connectionInfo { get; set; }
    }
    
    1. 在任何子页面/组件中获取CascadingParameter
    @code {
        [CascadingParameter]
        private ConnectionInfo? connectionInfo { get; set; }
    }
    

    这里唯一的问题是漫游用户 - 当用户更改他的 IP 地址并且 Blazor 没有“捕捉”这个(例如后台的浏览器选项卡)时,您将拥有旧的 IP 地址,直到用户刷新 (F5) 页面。

    【讨论】:

    • 这在 .NET 6 上对我不起作用。似乎 _host 使用的作用域对象与 blazor 使用的作用域对象不同。级联参数也不起作用。
    • 什么作用域对象不同? HttpContext?级联参数不起作用?您确定您的 blazor 应用程序正常吗?我的一个在 .net6 上运行良好。
    • 在尝试通过作用域对象或级联参数传递值,然后最终找到一个可行的解决方案后,我浪费了一天的大部分时间来确定为什么(这是工作时间!)。如果不出意外,显然我的 blazor 项目的结构与您的不同!
    【解决方案3】:

    以下是 2021 年在服务器端 Blazor for .NET 5 中的操作方法。

    请注意,我的解决方案只会为您提供 IP 地址,但使用我的解决方案应该很容易获得用户代理。

    我将在这里复制我的博客文章的内容:https://bartecki.me/blog/Blazor-serverside-get-remote-ip

    TLDR:

    您可以使用 JavaScript 调用您自己的公开端点,该端点将使用以下代码返回远程连接 IP:

    RemoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString() 
    

    ...如果您有反向代理服务器,则必须处理您的反向代理服务器,否则您只会获得反向代理的 IP 地址。

    或者您可以使用 JavaScript 调用外部端点,该端点将为您返回一个 IP 地址,但缺点是您必须配置 CORS,即使这样它也可能被一些广告拦截扩展程序阻止。

    详情:

    两种方法

    方法一:使用 JavaScript 调用外部服务

    优点:

    • 如果您使用的是 nginx、traefik 等反向代理,会稍微简单一些

    缺点:

    • 可能被外部扩展程序/广告拦截器拦截
    • 您必须配置 CORS
    _Host.cshtml
    <script>
      window.getIpAddress = () => {
        return fetch('https://jsonip.com/')
          .then((response) => response.json())
          .then((data) => {
            return data.ip
          })
      }
    </script>
    
    RazorPage.razor.cs
        public partial class RazorPage : ComponentBase
        {
            [Inject] public IJSRuntime jsRuntime { get; set; }
    
            public async Task<string> GetIpAddress()
            {
                try
                {
                    var ipAddress = await jsRuntime.InvokeAsync<string>("getIpAddress")
                        .ConfigureAwait(true);
                    return ipAddress;
                }
                catch(Exception e)
                {
                    //If your request was blocked by CORS or some extension like uBlock Origin then you will get an exception.
                    return string.Empty;
                }
            }
        }
    
    Startup.cs
            public void ConfigureServices(IServiceCollection services)
            {
                //code...
                services
                    .AddCors(x => x.AddPolicy("externalRequests",
                        policy => policy
                    .WithOrigins("https://jsonip.com")));
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                //code...
                app.UseCors("externalRequests");
            }
    

    方法 2:在我们的 Blazor 应用中公开一个端点并使用 JavaScript 调用它

    优点:

    • 您不必配置 CORS
    • 不会被扩展程序或广告拦截器拦截

    缺点:

    • 如果您使用的是 nginx、traefik 等反向代理,可能会稍微复杂一些。

    现在请注意,您将使用这种方法,因为如果您使用的是反向代理,那么您实际上会收到您的反向代理 IP 地址。 您的反向代理很可能已经在某种标头中转发了外部客户端的 IP 地址,但这取决于您自己的判断。

    示例:https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/

    InfoController.cs
        [Route("api/[controller]")]
        [ApiController]
        public class InfoController : ControllerBase
        {
            [HttpGet]
            [Route("ipaddress")]
            public async Task<string> GetIpAddress()
            {
                var remoteIpAddress = this.HttpContext.Request.HttpContext.Connection.RemoteIpAddress;
                if (remoteIpAddress != null)
                    return remoteIpAddress.ToString();
                return string.Empty;
            }
        }
    
    Startup.cs
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers(); //remember to map controllers if you don't have this line
                    endpoints.MapBlazorHub();
                    endpoints.MapFallbackToPage("/_Host");
                });
    
    _Host.cshtml
    <script>
      window.getIpAddress = () => {
        return fetch('/api/info/ipaddress')
          .then((response) => response.text())
          .then((data) => {
            return data
          })
      }
    </script>
    
    RazorPage.razor.cs
        public partial class RazorPage : ComponentBase
        {
            [Inject] public IJSRuntime jsRuntime { get; set; }
    
            public async Task<string> GetIpAddress()
            {
                try
                {
                    var ipAddress = await jsRuntime.InvokeAsync<string>("getIpAddress")
                        .ConfigureAwait(true);
                    return ipAddress;
                }
                catch(Exception e)
                {
                    //If your request was blocked by CORS or some extension like uBlock Origin then you will get an exception.
                    return string.Empty;
                }
            }
        }
    

    【讨论】:

    • 有没有办法为 asp.net 核心托管的 blazor Web 程序集执行此操作?
    • 当我将 jsRuntime 注入更改为类构造函数中的正常 DI 进程时,方法 #2 对我来说适用于 .NET 6。
    【解决方案4】:

    请注意,这仅指服务器端 Blazor

    “目前没有很好的方法来做到这一点。我们将研究 我们可以如何提供这些信息以供客户使用。”

    来源:Blazor dev at Github

    解决方法

    客户端向服务器发起 ajax 调用,然后服务器可以获取本地 ip 号。 Javascript:

    window.GetIP = function () {
        var token = $('input[name="__RequestVerificationToken"]').val();
        var myData = {}; //if you want to post extra data
        var dataWithAntiforgeryToken = $.extend(myData, { '__RequestVerificationToken': token });
        var ip = String('');
        $.ajax({
            async: !1, //async works as well 
            url: "/api/sampledata/getip",
            type: "POST",
            data: dataWithAntiforgeryToken,
            success: function (data) {
                ip = data;
                console.log('Got IP: ' + ip);
            },
            error: function () {
                console.log('Failed to get IP!');
            }
        });
        return ip;
    };
    

    后端(ASP.NET Core 3.0):

        [HttpPost("[action]")]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public string GetIP()
        {
            return HttpContext.Connection.RemoteIpAddress?.ToString();
        }
    

    请注意,这是不安全的,ipnumber 可能会被欺骗,所以不要用于任何重要的事情。

    【讨论】:

    • 感谢分享,暂时不用打猎了。我应该留下这个问题,因为他们将来会调查它,还是我现在把它标记为答案,因为它目前在技术上是正确的,但是当它改变时,这个问题将被埋没并且可能不会被拾起?
    • @jsmars 标记为答案,我会保持更新。我敢肯定有人会在修复后发表评论:)
    • @Sire,为什么要将您的答案标记为已接受?真的吗 ?你的回答对这个问题有什么价值?在 github 中提供指向我 (enetstudio) 打开的问题的链接,用户 Isaac 在此处显示其答案。顺便说一句,我相信我有这个问题的完整答案......
    • @enet 如果 Blazor 开发人员说目前没有好的方法,我相信这就是答案。但如果你有更正确的答案,请分享。
    • 它确实增加了开发人员说目前不可能的价值。如果您有更好的答案可以解决问题,或者其他任何人,我一定会更改该问题的答案,所以如果您有解决方案,请随时写一个。这将不胜感激! :)
    猜你喜欢
    • 2020-02-12
    • 2010-11-22
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多