首页 > 解决方案 > PWA:强制 window.open 打开浏览器而不是 PWA

问题描述

我已经构建了一个带有角度前端的 ASP.NET Core 应用程序。Angular 应用程序具有@angular/pwa节点包设置,因此它是一个渐进式 Web 应用程序,可以安装在 android/windows 上,其行为类似于本机应用程序。

我已经使用 Microsoft.AspNetCore.Identity 设置了外部登录(Microsoft、Google、Facebook、Twitter)。从我的角度应用程序中,我打开了一个外部登录页面的弹出窗口:

  this.authWindow = window.open(`${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}`, null, 'width=600,height=400');

弹出窗口的 url 路由到我return Challenge()调用的 ASP.NET Core 端点,该端点返回特定外部提供程序(Microsoft、Google、Facebook、Twitter)的登录页面。

在 Windows 上的 Chrome 中,单击触发 window.open() 的按钮以打开带有外部登录页面的窗口。成功登录后,您将被重定向到回调页面,这是一个剃须刀页面,它向包含 Angular 应用程序的主窗口发送消息。正在处理消息并关闭弹出窗口。

问题

当我在 Chrome for Android 上使用该网站时,我可以将 PWA 安装为应用程序,这会在我的 android 主页上添加一个图标。当我打开 PWA 并单击按钮打开弹出窗口时,弹出窗口正在我的 PWA 的弹出窗口中打开,所以没有问题。

当我在 android 上打开 Chrome 并访问该网站时,在安装 PWA 时,window.open()调用不会为 Chrome 浏览器打开弹出窗口,而是尝试为 Progressive Web App 打开弹出窗口。由于是这种情况,PWA 内的弹出窗口无法在 Chrome 中通知网站成功登录(呃……)。

但是当没有安装 PWA 时,它window.open()可以正常工作并在 Chrome 本身中打开弹出窗口。

所以底线是,PWA 安装在 android 上。我希望能够window.open()在 Chrome 中从我的网站调用,并让它在Chrome 浏览器而不是 PWA 中打开弹出窗口。

我尝试过的事情

  1. 修改ngsw-config.json

    { ..., "navigationUrls": [ "/ ", "!/ / . ", "!/ / __ ", "!/ / __ / ", "!/web/v2/Account/connect/ / ", "!/web/v2/Account/add/ /**" ] }

  2. 打开窗口target='_system'

    this.authWindow = window.open( ${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}, '_system', 'width=600,height=400');

  3. 打开窗口target='_blank'

    this.authWindow = window.open( ${this.baseUrl}/web/v2/Account/${this.action}/${medium}/${this.platform}, '_blank', 'width=600,height=400');

  4. 打开有和没有baseUrl的窗口target='_blank',只是一个绝对路径。

    this.authWindow = window.open( /web/v2/Account/${this.action}/${medium}/${this.platform}, '_blank', 'width=600,height=400');

  5. 使用 ngsw 绕过

    this.authWindow = window.open( /web/v2/Account/${this.action}/${medium}/${this.platform}?ngsw-bypass=true, '_blank', 'width=600,height=400');

但是所有技巧似乎都表现相同,并且仍然在 PWA 中打开窗口。

标签: progressive-web-appswindow.open

解决方案


我最终创建了一个子域来托管我的外部登录端点(ExternalLogin、ExternalLoginCallback、AddExternalLogin、AddExternalLoginCallback):

[Controller]
[Route("web/v2/[controller]")]
public class AccountController : Controller
{
    private IAccountService accountService;
    public AccountController(IAccountService accountService)
    {
        this.accountService = accountService;
    }

    ...

    // GET: web/Account/providers
    [AllowAnonymous]
    [HttpGet("providers", Name = "web-v2-account-external-providers")]
    public async Task<ActionResult<IEnumerable<string>>> Providers()
    {
        var result = await accountService.GetProviders();
        return Ok(result);
    }

    // GET: web/Account/connect/{provider}
    [AllowAnonymous]
    [HttpGet("connect/{medium}/{provider}", Name = "web-v2-account-external-connect-challenge")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> ExternalLogin([FromRoute]string medium, [FromRoute]string provider)
    {
        var redirectUrl = Url.RouteUrl("web-v2-account-external-connect-callback", new { medium, provider });
        var properties = await accountService.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return Challenge(properties, provider);
    }

    // GET: web/Account/connect/{provider}/callback
    [HttpGet("connect/{medium}/{provider}/callback", Name = "web-v2-account-external-connect-callback")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> ExternalLoginCallback([FromRoute]string medium, [FromRoute]string provider)
    {
        try
        {
            var login_result = await accountService.PerfromExternalLogin();
            if (login_result.Status)
            {
                var model = new LoginResultVM
                {
                    Status = true,
                    Medium = medium,
                    Platform = login_result.Platform
                };
                return View(model);
            }
            else
            {
                var model = new LoginResultVM
                {
                    Status = false,
                    Medium = medium,
                    Platform = login_result.Platform,

                    Error = login_result.Error,
                    ErrorDescription = login_result.ErrorDescription
                };
                return View(model);
            }
        }
        catch (OtherAccountException otherAccountEx)
        {
            var model = new LoginResultVM
            {
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = otherAccountEx.Message
            };
            return View(model);
        }
        catch (Exception ex)
        {
            var model = new LoginResultVM
            {
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = "There was an error with your social login"
            };
            return View(model);
        }
    }

    // GET: web/Account/logins
    [Authorize]
    [HttpGet("logins", Name = "web-v2-account-external-logins")]
    public async Task<ActionResult<IEnumerable<string>>> GetExternalLogins()
    {
        var logins = await accountService.GetExternalLogins(User);
        return Ok(logins.Select(l => l.ProviderDisplayName));
    }

    // GET: web/Account/add/{provider}
    [Authorize]
    [HttpGet("add/{medium}/{provider}", Name = "web-v2-account-external-add-challenge")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> AddExternalLogin([FromRoute]string medium, [FromRoute]string provider)
    {
        var redirectUrl = Url.RouteUrl("web-v2-account-external-add-callback", new { medium, provider });
        var properties = await accountService.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return Challenge(properties, provider);
    }

    // GET: web/Account/add/{provider}/callback
    [Authorize]
    [HttpGet("add/{medium}/{provider}/callback", Name = "web-v2-account-external-add-callback")]
#if RELEASE
    [Host("external.mintplayer.com")]
#endif
    public async Task<ActionResult> AddExternalLoginCallback([FromRoute]string medium, [FromRoute]string provider)
    {
        try
        {
            await accountService.AddExternalLogin(User);
            var model = new LoginResultVM
            {
                Status = true,
                Medium = medium,
                Platform = provider
            };
            return View(model);
        }
        catch (Exception)
        {
            var model = new LoginResultVM
            {
                Status = false,
                Medium = medium,
                Platform = provider,

                Error = "Could not login",
                ErrorDescription = "There was an error with your social login"
            };
            return View(model);
        }
    }
}

在 PWA 中运行时,window.open 仍将在 PWA 内的嵌入式浏览器中打开链接,从浏览器运行时 window.open 仍将在新的浏览器窗口中打开链接(而不是在您的 PWA 中)。在这两种情况下,我仍然可以访问开启程序来发送消息(window.opener.postMessage)。


推荐阅读