欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解在ASP.NET Core中使用Angular2以及与Angular2的Token base身份认证

程序员文章站 2023-11-09 12:41:52
angular2是对angular1的一次彻底的,破坏性的更新。 相对于angular1.x,借用某果的广告语,唯一的不同,就是处处都不同。 •首先,推...

angular2是对angular1的一次彻底的,破坏性的更新。

相对于angular1.x,借用某果的广告语,唯一的不同,就是处处都不同。

•首先,推荐的语言已经不再是javascript,取而代之的typescript,(typescript = es6 + 类型系统 + 类型注解), typescriipt的类型系统对于开发复杂的单页web app大有帮助,同时编译成javascript后的执行效率也比大多数手写javascript要快。有兴趣的同学可以查阅官方文档: |。

•得益于彻底重构,性能相对于angular1.x有了大幅提升,也更适合再全平台部署。

•angular2是基于component的,component可以理解为是1.x时代的controller + $scope + view

•view的很多语法也做了更新,比如<li ng-repeat="movie in vm.movies"></li> 变成了 <li *ngfor="let movie of movies"></li>

关于angular2,强烈建议查阅官方文档:|

注意:本文章属于step by step + code sample教程,且篇幅较长,建议下载本sample并跟着本文进度自己重做一遍本例,下载完整代码并分析代码结构才有意义,下载地址:how to authorization angular 2 app with asp.net core web api

1.前期准备

•推荐使用vs2015 update3或更新的版本完成本示例,下载地址:

•你需要安装.net core开发环境,这里提供vs版: //www.jb51.net/softs/472362.html

•安装node.js 版本5.0.0或以上,(在本例中,这个主要是编译typescript用的)下载地址:node.js and npm

•npm 3.0.0或以上,默认npm会随着node.js一并安装完毕。(在本例中,这个主要是下载各种angular的各个包用的,参考vs中的nuget)

2.创建项目

在vs中新建项目,项目类型选择 asp.net core web application(.net core),输入项目名称为:csauthorangular2inaspnetcore,template选择为empty.

3.在项目中整合angular2

3.1.配置startup.cs

注:添加下面的代码时ide会报代码错误,这是因为还没有引用对用的包,进入报错的这一行,点击灯泡,加载对应的包就可以了。

详解在ASP.NET Core中使用Angular2以及与Angular2的Token base身份认证

(图文无关)

在configureservices中添加如下代码

services.addmvc();

这里是添加mvc服务

在configure中添加如下代码

app.usestaticfiles();

app.usemvc(routes =>
{
  routes.maproute(
    name: "default",
    template: "{controller=home}/{action=index}");
});

第一句是启用静态文件,第二句是应用mvc模式并添加路由配置。

完整的代码应该是这个样子

public class startup
{
  // this method gets called by the runtime. use this method to add services to the container.
  // for more information on how to configure your application, visit http://go.microsoft.com/fwlink/?linkid=398940
  public void configureservices(iservicecollection services)
  {
    services.addmvc();
  }

  // this method gets called by the runtime. use this method to configure the http request pipeline.
  public void configure(iapplicationbuilder app, ihostingenvironment env, iloggerfactory loggerfactory)
  {
    app.usestaticfiles();

    app.usemvc(routes =>
    {
      routes.maproute(
        name: "default",
        template: "{controller=home}/{action=index}");
    });
  }
}

3.2.添加控制器以及视图

3.2.1.在项目根目录下添加controllers目录,并在其中添加一个控制器homecontroller.cs,默认代码即可。

3.2.2.在项目跟目录下创建views目录,在views目录中新建目录home, 最后在home目录中新建视图index.cshtml,内容应该是这样:

<html>
<head>
  <title>angular quickstart</title>
  <base href="/">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- 1. load libraries -->
  <!-- polyfill(s) for older browsers -->
  <script src="node_modules/core-js/client/shim.min.js"></script>
  <script src="node_modules/zone.js/dist/zone.js"></script>
  <script src="node_modules/reflect-metadata/reflect.js"></script>
  <script src="node_modules/systemjs/dist/system.src.js"></script>
  <!-- 2. configure systemjs -->
  <script src="systemjs.config.js"></script>
  <script>
   system.import('app').catch(function(err){ console.error(err); });
  </script>
