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;

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

  // 将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.修改模版

商品信息
  • {{pro.title}}

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;

  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 {
    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.修改模版


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);

你可能感兴趣的:(angular (6)与服务器通信)