angular (6)与服务器通信

<1>web服务器

1、使用node.js创建服务器

node.js可以用typescript语言来开发;
node.js在使用http和socket方面做了大量的工作,可以减少我们自己的开发工作量。
1.新建一个文件夹serve,并用npm初始化这个文件夹

npm init -y

建立了一个包含默认配置的package.json文件


64.png

2.使用typescript语言开发,引入node的类型定义文件

npm i @types/node --save

类型定义文件的作用是可以让typescript可以使用现在已有的javascript的库


65.png

3.serve文件夹下新建配置文件tsconfig.json
node本身不认typescript,所以需要将typescript编译成javascript。

{
    "compileOnSave": true,
    "compilerOptions":{       //编译器配置
        "target":"es5",       //目标是编译成es5规范的脚本,也就是js
        "module":"commonjs",  //模块的规范是commonjs
        "emitDecoratorMetadata": true,
        "experimentalDecorators":true,   //这两个是要保留装饰器的元数据
        "outDir":"build",   //编译后文件默认放置在build文件夹下
        "lib":["es6"]       //开发时使用es6的语法
    },
    "exclude": [            //编译时要排除的文件
        "node_modules"
    ]
}

4.serve文件夹下新建文件serve\hello_serve.ts

66.png

import * as http from 'http';

const serve = http.createServer((request,response) => {
    response.end("hello node!");
});
serve.listen(8000);

5.vs code编译器下
用Ctrl+Shift+B命令编译ts文件

67.png

6.用hello_serve.js文件启动node服务器

node build/hello_serve.js

7.浏览器中访问http://localhost:8000/
得到服务器返回的字符串

68.png

2、使用Epress创建restful的http服务

Epress框架提供了所以web应用都需要的一些常用功能
1.安装Epress框架

npm install express --save

2.安装Epress框架类型定义文件来进行typescript开发

npm install @types/express --save

3.在serve/serve目录下新建配置文件action_serve.ts

69.png

import * as express from 'express';

const app = express();
// 当服务器为开启状态时,如果在根目录'/'下,
// 接收到了get请求,那么服务器会返回hello express
app.get('/',(req, res) => {
    res.send("hello express");
});

app.get('/product',(req,res) => {
    res.send("接收到产品查询请求");
});
const serve = app.listen(8000,"localhost",() => {
    console.log("服务器已启动,地址为http://localhose:8000");
});

4.vs code编译器下
用Ctrl+Shift+B命令编译ts文件

70.png

5.用auction_serve.js文件启动node服务器

node build/auction_serve.js

控制台中


71.png

6.浏览器中访问http://localhost:8000/

72.png

访问product时
73.png

此时已经建立好了两个http服务

3、监控服务器文件的变化

因为当修改文件时,重新刷新地址,内容是不会变化的,必须要重启服务才可以更新内容,所以下面安装可以监控源代码变化并自动重启服务器的工具nodemonitor。
1.安装nodemon
···
npm install -g nodemon
···

2.用nodemon来启动服务器

nodemon build/auction_serve.js

当修改内容时


74.png

4、修改产品查询请求的服务

产品查询请求的服务应该返回一个json数据
1.修改auction_serve.ts

import * as express from 'express';

const app = express();

// 商品类的定义文件
export class Product {
    constructor(
        public id:number,
        public title:string,
        public price:number,
        public rating:number,
        public desc:string
    ){}
}
// 商品数据
const products:Product[] = [
    new Product(1,"商品1",1.99,1.5,"这是商品1"),
    new Product(2,"商品2",2.99,2.5,"这是商品2"),
    new Product(3,"商品3",3.99,3.5,"这是商品3"),
    new Product(4,"商品4",4.99,4.5,"这是商品4"),
    new Product(5,"商品5",5.99,4.5,"这是商品5")
];

app.get('/',(req, res) => {
    res.send("hello express");
});

//此时返回的是一个json数据,传入定义的products
app.get('/product',(req,res) => {
    res.json(products);
});
const serve = app.listen(8000,"localhost",() => {
    console.log("服务器已启动,地址为http://localhose:8000");
});

2.刷新浏览器

75.png

5、新增服务,可以根据指定的id来获取商品信息

1.修改auction_serve.ts