</head>
<!-- 3. display the application -->
<body>
  <my-app>loading...</my-app>
</body>
</html>

现在运行项目的话你仅仅能看到一个loading,再控制台中你还能看到错误,这是因为我们还没有配置angular。让我们前往wwwroot目录。

3.3.在项目的wwwroot目录中添加如下结构:

3.3.1搭建angular2基础环境

•package.json

{
 "name": "angular-quickstart",
 "version": "1.0.0",
 "scripts": {
  "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
  "lite": "lite-server",
  "postinstall": "typings install",
  "tsc": "tsc",
  "tsc:w": "tsc -w",
  "typings": "typings"
 },
 "licenses": [
  {
   "type": "mit",
   "url": "https://github.com/angular/angular.io/blob/master/license"
  }
 ],
 "dependencies": {
  "@angular/common": "2.0.2",
  "@angular/compiler": "2.0.2",
  "@angular/core": "2.0.2",
  "@angular/forms": "2.0.2",
  "@angular/http": "2.0.2",
  "@angular/platform-browser": "2.0.2",
  "@angular/platform-browser-dynamic": "2.0.2",
  "@angular/router": "3.0.2",
  "@angular/upgrade": "2.0.2",
  "angular-in-memory-web-api": "0.1.5",
  "bootstrap": "3.3.7",
  "core-js": "2.4.1",
  "reflect-metadata": "0.1.8",
  "rxjs": "5.0.0-beta.12",
  "systemjs": "0.19.39",
  "zone.js": "0.6.25"
 },
 "devdependencies": {
  "concurrently": "3.0.0",
  "gulp": "^3.9.1",
  "lite-server": "2.2.2",
  "typescript": "2.0.3",
  "typings": "1.4.0"
 }
}

•systemjs.config.js

(function (global) {
  system.config({
    paths: {
      // paths serve as alias
      'npm:': 'node_modules/'
    },
    // map tells the system loader where to look for things
    map: {
      // our app is within the app folder
      app: 'app',
      // angular bundles
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
      '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
      // other libraries
      'rxjs': 'npm:rxjs',
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
    },
    // packages tells the system loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.js',
        defaultextension: 'js'
      },
      rxjs: {
        defaultextension: 'js'
      }
    }
  });
})(this);

•tsconfig.js

{
 "compileonsave": true,
 "compileroptions": {
  "target": "es5",
  "module": "commonjs",
  "moduleresolution": "node",
  "sourcemap": true,
  "emitdecoratormetadata": true,
  "experimentaldecorators": true,
  "removecomments": false,
  "noimplicitany": false
 },
 "exclude": [
  "node_modules"
 ]
}

•typings.json(注,在最新文档中typings已被npm的@types替代,参见官方文档:)

{
 "globaldependencies": {
  "core-js": "registry:dt/core-js#0.0.0+20160725163759",
  "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
  "node": "registry:dt/node#6.0.0+20160909174046"
 }
}

右击wwwroot中的package.json,选择restore packages(或者在cmd下进入wwwroot目录,并执行命令 npm install),npm会去下载需要的包,并存储于node_modules目录中。

详解在ASP.NET Core中使用Angular2以及与Angular2的Token base身份认证

3.3.2.配置启动文件以启用angular2

在wwwroot下新建目录app,app拥有如下文件:

•app.component.ts

import { component } from '@angular/core';

@component({
  moduleid: module.id,
  selector: 'my-app',
  template: "this is in angular2",
})
export class appcomponent {
}

可以发现被@component装饰属性装饰了appcomponent,selector指代你component的占位符,比如本例中你可以再home/index.cshtml中发现一段这样的标记

<my-app>loading...</my-app>

template既为该component的view,不要忘记moduleid,不添加它会出现很多奇怪的问题。

•app.module.ts

import { ngmodule } from "@angular/core";
import { browsermodule } from "@angular/platform-browser";

import { appcomponent } from "./app.component";

@ngmodule({
  bootstrap: [appcomponent],
  imports: [
    browsermodule
  ],
  declarations: [
    appcomponent
  ]
})
export class appmodule { }

•main.ts

