[小黄书管理平台]登录服务的实现(代码片段)

天地会珠海分舵 天地会珠海分舵     2022-12-04     697

关键词:

上一章我们重新定制了登录页面,且学习了angularjs通过模型驱动表单,更好的对表单以及表单下面的控件进行控制。

这一章我们会开始动手写代码,实现小黄书管理平台的登录功能。其中涉及以下几个点:

  • 支持es6 async/await特性
  • 使用Logger
  • 实现promisify化的HTTP请求服务
  • 实现登录服务
  • 调用登录服务进行登录

1. 支持es6 async/await特性


ES6增加了很多很好的特性方便我们更快更好的编写JS代码,比如async/await就能让我们很好的避免回调地狱(callback hell)。

ng2-admin默认没有支持上es6,所以我们需要对TypeScript做一些配置上面的修改,让其支持上es6。

我们项目中的TypeScript配置文件是项目根目录下的tsconfig.webpack.json,我们先看下期中的编译选项:

  "compilerOptions": 
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "noEmit": true,
    "noEmitHelpers": true,
    "strictNullChecks": false,
    "lib": [
      "es2015",
      "dom"
    ],
    "typeRoots": [
      "node_modules/@types"
    ]
  ,

我们可以看到里面并没有提供对es6的支持,所以,我们需要修改如下:

  "compilerOptions": 
    "declaration": false,
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "suppressImplicitAnyIndexErrors": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "noEmit": true,
    "noEmitHelpers": true,
    "strictNullChecks": false,
    "lib": [
      "es6",
      "dom"
    ],
    "typeRoots": [
      "node_modules/@types"
    ]
  ,

然后重新运行npm start进行编译和运行,编译和运行没有问题的话,代表es6已经可以支持上,我们就可以在实现代码时通过es6的方式来实现了。

2. 使用Logger


平时,我们贪图调试的方便,往往直接就console.log进行打印。但是在真实项目中,我们往往需要更好的日记库来做更好的记录日记。比如angular2-logger就是一个很不错的库,可以让我们很简单的记录不同层级的日记。

https://github.com/code-chunks/angular2-logger

首先我们需要安装:

npm install --save angular2-logger

因为我们在整个应用上都会用到这个logger,所以我们在app.module.js中,引入这个logger模块。

import DEBUG_LOGGER_PROVIDERS from "angular2-logger/core"; //DEBUG LEVEL以上的信息都打印出来
...
@NgModule(
  bootstrap: [App],
  declarations: [
    App
  ],
  imports: [ // import Angular's modules
    BrowserModule,
    HttpModule,
    RouterModule,
    CookieModule.forRoot(),
    FormsModule,
    ReactiveFormsModule,
    NgaModule.forRoot(),
    NgbModule.forRoot(),
    PagesModule,
    routing
  ],
  providers: [ // expose our Services and Providers into Angular's dependency injection
    ENV_PROVIDERS,
    APP_PROVIDERS,
    DEBUG_LOGGER_PROVIDERS,
    HttpService
  ]
)

export class AppModule 
  ...

这里我们直接从angular2-logger/core中import的是DEBUG_LOGGER_PROVIDERS这个provider,目的是让运行时的应用把所有通过logger打印出来的DEBUG层级以上的日记都打印出来。

这里有多个providers可以选择

  ERROR_LOGGER_PROVIDERS
  WARN_LOGGER_PROVIDERS
  INFO_LOGGER_PROVIDERS
  DEBUG_LOGGER_PROVIDERS
  LOG_LOGGER_PROVIDERS
  OFF_LOGGER_PROVIDERS

最后,我们就可以像下面这个例子那样通过这个日记服务来写log了:


import  Logger  from "angular2-logger/core";
...
 @Component(
 	...
 )
 export class AppComponent()
 	constructor( private _logger: Logger )
 		this._logger.error('This is a priority level 1 error message...');
 		this._logger.warn('This is a priority level 2 warning message...');
 		this._logger.info('This is a priority level 3 warning message...');
 		this._logger.debug('This is a priority level 4 debug message...');
 		this._logger.log('This is a priority level 5 log message...');
 	
 

3. 实现promisify化的HTTP请求服务


“@angular/http”库提供了很多模块让我们去实现HTTP请求。但是,我们如果每次调用一个请求的时候都需要重复的去整理各种请求参数选项,然后再发出对应的请求,代码会有太多的冗余,不利于扩展。

