Python语言技术文档

微信小程序技术文档

php语言技术文档

jsp语言技术文档

asp语言技术文档

C#/.NET语言技术文档

html5/css技术文档

javascript

点击排行

您现在的位置:首页 > 技术文档 > MVC/.NET框架

详解在asp.net框架Core中使用Angular2以及与Angular2的Token base身份认证

来源:中文源码网    浏览:300 次    日期:2024-04-27 02:52:01
【下载文档:  详解在asp.net框架Core中使用Angular2以及与Angular2的Token base身份认证.txt 】


详解在ASP.NET Core中使用Angular2以及与Angular2的Token base身份认证
Angular2是对Angular1的一次彻底的,破坏性的更新。
相对于Angular1.x,借用某果的广告语,唯一的不同,就是处处都不同。
•首先,推荐的语言已经不再是Javascript,取而代之的TypeScript,(TypeScript = ES6 + 类型系统 + 类型注解), TypeScriipt的类型系统对于开发复杂的单页Web app大有帮助,同时编译成javascript后的执行效率也比大多数手写javascript要快。有兴趣的同学可以查阅官方文档:英文传送门 |中文传送门。
•得益于彻底重构,性能相对于Angular1.x有了大幅提升,也更适合再全平台部署。
•Angular2是基于Component的,Component可以理解为是1.x时代的Controller + $Scope + view
•View的很多语法也做了更新,比如
  • 变成了

  • 关于Angular2,强烈建议查阅官方文档:英文传送门| 中文传送门
    注意:本文章属于Step by step + Code Sample教程,且篇幅较长,建议下载本Sample并跟着本文进度自己重做一遍本例,下载完整代码并分析代码结构才有意义,下载地址:How to authorization Angular 2 app with asp.net core web api
    1.前期准备
    •推荐使用VS2015 Update3或更新的版本完成本示例,下载地址://www.zwyuanma.com/softjc/446184.html
    •你需要安装.NET Core开发环境,这里提供VS版: //www.zwyuanma.com/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会报代码错误,这是因为还没有引用对用的包,进入报错的这一行,点击灯泡,加载对应的包就可以了。
    (图文无关)
    在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,内容应该是这样:


    Angular QuickStart















    Loading...


    现在运行项目的话你仅仅能看到一个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": "http://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目录中。
    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中发现一段这样的标记
    Loading...
    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
    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 Users { get; set; } = new List {
    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 Users { get; set; } = new List {
    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 {
    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 {
    return this.authGet("/api/TokenAuth");
    }
    authPost(url: string, body: any): Promise {
    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 {
    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 {
    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

    Hi {{userName}}




    please login


    Login

    *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













    userName:
    userName:

    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,就必须加这个。

    AppRoutingModule即为我们刚才添加的路由文件。
    AuthService是我们最早添加的service文件。
    AppComponent是我们最初添加的那个app.component.ts里的那个component.
    HomeComponent,LoginComponent同上。
    最后我们再app.component.ts中添加路由锚点,
    把template的值为 ""
    完整的代码应该是这样:
    import { Component } from '@angular/core';
    @Component({
    moduleId: module.id,
    selector: 'my-app',
    template: "",
    })
    export class AppComponent {
    }
    router-outlet是路由锚点的关键词。
    至此,所有代码完成,F5调试吧。
    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持中文源码网。

    相关内容