import * as express from 'express';
const app = express();
export class Product {
    constructor(
        public id:number,
        public title:string,
        public price:number,
        public rating:number,
        public desc:string
    ){}
}
const products:Product[] = [
    new Product(1,"商品1",1.99,1.5,"这是商品1"),
    new Product(2,"商品2",2.99,2.5,"这是商品2"),
    new Product(3,"商品3",3.99,3.5,"这是商品3"),
    new Product(4,"商品4",4.99,4.5,"这是商品4"),
    new Product(5,"商品5",5.99,4.5,"这是商品5")
];

app.get('/',(req, res) => {
    res.send("hello express");
});
app.get('/product',(req,res) => {
    res.json(products);
});

// 新增根据id查询数据的服务
app.get('/product/:id',(req,res) => {
    res.json(products.find((product) => product.id == req.params.id));
});

const serve = app.listen(8000,"localhost",() => {
    console.log("服务器已启动,地址为http://localhose:8000");
});

2.结果

76.png

77.png

<2>http通信

在angular应用中发送http请求来调用上面新建的服务,并处理服务器返回的数据。
在默认情况下,angular的http服务使用响应式编程的方式来处理http请求。
1.在angular项目中新建一个产品组件

ng g component product

2.修改app.module.ts,引入HttpModule模块

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent } from './app.component';
import { ProductComponent } from './product/product.component';

// 引入
import { HttpModule } from '@angular/http';