所以,我们需要将HTTP请求进行进一步封装,封装成一个简单的request调用API。

毕竟,我们在请求一个RESTFUL API时,变化的往往只是:

  • 请求类型. 是POST还是GET等
  • 请求哪个API
  • 请求参数

当前我们先不考虑url中带有querystring参数的情况,先考虑数据参数都是通过body进行传输的情况。

实现代码如下:

/**
 * Created by kevinzhu on 03/05/2017.
 */

import Injectable, Inject from '@angular/core';
import Http, Response, RequestOptions, Request, Headers from '@angular/http';
import 'rxjs/add/operator/toPromise';

const APIURL = 'http://localhost:3000/v1';

@Injectable()
export class HttpService 
  constructor(private http: Http)

  private getHeader = () => 
    const headers = new Headers();
    headers.append("Content-Type", 'application/json');
    return headers;
  ;

  public request = async (req) => 
    const baseUrl = APIURL;
    const requestOptions = new RequestOptions(
      method: req.method,
      url: baseUrl + req.api,
      headers: req.header ? req.header : this.getHeader(),
      body: JSON.stringify(req.body)
    );

    return await this.http.request(new Request(requestOptions)).toPromise();

  ;


可以看到代码里面用到了好几个es6的特性,比如async/awai方法,让我们的代码更简洁。

代码的大概意思就是:

  • 设置我们的请求要发往的URL
  • 在服务的构造函数中注入Http服务, 我们在app.module.ts中已经注入HttpModule模块,所以我们这里可以直接使用
  • 封装一个getHeader方法来设置HTTP请求的头,设置发送内容类型为json格式
  • 实现request请求api:注意方法里面的async,代表这是一个异步调用。调用者可以在调用该函数时前面加上await来等待该函数在执行完成后返回。且返回应该是一个promise对象。
    • 配置Http请求选项RequestOptions:
      • method: 请求方式’POST’等,调用者调用request方法时需要提供该参数
      • url: 请求API的目标地址
      • header:HTTP头,调用者有提供的话就用调用者提供的,没有的话就通过上面的getHeader来获取
      • body:发送给服务器端的请求参数,一般在POST中会用到
    • 通过调用http.request方法来发送请求到服务器。注意这里使用了rxjs库的toPromise方法,将http.request请求promisify化,这样我们就可以结合await来等待请求的完成,再将一个promise对象返回给上层调用函数。

这里的rxjs据说是一个很强大的库,我个人是没有什么研究。暂时只是知道有这个用法而已,其他的今后看情况研究吧。

4.1 实现登录服务


和我们实现HTTP的服务的原因一样,为了方便调用,我们需要对登录相关的API封装成一个服务,比如login,logout等。

4.1. 注入HttpService和Logger服务

首先,我们在loginService中的构造函数中注入了我们上面实现的httpService服务和Logger,我们在app.module.ts中已经在Provider中引入了这些服务。

/**
 * Created by KevinZhu on 03/05/2017.
 */

import  Injectable     from '@angular/core';
import  HttpService  from '../../http.service';
import  Logger  from "angular2-logger/core";

@Injectable()
export class LoginService 

  private logoutApi = '/auth/logout';  // URL to web api
  private loginApi = '/auth/login';

  constructor(private httpService: HttpService,
              private log: Logger)  ;

  ...

同时,我们定义了两个api,我们在登录和登出的时候,需要作为参数来传给httpService的request,这个函数我们在上面已经看过。

4.2. 实现登录API

注入了httpService服务之后,我们就可以调用其提供的request方法来和服务器进行交互了,比如,进行login操作。

和request这个方法对应,我们的login也需要使用async这个关键字,这样我们才能在调用request方法前使用await来等待其返回。

async login(name: String, password: String): Promise<any> 
    const body = 
      name: name,
      password: password,
      user_type: 'platform'
    ;
    const response = await this.httpService.request( method: 'POST', body, api:this.loginApi );
    this.log.debug('response:',response);

    const res = JSON.parse(response._body);
    const accessToken = res.access_token;
    this.log.debug('accessToken:', accessToken);

    return response;
  
  • login方法接收两个参数,就是我们登录用的用户名和密码
  • 我们需要将这两个参数和user_type一起,组合成一个数据结构来作为body发送给小黄书后台服务器的login接口。如果不解的这个接口的实现的,请查看前面小黄书服务器相关的章节
  • 最后我们按照request方法的参数要求,将登陆请求通过调用request方法来进行登录操作
  • 最后将登陆的promisify化的结果,返回给调用者。我们会在login.componet.js中对login这个接口进行调用,然后进一步处理返回的结果。

