JSON : Placeholder (https://jsonplaceholder.typicode.com/) 是一個用於測試的 REST API 網站。
如下使用 RxJS6 + React.js 調用該網站的 REST API,獲取字符串以及 JSON 數據。css
全部 GET API 都返回JSON數據,格式(JSON-Schema)以下:node
{ "type":"object", "properties": { "userId": {"type" : "integer"}, "id": {"type" : "integer"}, "title": {"type" : "string"}, "body": {"type" : "string"} } }
# 安裝 CLI $ npm install -g create-react-app # 建立新的應用程序 RxExample $ create-react-app rx-example --scripts-version=react-scripts-ts $ cd rx-example $ npm start
打開 Intellij IDEA, File / New / Project From Existing Sources...,而後選中工程所在文件夾
在嚮導的第1頁選擇 Create project from existing sources
完成嚮導後在點擊 Finish 建立工程。react
點擊 Add Configurations, 點擊 +npm
Name: React CLI Server
Scripts: start
點擊 OK 完成配置。
點擊 React CLI Server 啓動程序。
http://localhost:3000/ 可打開網頁。ios
打開 tslint.json,將其改成git
{ "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"], "linterOptions": { "exclude": [ "config/**/*.js", "node_modules/**/*.ts", "coverage/lcov-report/*.js" ] }, "rules": { "interface-name": false, "ordered-imports": false, "no-console": false, "object-literal-sort-keys": false, "member-access": false, "variable-name": false, "member-ordering": false, "class-name": false } }
在 src 文件夾下添加 post.ts,內容以下github
export class Post { userId!: number; id!: number; title!: string; body!: string; toString(): string { return `Post {userId = ${this.userId}, id = ${this.id}, title = "${this.title}", body = "${this.body.replace(/\n/g, '\\n')}"}`; } }
react.di 是一個在 React.js 中實現 DI(依賴注入) 的包。
使用這個包須要安裝 react.di 和 reflect-metadata。typescript
$ npm install react.di@next reflect-metadata --save
打開 tsconfig.json 在 compilerOptions 中
添加關於 emitDecoratorMetadata 的設定。npm
"emitDecoratorMetadata": true, "experimentalDecorators": true,
訪問 API 採用將 RxJS 和 axios 合爲一體的 Rxios 組件,可是該組件還沒有升級到 RxJS6。
這裏咱們只安裝 RxJS 和 axios,而後直接以源碼形式引入 Rxios,並將其升級到 RxJS6。json
$ npm install axios rxjs
在 src 文件夾下添加 rxios.ts,內容以下axios
// https://github.com/davguij/rxios/blob/master/src/index.ts // ported to rxjs6 import axios, { AxiosInstance, AxiosRequestConfig, AxiosPromise } from 'axios'; // import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs'; export interface rxiosConfig extends AxiosRequestConfig { localCache?: boolean; } class rxios { private _httpClient: AxiosInstance; constructor(options: rxiosConfig = {}) { this._httpClient = axios.create(options); } private _makeRequest<T>(method: string, url: string, queryParams?: object, body?: object) { let request: AxiosPromise<T>; switch (method) { case 'GET': request = this._httpClient.get<T>(url, {params: queryParams}); break; case 'POST': request = this._httpClient.post<T>(url, body, {params: queryParams}); break; case 'PUT': request = this._httpClient.put<T>(url, body, {params: queryParams}); break; case 'PATCH': request = this._httpClient.patch<T>(url, body, {params: queryParams}); break; case 'DELETE': request = this._httpClient.delete(url, {params: queryParams}); break; default: throw new Error('Method not supported'); } return new Observable<T>(subscriber => { request.then(response => { subscriber.next(response.data); subscriber.complete(); }).catch((err: Error) => { subscriber.error(err); subscriber.complete(); }); }); } public get<T>(url: string, queryParams?: object) { return this._makeRequest<T>('GET', url, queryParams); } public post<T>(url: string, body: object, queryParams?: object) { return this._makeRequest<T>('POST', url, queryParams, body); } public put<T>(url: string, body: object, queryParams?: object) { return this._makeRequest<T>('PUT', url, queryParams, body); } public patch<T>(url: string, body: object, queryParams?: object) { return this._makeRequest<T>('PATCH', url, queryParams, body); } public delete(url: string, queryParams?: object) { return this._makeRequest('DELETE', url, queryParams); } } export {rxios, rxios as Rxios};
在 src 文件夾下添加 post.service.ts,內容以下
import { Injectable } from 'react.di'; import { Observable, from } from 'rxjs'; import { map, mergeAll, take, tap } from 'rxjs/operators'; import { Post } from './post'; import { Rxios } from './rxios'; @Injectable export class PostService { private readonly http = new Rxios(); private readonly baseUrl = 'http://jsonplaceholder.typicode.com/'; constructor() { this.getPostAsString().subscribe(); this.getPostAsJson().subscribe(); this.getPosts(2).subscribe(); this.createPost().subscribe(); this.updatePost().subscribe(); this.deletePost().subscribe(); } private getPostAsString(): Observable<string> { const url = `${this.baseUrl}posts/1`; return new Rxios({transformResponse: undefined}).get<string>(url) .pipe( tap((result: any) => console.log(result)), ); } private getPostAsJson(): Observable<Post> { const url = `${this.baseUrl}posts/1`; return this.http.get<Post>(url) .pipe( map((result: any) => Object.assign(new Post(), result)), tap((result: any) => console.log('' + result)), ); } private getPosts(n: number): Observable<Post> { const url = `${this.baseUrl}posts`; return from(this.http.get<Post[]>(url)) .pipe( mergeAll(), map((result: any) => Object.assign(new Post(), result)), take(n), tap((result: any) => console.log('' + result)), ); } private createPost(): Observable<string> { const url = `${this.baseUrl}posts`; return this.http.post(url, { params: { userId: 101, title: 'test title', body: 'test body', } }) .pipe( map((result: any) => JSON.stringify(result)), tap((result: any) => console.log(result)), ); } private updatePost(): Observable<string> { const url = `${this.baseUrl}posts/1`; return this.http.put(url, { params: { userId: 101, title: 'test title', body: 'test body', } }) .pipe( map((result: any) => JSON.stringify(result)), tap((result: any) => console.log(result)), ); } private deletePost(): Observable<string> { const url = `${this.baseUrl}posts/1`; return this.http.delete(url) .pipe( map((result: any) => JSON.stringify(result)), tap((result: any) => console.log(result)), ); } }
打開 App.tsx,將其改成
import * as React from 'react'; import './App.css'; import logo from './logo.svg'; import { PostService } from './post.service'; import { Inject, Module } from 'react.di'; @Module({ providers: [ PostService, ], }) class App extends React.Component { @Inject postService!: PostService; componentDidMount() { console.log(this.postService); } public render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> To get started, edit <code>src/App.tsx</code> and save to reload. </p> </div> ); } } export default App;
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" } Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"} Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"} Post {userId = 1, id = 2, title = "qui est esse", body = "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"} {"params":{"userId":101,"title":"test title","body":"test body"},"id":101} {"params":{"userId":101,"title":"test title","body":"test body"},"id":1} {}