import { platformbrowserdynamic } from '@angular/platform-browser-dynamic';
import { appmodule } from './app.module';
const platform = platformbrowserdynamic();
platform.bootstrapmodule(appmodule);

基础整合完毕。

按f5 debug一下,现在你能再浏览器中看到一句话:this is in angular 2

详解在ASP.NET Core中使用Angular2以及与Angular2的Token base身份认证

4.实现身份认证

废了半天劲,看着很傻,没有任何成就感。怎么办,让我们再深入一点,接下来我们来为angular2完成一个token base的身份验证,我会把angular2的routing,data bind,service,http,等等你工作中最常用到的挨个演示一遍。

4.1.server端

4.1.1.创建一些辅助类

4.1.1.1.在项目根目录下创建一个文件夹auth,并添加rsakeyhelper.cs以及tokenauthoption.cs两个文件

•在rsakeyhelper.cs中

using system.security.cryptography;

namespace cstokenbaseauth.auth
{
  public class rsakeyhelper
  {
    public static rsaparameters generatekey()
    {
      using (var key = new rsacryptoserviceprovider(2048))
      {
        return key.exportparameters(true);
      }
    }
  }
}

•在tokenauthoption.cs中

using system;
using microsoft.identitymodel.tokens;

namespace cstokenbaseauth.auth
{
  public class tokenauthoption
  {
    public static string audience { get; } = "exampleaudience";
    public static string issuer { get; } = "exampleissuer";
    public static rsasecuritykey key { get; } = new rsasecuritykey(rsakeyhelper.generatekey());
    public static signingcredentials signingcredentials { get; } = new signingcredentials(key, securityalgorithms.rsasha256signature);

    public static timespan expiresspan { get; } = timespan.fromminutes(20);
  }
}

4.1.1.2.在项目根目录下创建目录model,并在其中添加requestresult.cs,代码应该是这样。

public class requestresult
{
  public requeststate state { get; set; }
  public string msg { get; set; }
  public object data { get; set; }
}

public enum requeststate
{
  failed = -1,
  notauth = 0,
  success = 1
}

4.1.2更新startup.cs

在configureservices中添加如下代码:

services.addauthorization(auth =>
{
  auth.addpolicy("bearer", new authorizationpolicybuilder()
    .addauthenticationschemes(jwtbearerdefaults.authenticationscheme‌​)
    .requireauthenticateduser().build());
});

这里是添加身份认证服务

在configure方法中添加如下代码:

app.useexceptionhandler(appbuilder =>
{
  appbuilder.use(async (context, next) =>
  {
    var error = context.features[typeof(iexceptionhandlerfeature)] as iexceptionhandlerfeature;

    //when authorization has failed, should retrun a json message to client
    if (error != null && error.error is securitytokenexpiredexception)
    {
      context.response.statuscode = 401;
      context.response.contenttype = "application/json";

      await context.response.writeasync(jsonconvert.serializeobject(new requestresult
      {
        state = requeststate.notauth,
        msg = "token expired"
      }));
    }
    //when orther error, retrun a error message json to client
    else if (error != null && error.error != null)
    {
      context.response.statuscode = 500;
      context.response.contenttype = "application/json";
      await context.response.writeasync(jsonconvert.serializeobject(new requestresult
      {
        state = requeststate.failed,
        msg = error.error.message
      }));
    }
    //when no error, do next.
    else await next();
  });
});

本段是handle当身份认证失败时抛出的异常,并返回合适的json

在相同的方法中添加另外一段代码:

app.usejwtbearerauthentication(new jwtbeareroptions()
{
  tokenvalidationparameters = new tokenvalidationparameters()
  {
    issuersigningkey = tokenauthoption.key,
    validaudience = tokenauthoption.audience,
    validissuer = tokenauthoption.issuer,
    // when receiving a token, check that we've signed it.
    validateissuersigningkey = true,
    // when receiving a token, check that it is still valid.
    validatelifetime = true,
    // this defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time 
    // when validating the lifetime. as we're creating the tokens locally and validating them on the same 
     // machines which should have synchronised time, this can be set to zero. where external tokens are
    // used, some leeway here could be useful.
    clockskew = timespan.fromminutes(0)
  }
});