4.3. 将访问凭证保存到cookie中

登录之后,我们将会获得访问凭证access_token。我们需要将其保存到cookie中,以便今后其他服务进行调用。

所以我们需要注入在app.module.ts中登记的CookieService服务,然后从登录返回结果中取出访问凭证,并保存到cookie中。

/**
 * Created by KevinZhu on 03/05/2017.
 */

import  Injectable     from '@angular/core';
import  HttpService  from '../../http.service';
import  CookieService  from 'ngx-cookie';
import  Logger  from "angular2-logger/core";

@Injectable()
export class LoginService 

  private logoutApi = '/auth/logout';  // URL to web api
  private loginApi = '/auth/login';

  constructor(private httpService: HttpService,
              private cookieService:CookieService,
              private log: Logger)  ;

  async login(name: String, password: String): Promise<any> 
    const body = 
      name: name,
      password: password,
      user_type: 'platform'
    ;
    const response = await this.httpService.request( method: 'POST', body, api:this.loginApi );
    this.log.debug('response:',response);

    const res = JSON.parse(response._body);
    const accessToken = res.access_token;
    this.log.debug('accessToken:', accessToken);

    this.cookieService.put("access_token", accessToken);
    this.log.debug('access_token from cookie:',this.cookieService.get("access_token"));

    return response;
  
...

在获取到访问凭证之后,我们会调用cookieService.put的方法将访问凭证保存到cookie中。

同时,为了验证结果,我们会使用cookieService.get方法将上面保存的访问凭证取出来,并打印出来进行验证。

4.4. 登出

有登录,必然就有登出。我们会在登录服务的实现文件中,将登录方法一并给封装起来。

  async logout(): Promise<any> 
    const response = await this.httpService.request(method: 'POST', api: this.logoutApi);
    this.log.debug('response:',response);
    this.cookieService.remove("access_token");
    return response;
  

在登出之后,我们需要同时通过cookeService.remove方法,将login时保存的access_token给从cookie中删除掉。

5. 通过登录页面进行登录


实现了登录服务之后,我们就可以轻松的在login.component.ts中,调用登录服务提供的方法来进行登录了。

但是,开始之前我们先要了解下angular2-toaster这个库。因为我们需要用它,来再我们登录失败时显示相应的一些提示。

5.1. 使用Toaster

详细使用方式,请查看官方README:
https://github.com/stabzs/Angular2-Toaster

5.1.1 安装依赖包

根据文档的描述,使用Angular2-Toaster的前提是,必须先安装animations的支持。且,经过实践,我们必须指定4.01的版本。

npm install @angular/animations@4.0.1 --save

同时,安装Angular2-Toaster:

npm install angular2-toaster --save

5.1.2. 引入BrowserAnimations模块

安装完“@angular/animations”模块之后,我们要在根模块app.module.ts中将BrowserAnimationsModule给import进来。

import BrowserAnimationsModule from '@angular/platform-browser/animations';
...

/**
 * `AppModule` is the main entry point into Angular2's bootstraping process
 */
@NgModule(
  bootstrap: [App],
  declarations: [
    App
  ],
  imports: [ // import Angular's modules
    BrowserModule,
    BrowserAnimationsModule,
    HttpModule,
    RouterModule,
    CookieModule.forRoot(),
    FormsModule,
    ReactiveFormsModule,
    NgaModule.forRoot(),
    NgbModule.forRoot(),
    PagesModule,
    // ToasterModule,
    routing
  ],
  providers: [ // expose our Services and Providers into Angular's dependency injection
    ENV_PROVIDERS,
    APP_PROVIDERS,
    DEBUG_LOGGER_PROVIDERS,
    HttpService,
    // ToasterService
  ]
)

export class AppModule 
  ...

5.1.3. 引入ToasterModule模块

同理,我们需要将ToasterModule模块引入进来给我们的login.component.ts使用。但是我们不是在app.module.ts中引入,而是在login.module.ts中引入(我也不知道为什么不能再根模块中引入,只是知道在里面引入的话代码就会跑不起来。希望知道的童鞋指点下)。

