使用 angular-cli 在通用渲染中使用 cookie

Posted

技术标签:

【中文标题】使用 angular-cli 在通用渲染中使用 cookie【英文标题】:Using cookies in Universal Rendering using angular-cli 【发布时间】:2018-01-26 18:12:42 【问题描述】:

我使用 angular-cli 制作了一个 Angular 应用程序,并按照给定的here 的步骤在其中进行了通用渲染。但有人提到,如果我正在做通用渲染,我不能在我的应用程序中使用窗口和文档等全局变量,我如何在前端设置 cookie 以维护用户会话。以前我使用的是 JWT,但为此我需要在浏览器中设置 cookie。

【问题讨论】:

【参考方案1】:

注意:以下所有信息仅在您使用带有 Express 的 Node 来动态呈现(在请求到达服务器时)您的 Angular 应用程序时才有效在服务器上。我还没有研究预渲染。我不知道如果你使用 Express 以外的框架,比如 Hapi 或其他任何其他框架,你会怎么做。我只能猜测@nguniversal 也应该为这些人提供解决方案,但还没有研究过。

话虽如此,以下是我刚刚设法在我的应用程序中提供 cookie 的方式,当它在服务器端呈现时:

app.server.module.ts:

// ...
import  Request  from 'express';
import  REQUEST  from '@nguniversal/express-engine/tokens';

@Injectable()
export class RequestCookies 
    constructor(@Inject(REQUEST) private request: Request) 

    get cookies() 
        return !!this.request.headers.cookie ? this.request.headers.cookie : null;
    


@NgModule(
    imports: [
        // ...
    ],
    providers: [
        CookieService,
         provide: 'req', useClass: RequestCookies 
    ],
    bootstrap: [AppComponent]
)
export class AppServerModule 

app.module.ts:(有些人可能称之为app.browser.module.ts

@NgModule(
    declarations: [
        // ...
    ],
    imports: [
        // ...
    ],
    providers: [
        CookieService,
        
            provide: 'req',
            useValue: null
        
    ],
    bootstrap: [AppComponent]
)
export class AppModule 

cookie.service.ts:

import  Inject, Injectable  from '@angular/core';


@Injectable()
export class CookieService 
    private cookieStore = ;

    constructor(
        @Inject('req') private readonly req: any,
    ) 
        if (this.req !== null) 
            this.parseCookies(this.req.cookies);
         else 
            this.parseCookies(document.cookie);
        
    

    public parseCookies(cookies) 
        this.cookieStore = ;
        if (!!cookies === false)  return; 
        let cookiesArr = cookies.split(';');
        for (const cookie of cookiesArr) 
            const cookieArr = cookie.split('=');
            this.cookieStore[cookieArr[0]] = cookieArr[1];
        
    

    get(key: string) 
        return !!this.cookieStore[key] ? this.cookieStore[key] : null;
    

any.component.ts:

// ...
constructor(
    private cookieService: CookieService
) 

needMyCookie() 
    const userCookie = this.cookieService.get('user');

确保你> npm install express @nguniversal/express-engine

附:我首先查看了这个 repo:https://github.com/angular/universal-starter/tree/master/cli 用于服务器端渲染,我自己只做了 cookie 部分(不包括在那里)。 该回购遵循您链接中的普遍故事,但超出了范围。如果您只是遵循普遍的故事,它会说:

暂不支持延迟加载

但是,如果您尝试我提供链接的 repo,您​​会发现延迟加载的模块也在服务器上被渲染!不知道关键是什么,可能和server.js文件和@nguniversal/module-map-ngfactory-loader中的代码有关

我已经尝试过: CLI 版本 1.4.2 和 Angular 版本:4.4.1

这是我在浏览器中禁用 javascript 后得到的结果:

我的轮播项目是这样间隔的,因为它们实际上已缩放到 90%。我在 onInit 中的项目上有一个 zoomIn 动画。在服务器渲染视图上,我将它们强制设置为 90%,这样它们在第一次加载时不会以 100% 显示,然后在客户端启动后,让它们以 90 到 100 的动画速度显示。

此外,如果有人遇到此答案并注意到屏幕截图中对视口宽度的引用,在主导航链接下方的代码块中 - 如果他们想知道 - 这是我的WindowService

import  Injectable, PLATFORM_ID, Inject  from '@angular/core';
import  isPlatformBrowser, isPlatformServer  from '@angular/common';

@Injectable()
export class WindowService 

    constructor(
        @Inject(PLATFORM_ID) private readonly platformId: any,
    ) 

    get width() 
        if (isPlatformBrowser(this.platformId)) 
            return window.innerWidth;
         else 
            return 'Not available server-side!';
            // return null;
        
    


只需将其添加到app.module.tsapp.server.module.ts 中的providers: [] 数组即可。

这里需要注意的另一件事是我将Node与Express一起使用。如果您使用其他框架,例如 Hapi 或其他任何其他框架,我不知道这样做。

【讨论】:

不错的实现,但我收到错误:InjectionToken REQUEST 没有提供者你对此有什么想法吗? @OkanAslankan 您在某处可以公开访问代码吗?就我而言,它仍然像我在上面发布的代码一样工作。唯一的区别是我将@Injectable() 装饰器添加到RequestCookies 类中(我也更新了答案)......但除非你想直接将该类注入另一个服务,否则不需要这样做,而且我认为错误听起来会有所不同。 另外,您可以查看这个示例,也许它会有所帮助:github.com/patrickmichalina/fusebox-angular-universal-starter/… 浏览 Patrick 的示例也帮助了我几次,并意识到了一些我什至没有想到的事情。 我通过在构造函数中手动注入它来解决它。在 github 上看到了一些关于这个问题的问题。谢谢 我试过了,还是不行,这里是repo github.com/intergleam/universal-starter/tree/…【参考方案2】:

我已经成功使用了 npm 包 ng2-cache(我使用的是 Angular 4,它工作得很好)。我正在使用本地存储,所以我将它放在我的模块提供程序数组中,如下所示:

providers: [
    CacheService,
    provide: CacheStorageAbstract, useClass: CacheLocalStorage,
    ...
]

另一种选择是使用 isPlatformBrowser(),如 here 所示。

最丑陋的选择是将处理 cookie 的代码放在 try-catch 语句中。这样它就可以在浏览器上运行,而在服务器端被忽略。

【讨论】:

就我个人而言,我还没有尝试过 ng2-cache。但我认为忽略服务器端的 cookie 不应该是一种选择。大多数情况下,我们需要从 cookie 中获取至少一个令牌,以便在来自服务器的第一个渲染中获得授权和/或显示基于用户的数据。 那么目前认证令牌的选项是什么? 自从之前提出这个问题以来,服务器端渲染已经大大扩展。有许多文档化的解决方案说明如何使其工作,但这实际上取决于您的堆栈。 能否请您修改github.com/angular/universal-starter 以显示如何使用cookie?

以上是关于使用 angular-cli 在通用渲染中使用 cookie的主要内容,如果未能解决你的问题,请参考以下文章

为开发设置 Angular 通用应用程序

您必须在 angular-cli 项目中才能在重新安装 angular-cli 后使用 build 命令

如何在 iis 上部署 angular-cli 应用程序

如何使用角度通用在服务器端渲染中加载 json

使用通用 PasswordChangeForm Django 视图渲染时捕获 NoReverseMatch

仅适用于某些路线的角度通用渲染