本段代码是应用jwtbearerauthentication身份认证。

4.1.3.tokenauthcontroller.cs

在controllers中新建一个web api controller class,命名为tokenauthcontroller.cs。我们将在这里完成登录授权,

在同文件下添加两个类,分别用来模拟用户模型,以及用户存储,代码应该是这样:

public class user
{
  public guid id { get; set; }
  public string username { get; set; }
  public string password { get; set; }
}

public static class userstorage
{
  public static list<user> users { get; set; } = new list<user> {
    new user {id=guid.newguid(),username="user1",password = "user1psd" },
    new user {id=guid.newguid(),username="user2",password = "user2psd" },
    new user {id=guid.newguid(),username="user3",password = "user3psd" }
  };
}

接下来在tokenauthcontroller.cs中添加如下方法

private string generatetoken(user user, datetime expires)
{
  var handler = new jwtsecuritytokenhandler();
  
  claimsidentity identity = new claimsidentity(
    new genericidentity(user.username, "tokenauth"),
    new[] {
      new claim("id", user.id.tostring())
    }
  );

  var securitytoken = handler.createtoken(new securitytokendescriptor
  {
    issuer = tokenauthoption.issuer,
    audience = tokenauthoption.audience,
    signingcredentials = tokenauthoption.signingcredentials,
    subject = identity,
    expires = expires
  });
  return handler.writetoken(securitytoken);
}

该方法仅仅只是生成一个auth token,接下来我们来添加另外一个方法来调用它

在相同文件中添加如下代码

[httppost]
public string getauthtoken(user user)
{
  var existuser = userstorage.users.firstordefault(u => u.username == user.username && u.password == user.password);

  if (existuser != null)
  {
    var requestat = datetime.now;
    var expiresin = requestat + tokenauthoption.expiresspan;
    var token = generatetoken(existuser, expiresin);

    return jsonconvert.serializeobject(new {
      statecode = 1,
      requertat = requestat,
      expiresin = tokenauthoption.expiresspan.totalseconds,
      accesstoken = token
    });
  }
  else
  {
    return jsonconvert.serializeobject(new { statecode = -1, errors = "username or password is invalid" });
  }
}

接下来我们来完成授权部分,在相同的文件中添加如下代码:

public string getuserinfo()
{
  var claimsidentity = user.identity as claimsidentity;

  return jsonconvert.serializeobject(new requestresult
  {
    state = requeststate.success,
    data = new
    {
      username = claimsidentity.name
    }
  });
}

为方法添加装饰属性

[httpget]

[authorize("bearer")]

第二行代码说明这个action需要身份验证。

该文件完整的代码应该是这个样子:

using system;
using system.collections.generic;
using system.linq;using microsoft.aspnetcore.mvc;
using csauthorangular2inaspnetcore.auth;
using system.identitymodel.tokens.jwt;
using newtonsoft.json;
using system.security.claims;
using system.security.principal;
using microsoft.identitymodel.tokens;
using csauthorangular2inaspnetcore.model;
using microsoft.aspnetcore.authorization;


namespace csauthorangular2inaspnetcore.controllers
{
  [route("api/[controller]")]
  public class tokenauthcontroller : controller
  {
    [httppost]
    public string getauthtoken([frombody]user user)
    {
      var existuser = userstorage.users.firstordefault(u => u.username == user.username && u.password == user.password);

      if (existuser != null)
      {
        var requestat = datetime.now;
        var expiresin = requestat + tokenauthoption.expiresspan;
        var token = generatetoken(existuser, expiresin);

        return jsonconvert.serializeobject(new requestresult
        {
          state = requeststate.success,
          data = new
          {
            requertat = requestat,
            expiresin = tokenauthoption.expiresspan.totalseconds,
            tokeytype = tokenauthoption.tokentype,
            accesstoken = token
          }
        });
      }
      else
      {
        return jsonconvert.serializeobject(new requestresult
        {
          state = requeststate.failed,
          msg = "username or password is invalid"
        });
      }
    }