@NgModule({
  declarations: [
    AppComponent,
    ProductComponent
  ],
  imports: [
    BrowserModule,
    // 引入
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

3.修改product.component.ts

import { Component, OnInit } from '@angular/core';


import { Observable } from 'rxjs';
import { Http } from '@angular/http';
import 'rxjs/Rx';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html',
  styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {

  // 声明一个流,负责接收服务器上传过来的流
  dataSource:Observable<any>;

  // 用来和模版做数据绑定的数组
  myproducts:Array<any> = [];

  // 将angular的服务依赖注入进来
  constructor(private http:Http) { 
    this.dataSource = this.http.get('/product')
    .map((res) => res.json());
   }

  ngOnInit() {
    // 订阅流
    this.dataSource.subscribe(
      // 把订阅到的数据传给myproducts属性
      (data) => this.myproducts = data
    )
  }

}

http发get请求,返回response,拿到response里的json数据赋值给myproducts

因为此时获取产品信息的路径为http://localhost:4200/product,而服务器的地址为http://localhost:8000/product,所以还要进行配置。
4.根目录下新建一个配置文件proxy.conf.json

将前缀为api的路径转发到http://localhost:8000

{
    "/api": {
        "target": "http://localhost:8000"
    }
}

5.修改package.json

6.修改serve文件下的auction_serve.ts

7.用下面的命令重启angular项目

ng serve --proxy-config proxy.conf.json

8.结果

方法二

在模版上使用异步管道async自动的订阅流
1.修改模版

<div>
    商品信息
</div>
<ul>
    <!-- 添加async管道 -->
    <li *ngFor="let pro of myproducts | async">
        {{pro.title}}
    </li>
</ul>

2.修改控制器

import { Component, OnInit } from '@angular/core';


import { Observable } from 'rxjs';
import { Http } from '@angular/http';
import 'rxjs/Rx';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html',
  styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
  // 流直接给myproducts
  myproducts:Observable<any>;

  constructor(private http:Http) { 
    this.myproducts = this.http.get('/api/product')
    .map((res) => res.json());
   }

  ngOnInit() {
    // 删除订阅
  }

}

3.结果相同

<3>websocket通讯

1、了解websocket协议

websocket是一种低负载的二进制协议。


2、创建websocket服务器

1.在serve中安装ws依赖库和类型文件

npm install ws --save

npm install @types/ws --save-dev

2.修改auction_serve.ts

import * as express from 'express';

// 引入服务
import {Server} from 'ws';

const app = express();
export class Product {
    constructor(
        public id:number,
        public title:string,
        public price:number,
        public rating:number,
        public desc:string
    ){}
}
const products:Product[] = [
    new Product(1,"商品1",1.99,1.5,"这是商品1"),
    new Product(2,"商品2",2.99,2.5,"这是商品2"),
    new Product(3,"商品3",3.99,3.5,"这是商品3"),
    new Product(4,"商品4",4.99,4.5,"这是商品4"),
    new Product(5,"商品5",5.99,4.5,"这是商品5")
];
app.get('/',(req, res) => {
    res.send("hello express");
});
app.get('/api/product',(req,res) => {
    res.json(products);
});
app.get('/api/product/:id',(req,res) => {
    res.json(
        products.find((product) => product.id == req.params.id)
    );
});
const serve = app.listen(8000,"localhost",() => {
    console.log("服务器已启动,地址为http://localhose:8000");
});
 
// 新建一个webscoket服务
const wsServer = new Server({port:8085});
wsServer.on("connection",websocket => {
    websocket.send("这个消息是服务器主动推送的");
});

3.写客户端的服务,在angular项目中生成一个服务service

ng g service shared/webSocket

出现问题:Error: ELOOP: too many symbolic links encountered
解决办法:删除node_modules文件夹, 重新npm install

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable()
export class WebSocketService {
  // 客户端的服务
  ws:WebSocket;
  constructor() { }

  // 定义返回一个流,这个流包含了服务器返回的消息
  creatObservableSocket(url:string) :Observable<any> {
    this.ws = new WebSocket(url);//连接服务器
    return new Observable(
      observer => {
        // 发送下一个元素
        this.ws.onmessage = (event) => observer.next(event.data);
        // 抛出异常
        this.ws.onerror = (event) => observer.error(event);
        // 流结束
        this.ws.onclose = (event) => observer.complete();
      }
    )
  }

  // 向服务器发送一个消息
  sendMess(message:string) {
    this.ws.send(message);
  }

}

4.客户端的组件,新建一个组件

ng g component webSocketComponent

1.修改web-socket-component.component.ts

import { Component, OnInit } from '@angular/core';
import { WebSocketService } from './../shared/web-socket.service';

@Component({
  selector: 'app-web-socket-component',
  templateUrl: './web-socket-component.component.html',
  styleUrls: ['./web-socket-component.component.css']
})
export class WebSocketComponentComponent implements OnInit {
  // 将刚刚写的客户端的服务WebSocketService通过依赖注入,注入到组件中来
  constructor(private wsService:WebSocketService) { }

  ngOnInit() {
    // 订阅服务器发来消息产生的流
    this.wsService.creatObservableSocket("ws://localhost:8085")
    .subscribe(
      data => console.log(data),
      err => console.log(err),
      () => console.log("流已经结束")
    )
  }
// 向服务器主动发送消息
sendMessagegToServer(){
  this.wsService.sendMess("这是客户端发过来的消息");
}
}

2.修改模版

<button (click)="sendMessagegToServer">向服务器发消息</button>

3.修改app.module.ts -- sproviders里使用提供器

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent } from './app.component';
import { ProductComponent } from './product/product.component';
import { HttpModule } from '@angular/http';
import { WsComponentComponent } from './ws-component/ws-component.component';
import { WebSocketComponentComponent } from './web-socket-component/web-socket-component.component';

// 引入
import { WebSocketService } from './shared/web-socket.service';

@NgModule({
  declarations: [
    AppComponent,
    ProductComponent,
    WsComponentComponent,
    WebSocketComponentComponent
  ],
  imports: [
  BrowserModule,
    HttpModule
  ],
  //使用依赖注入时,要在providers里使用提供器
  providers: [WebSocketService],
  bootstrap: [AppComponent]
})
export class AppModule { }

4.修改serve中auction_serve.ts

import * as express from 'express';
import {Server} from 'ws';
const app = express();

export class Product {
    constructor(
        public id:number,
        public title:string,
        public price:number,
        public rating:number,
        public desc:string
    ){}
}
const products:Product[] = [
    new Product(1,"商品1",1.99,1.5,"这是商品1"),
    new Product(2,"商品2",2.99,2.5,"这是商品2"),
    new Product(3,"商品3",3.99,3.5,"这是商品3"),
    new Product(4,"商品4",4.99,4.5,"这是商品4"),
    new Product(5,"商品5",5.99,4.5,"这是商品5")
];
app.get('/',(req, res) => {
    res.send("hello express");
});
app.get('/api/product',(req,res) => {
    res.json(products);
});
app.get('/api/product/:id',(req,res) => {
    res.json(
        products.find((product) => product.id == req.params.id)
    );
});
const serve = app.listen(8000,"localhost",() => {
    console.log("服务器已启动,地址为http://localhose:8000");
});
 
// 新建一个服务
const wsServer = new Server({port:8085});
wsServer.on("connection",websocket => {
    websocket.send("这个消息是服务器主动推送的");
    websocket.on("message",message => {
        console.log("message"+message);
    });
});

5.结果


5.定时推送
修改auction_serve.ts

setInterval(()=>{
    // 判断客户端是连接上的状态
    if(wsServer.clients) {
        wsServer.clients.forEach(client => {
            client.send("这是定时推送");
        })
    }
},2000);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容