在ASP.NET Core中使用Angular2,以及與Angular2的Token base身份認證

来源:http://www.cnblogs.com/liubingbing/archive/2017/02/17/6410053.html
-Advertisement-
Play Games

註:下載本文提到的完整代碼示例請訪問:How to authorization Angular 2 app with asp.net core web api 在ASP.NET Core中使用Angular2,以及與Angular2的Token base身份認證 Angular2是對Angular1 ...


註:下載本文提到的完整代碼示例請訪問:How to authorization Angular 2 app with asp.net core web api

在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的很多語法也做了更新,比如<li ng-repeat="movie in vm.movies"></li> 變成了 <li *ngFor="let movie of movies"></li>
  • 等等。。

關於Angular2,強烈建議查閱官方文檔:英文傳送門 | 中文傳送門

廢話不多說,接下來的內容中,將介紹如何將 Angular2 整合到 ASP.NET Core 中,並實現一個Anguar2 和 ASP.NET Core Web API 的身份認證。

 

註意:本文章屬於Step by step + Code Sample教程,且篇幅較長,建議下載本Sample並跟著本文進度自己重做一遍本例,下載完整代碼並分析代碼結構才有意義,下載地址:How to authorization Angular 2 app with asp.net core web api

1.前期準備

  • 推薦使用VS2015 Update3或更新的版本完成本示例,下載地址:https://www.visualstudio.com/
  • 你需要安裝.NET Core開發環境,這裡提供VS版:https://www.microsoft.com/net/core#windows
  • 安裝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,內容應該是這樣:

<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目錄中。

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

 

---分割線-------------------------------------------------------------------------

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同上。

 

最後我

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 5.5 HTTP Cookies in ASP.NET Web API 5.5 ASP.NET Web API中的HTTP Cookie 本文引自:http://www.asp.net/web-api/overview/working-with-http/http-cookies By Mike W ...
  • EF雖然是一個晚生畸形的ORM框架,但功能強大又具有靈活性的,給了開發人員一定的發揮空間。因為微軟出發點總是好的,讓開發變得簡單,但實際上不是所有的事情都這麼理想。這裡順便推薦馬丁大叔的書《企業應架構模式》。 本節主要深入分析EF的分層問題,下麵是本節的已列出的要探討內容。 領域模型的概念 DbCo ...
  • 導航 閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html。 本文主要來講解以下內容: 〇、前言 Ⅰ、Using[FromUri] Ⅱ、Using[FromBody] Ⅲ、Type Co ...
  • 本文的主要內容是通過兩種方式實現簡單郵件的接收,一種方式是通過C語言實現,另一種是通過C#實現的, 兩種方式在實現上有許多的不同之處,但是本質上都是一樣的. 一,C語言實現方式 C語言接收郵件的步驟: 1.創建TCP連接 埠:預設110 2.連接到Pop3伺服器 伺服器名稱:例如"pop3.163 ...
  • 浪漫的周末從cnblogs開始。話說,今天和往常的周末一樣,韓君躲在被窩裡用手機翻閱著園子里的珠璣。一篇《應用XML作為資料庫的快速開發框架》的文章在韓君腦子裡激起了一波球形閃電。想想上周中剛好完成的一個WinCE小項目,這波久久不能平息。韓君做了一個比馬兄更艱難的決定,秒速穿戴衣褲後,開始了那第N ...
  • EF Core 1.0 Database First http://www.cnblogs.com/linezero/p/EFCoreDBFirst.html ASP.NET Core 開發 - Entity Framework (EF) Core,ASP.NET Core 操作資料庫。 Entit ...
  • 今後爭取每兩天能更新一次。平日的誘惑太多,雙休只顧玩了,進度有點慢。 接上一講的,類型的安全性,留下了點小尾巴——比較對象的相等性。 C#有四種比較相等的方式:除了“==”運算符外,System.Object定義了3中方法:ReferenceEqual()方法和兩種Equals(); 1.首先是“= ...
  • 前言 之前對於用SelfHost來手動實現Web API的宿主模式,似乎不是太深入,所以本篇文章我們一起來討論關於利用HttpClient來訪問Web API上的資源來進行探討以及註意相關事項,希望此文對你也有收穫。 來自XML或Json Content的簡單參數 當Web API方法中接受如Str ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...