import ToasterModule from 'angular2-toaster';
...

@NgModule(
  imports: [
    CommonModule,
    AppTranslationModule,
    ReactiveFormsModule,
    FormsModule,
    NgaModule,
    ToasterModule,
    routing
  ],
  declarations: [
    Login
  ],
  providers: [
    LoginService,
  ]
)
export class LoginModule 

5.1.4 在登录页面模板中添加Toaster显示位置

因为要在登录页面显示Toaster信息,所以,根据文档,我们必须要在页面模板中加入toaster-container的标签。

我们修改login.html这个页面模板,在登录表单的下面显示Toaster信息。

<div class="auth-main">
  <div class="auth-block">
    <h1 translate>'login.title'</h1>

    <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="form-horizontal">
      <div class="form-group row" [ngClass]="'has-error': (!user.valid && user.touched), 'has-success': (user.valid && user.touched)">
        <label for="inputUser3" class="col-sm-2 control-label" translate>'login.user'</label>

        <div class="col-sm-10">
          <input [formControl]="user" type="text" class="form-control" id="inputUser3" placeholder="'login.user' | translate">
        </div>
      </div>
      <div class="form-group row" [ngClass]="'has-error': (!password.valid && password.touched), 'has-success': (password.valid && password.touched)">
        <label for="inputPassword3" class="col-sm-2 control-label" translate>'login.password'</label>

        <div class="col-sm-10">
          <input [formControl]="password" type="password" class="form-control" id="inputPassword3" placeholder="'login.password' | translate">
        </div>
      </div>
      <div class="form-group row">
        <div class="text-center col-sm-10">
          <button [disabled]="!form.valid" type="submit" class="btn btn-default btn-auth" translate>'login.sign_in'</button>
        </div>
      </div>

    </form>
    
    <toaster-container></toaster-container>


  </div>
</div>

5.1.5 实现登录

这里贴出整个login.component.ts的代码。其实我们前面的章节在分析表单的时候已经看过部分代码,特别是在构造函数中使用FormBuilder来实现模型表单的分析。

这里我们更多是关注到在用户点击提交按钮时的交互,也就是onSubmit这个方法的实现。

import Component from '@angular/core';
import FormGroup,FormControl, AbstractControl, FormBuilder, Validators from '@angular/forms';
import  LoginService  from './login.service';
import  Logger  from "angular2-logger/core";
import ToasterContainerComponent, ToasterService, ToasterConfig, Toast from 'angular2-toaster';

import 'style-loader!./login.scss';

@Component(
  selector: 'login',
  templateUrl: './login.html',
  directives: [ToasterContainerComponent],
)
export class Login 

  public form:FormGroup;
  public user:AbstractControl;
  public password:AbstractControl;
  public submitted:boolean = false<

[小黄书小程序]导航栏和标题栏界面(代码片段)

我们前面几章已经将小黄书的后台基础框架给搭建好了。后台的小黄书相关的业务逻辑,会在往后根据客户端的业务需求来进行实现。今天起我们会开始小黄书小程序的实现。我们先看下小红书应用的主界面。这一篇文章中... 查看详情

小白如何入门全栈开发

...。那么,我们不妨就叫我们这个产品以及项目名称为小黄书吧。当然,因为这只是一个入门项目,我们不可能实现所有小红书的功能。我的想法,只要前后端的基本框架给搭建好了,并理解每个模块的一些基... 查看详情

[小黄书后台]insomnia及helloworld(代码片段)

我们今天开始迈出小黄书项目实战的第一步。前几篇文章就主要围绕Nodejs+Express的架构的构建。当我们架构建好之后,到时我们只需要在需要的位置填写代码就好了,开发起来就能非常快速。本篇文章作为这一系列的... 查看详情

[小黄书后台]文件上传到cdn(代码片段)

上一篇文章我们通过multer这个中间件将图片顺利的上传到了我们的服务器上面,且将图片的元数据存储到了Image这个mongodb的collections里面。这一章我们看下应该如何将文件上传到cdn,以便客户端通过cdn来快速访问图片。这... 查看详情

javaee二手书交易平台(代码片段)

...和未发布的)6.上传闲置7.查看书籍详情8.评论9.权限管理六、问题与收获1.问题2.收获一.系统描述会员:会员注册,基本信息修改;二手书籍出售信息的编辑、修改、发布(发布后不可再修改)、... 查看详情