    private string generatetoken(user user, datetime expires)
    {
      var handler = new jwtsecuritytokenhandler();

      claimsidentity identity = new claimsidentity(
        new genericidentity(user.username, "tokenauth"),
        new[] {
          new claim("id", user.id.tostring())
        }
      );

      var securitytoken = handler.createtoken(new securitytokendescriptor
      {
        issuer = tokenauthoption.issuer,
        audience = tokenauthoption.audience,
        signingcredentials = tokenauthoption.signingcredentials,
        subject = identity,
        expires = expires
      });
      return handler.writetoken(securitytoken);
    }

    [httpget]
    [authorize("bearer")]
    public string getuserinfo()
    {
      var claimsidentity = user.identity as claimsidentity;

      return jsonconvert.serializeobject(new requestresult
      {
        state = requeststate.success,
        data = new
        {
          username = claimsidentity.name
        }
      });
    }
  }

  public class user
  {
    public guid id { get; set; }

    public string username { get; set; }

    public string password { get; set; }
  }

  public static class userstorage
  {
    public static list<user> users { get; set; } = new list<user> {
      new user {id=guid.newguid(),username="user1",password = "user1psd" },
      new user {id=guid.newguid(),username="user2",password = "user2psd" },
      new user {id=guid.newguid(),username="user3",password = "user3psd" }
    };
  }
}

4.2angular2端

4.2.1创建view model

在wwwroot/app下创建一个目录:_model, 并添加一个typescript文件requestresult.ts,内容应该是这样。

export class requestresult {
  state: number;
  msg: string;
  data: object;
}

4.2.2创建service

在wwwroot/app下创建一个目录:_services,并添加一个typescript文件auth.service.ts,内容应该是这样。

import { injectable } from "@angular/core";
import { headers, http } from "@angular/http";
import "rxjs/add/operator/topromise";

import { requestresult } from "../_model/requestresult";

@injectable()
export class authservice {
  private tokeykey = "token";
  private token: string;

  constructor(
    private http: http
  ) { }

  login(username: string, password: string): promise<requestresult> {
    return this.http.post("/api/tokenauth", { username: username, password: password }).topromise()
      .then(response => {
        let result = response.json() as requestresult;
        if (result.state == 1) {
          let json = result.data as any;

          sessionstorage.setitem("token", json.accesstoken);
        }
        return result;
      })
      .catch(this.handleerror);
  }

  checklogin(): boolean {
    var token = sessionstorage.getitem(this.tokeykey);
    return token != null;
  }

  getuserinfo(): promise<requestresult> {
    return this.authget("/api/tokenauth");
  }

  authpost(url: string, body: any): promise<requestresult> {
    let headers = this.initauthheaders();
    return this.http.post(url, body, { headers: headers }).topromise()
      .then(response => response.json() as requestresult)
      .catch(this.handleerror);
  }

  authget(url): promise<requestresult> {
    let headers = this.initauthheaders();
    return this.http.get(url, { headers: headers }).topromise()
      .then(response => response.json() as requestresult)
      .catch(this.handleerror);
  }

  private getlocaltoken(): string {
    if (!this.token) {
      this.token = sessionstorage.getitem(this.tokeykey);
    }
    return this.token;
  }

  private initauthheaders(): headers {
    let token = this.getlocaltoken();
    if (token == null) throw "no token";

    var headers = new headers();
    headers.append("authorization", "bearer " + token);

    return headers;
  }

  private handleerror(error: any): promise<any> {
    console.error('an error occurred', error);
    return promise.reject(error.message || error);
  }
}

本文件主要用来完成登录以及登录验证工作,之后该service将可以被注入到component中以便被component调用。

注:主要的逻辑都应该写到service中

4.2.3.创建component

4.2.3.1.在wwwroot/app下创建一个目录home,该目录用来存放homecomponent,home应拥有如下文件:

•home.component.ts

import { component, oninit } from "@angular/core";

import { authservice } from "../_services/auth.service";

@component({
  moduleid: module.id,
  selector: "my-home",
  templateurl: "view.html",
  styleurls: ["style.css"]
})
export class homecomponent implements oninit {
  islogin = false;
  username: string;
  
  constructor(
    private authservice: authservice
  ) { }

  ngoninit(): void {
    this.islogin = this.authservice.checklogin();
    if (this.islogin) {
      this.authservice.getuserinfo().then(res => {
        this.username = (res.data as any).username;
      });
    }

  }
}