openharmony应用集成agc认证服务实现登录(代码片段)

...合内部各项优秀服务,并将在全球化、质量、安全、工程管理等领域长期积累的能力开放给开发者,其致力于为应用的创意、 查看详情

django第二课基于django图书借阅管理网站平台(代码片段)

概念本文在上一篇文章之上完成登录,图书显示,关键字搜索以及分页功能登录功能实现当用户在首页进行输入学生用户信息后,点击登录按钮发送请求给服务器,地址请求为:/toLogin/urls.pypath('toLogin/',views.to... 查看详情

在自己校园内使用ssm实现校园兼职管理平台系统(代码片段)

...环境:jdk1.83.数据库:mysql5.6/5.7注意事项:1.管理员登录页面链接在项目的页面最底部的版权,点击就好2.管理员账号密码:10000/123456下面展示部分功能图片:网站端端首页: 兼职说明论坛贴子用户注... 查看详情

基于ssm实现家政管理平台的开发和实现(代码片段)

...平的不断提高,家政服务者和客户之间的信息互通和管理是既重 查看详情

springboot实现用户统一认证管理(单点登录)(代码片段)

...以这个技术点入手的。需求大概是这样的:为了统一管理系统的用户,上游平台做了个统一认证平台,所有用户进入子系统只有一个入口,即:上游平台登录入口,新用户也是上游平台进行添加;子系... 查看详情

(六)从零开始搭建k8s集群——使用kubesphere管理平台创建mysql数据库容器服务(代码片段)

前言KubeSphere管理平台是k8s容器化服务的一个界面操作平台。本节我们通过KubeSphere平台完成一个mysql有状态服务的创建,从而简化我们使用k8s命令行工具kubectl实现应用服务的创建。正文①获取kubesphere登录地址及账号密码k8s集... 查看详情

pythonweb开发:登录实现及功能测试(代码片段)

前面我们实现了服务端管理员的一些增删改查操作,但是,我们还没有实现管理员登录的功能呢。现在我们来根据,API接口文档,实现管理员登录功能。一、处理登录、登出请求我们可以在mgr目录里面创建一个代码文件sign_in_out.... 查看详情

django第二课基于django图书借阅管理网站平台(代码片段)

概念本文在上一篇文章之上完成登录,图书显示,关键字搜索以及分页功能登录功能实现当用户在首页进行输入学生用户信息后,点击登录按钮发送请求给服务器,地址请求为:/toLogin/urls.pypath('toLogin/',views.to... 查看详情

python抖音快手短视频等多平台同步管理发步-惠头条短视频,同时支持哔哩哔哩小红书微视西瓜视频微信视频号(代码片段)

...xff0c;希望帮助大家更方便、高效的来进行自媒体的创作与管理。【本文介绍的是惠头条的短视频自动发布】[文章推荐]每日一练:Python爬虫爬取全国新冠肺炎疫情数据实例详解,使用beautifulsoup4库实现[专栏推荐]Python短视... 查看详情

go实战|电商平台用户登录(代码片段)

...口编写2.1service层定义用户登录的服务结构//UserLoginService管理用户登陆的服务typeUserLoginServicestruct UserNamestring`form:"user_name"json:"user_name"binding:"required,min=5,max=15"` Passwordstring`form:"password"json:"p... 查看详情

80行代码实现简易登录管理系统(代码片段)

...,突然觉得字典可以做的事很多,想着做一个简易的登录管理系统,程序还有很多需要优化的地方,也请大家指正,相互交流学习,需要原代码的小伙伴可以告诉我...... #-----------------------------------------------------------------------... 查看详情

快速搭建elk集中化日志管理平台(代码片段)

...看,而且查看起来还比较麻烦,老大让搭一个集中化日志管理的东西,然后就在网上找到了这个东西ELKELK就是elastic公司下的三款产品,elasticsearch,logstash,kibana    官网:https://www. 查看详情

golang各个小游戏平台登录验证(代码片段)

现在小游戏的平台很多,服务器要对接的平台也多的说不过,这里废话不多说.我这里只是提供思路,和我已经实现的逻辑代码,不能保证在你的项目,必定可以.不喜勿碰.我这里暂时只提供我对接过的平台,其他的大致逻辑相似,这里主... 查看详情