查阅代码,在@component中指定了view以及style。

authservice被在构造方法中被注入了本component,ngoninit是接口oninit的一个方法,他在component初始化时会被调用。

•style.css

/*styles of this view*/

本例中没有添加任何样式,如有需要可以写在这里。

•view.html

<div *ngif="islogin">
  <h1>hi <span>{{username}}</span></h1>
</div>

<div *ngif="!islogin">
  <h1>please login</h1>
  <a routerlink="/login">login</a>
</div>

*ngif=""是angular2 的其中一种标记语法,作用是当返回真时渲染该节点,完整教程请参阅官方文档。

4.2.3.2.在wwwroot/app下创建目录login,该目录用来存放logincomponent,文件结构类似于上一节。

•login.component.ts

import { component } from "@angular/core";
import { router } from '@angular/router';

import { authservice } from "../_services/auth.service";

@component({
  moduleid: module.id,
  selector: "my-login",
  templateurl: "view.html",
  styleurls: ["style.css"]
})
export class logincomponent {

  private username: string;
  private password: string;

  constructor(
    private authservice: authservice,
    private router: router
  ) { }

  login() {
    this.authservice.login(this.username, this.password)
      .then(result => {
        if (result.state == 1) {
          this.router.navigate(["./home"]);
        }
        else {
          alert(result.msg);
        }
      });
  }
}

•style.css

/*styles of this view*/

•view.html

<table>
  <tr>
    <td>username:</td>
    <td><input [(ngmodel)]="username" placeholder="usename:try type user1" /></td>
  </tr>
  <tr>
    <td>username:</td>
    <td><input [(ngmodel)]="password" placeholder="password:try type user1psd" /></td>
  </tr>
  <tr>
    <td></td>
    <td><input type="button" (click)="login()" value="login" /></td>
  </tr>
</table>

4.2.4.应用路由

路由是切换多页面用的。

在wwwroot/app下新建一个typescript文件,命名为app-routing.module.ts,内容应该是这个样子。

import { ngmodule } from "@angular/core";
import { routermodule, routes } from "@angular/router";

import { homecomponent } from "./home/home.component";
import { logincomponent } from "./login/login.component"

const routes: routes = [
  { path: "", redirectto: "/home", pathmatch: "full" },
  { path: "home", component: homecomponent },
  { path: "login", component: logincomponent }
];

@ngmodule({
  imports: [routermodule.forroot(routes)],
  exports: [routermodule]
})
export class approutingmodule { }

接下来我们来应用这个路由,

打开app.module.ts,更新代码如下:

import { ngmodule } from "@angular/core";
import { browsermodule } from "@angular/platform-browser";
import { httpmodule } from "@angular/http";
import { formsmodule } from "@angular/forms";

import { approutingmodule } from "./app-routing.module";

import { authservice } from "./_services/auth.service";

import { appcomponent } from "./app.component";
import { homecomponent } from "./home/home.component";
import { logincomponent } from "./login/login.component";

@ngmodule({
  bootstrap: [appcomponent],
  imports: [
    browsermodule,
    httpmodule,
    approutingmodule,
    formsmodule
  ],
  declarations: [
    appcomponent,
    homecomponent,
    logincomponent
  ],
  providers: [authservice]
})
export class appmodule { }

ngmodule和browsermodule你可以理解为基础模块,必加的。

httpmodule是做http请求用的。

formsmodule是做双向数据绑定用的,比如下面这样的,如果想把数据从view更新到component,就必须加这个。

<input [(ngmodel)]="username" placeholder="usename:try type user1" />

approutingmodule即为我们刚才添加的路由文件。

authservice是我们最早添加的service文件。

appcomponent是我们最初添加的那个app.component.ts里的那个component.

homecomponent,logincomponent同上。

最后我们再app.component.ts中添加路由锚点,

把template的值为 "<router-outlet></router-outlet>"

完整的代码应该是这样:

import { component } from '@angular/core';

@component({
  moduleid: module.id,
  selector: 'my-app',
  template: "<router-outlet></router-outlet>",
})
export class appcomponent {
}

router-outlet是路由锚点的关键词。

至此,所有代码完成,f5调试吧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。