如何 return Observable inside a subscription
How to return Observable inside a subscription
我想在 observable 中调用 http 请求,它从数据库进行 select 操作。我做了两个服务,DbService 和 BackendService。
BackendService 发出 http post 请求和 returns 响应数据。在我的设计中,BackendService 应该订阅 DbService 以获取 url,然后发出 http post 请求然后 return 响应数据。
BackendService 可以从 DbService 获取 url 并尝试发出 http 请求但不能。响应数据为(Json 格式)
{"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
我不明白这里发生了什么。我的服务和 AppComponent 文件如下。
有BackendService
import { Injectable } from "@angular/core";
import { getString, setString } from "application-settings";
import { Headers, Http, Response, RequestOptions } from "@angular/http";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";
import { DbService } from "./db.service";
@Injectable()
export class BackendService {
static BaseUrl= "http://blabla.com"
constructor(public http: Http, private db: DbService) {
}
sendPost(key: string, requestObj: Object):Observable<any>{
console.log("sendPost: ");
return new Observable(obs=> {
let obs1 = this.db.getActionUrl(key);
obs1.subscribe(value => {
let url = BackendService.BaseUrl + value;
console.log("key: ", key);
console.log("url: ", url);
var h = BackendService.getHeaders();
obs.next(this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json()));
// .catch((error: any) => Observable.throw(error.json())));
obs.complete();
}
, error => {
console.error("send post error: "+ error);
obs.error(error);
}
);
});
}
static getHeaders() {
let headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("SESSION-ID", this.sessionId);
// headers.append("Authorization", BackendService.appUserHeader);
return headers;
}
}
有DbService
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";
import 'rxjs/add/operator/toPromise';
var Sqlite = require("nativescript-sqlite");
@Injectable()
export class DbService {
private tableActions = "actions";
private columnActionName = "name";
private columnActionUrl = "url";
private database: any;
constructor() {
console.log("DbService Constructor");
(new Sqlite("my_app.db")).then(db => {
db.execSQL("CREATE TABLE IF NOT EXISTS " + this.tableActions + " (" + this.columnActionName + " TEXT PRIMARY KEY, " + this.columnActionUrl +" TEXT)").then(id => {
this.database = db;
console.log("DB SERVICE READY");
}, error => {
console.log("CREATE TABLE ERROR", error);
});
}, error => {
console.log("OPEN DB ERROR", error);
});
}
public getActionUrl(key: string):Observable<any>{
return new Observable(observer => {
if (key === "getApiMap") {
observer.next("/getApiMap");
observer.complete();
return;
}
console.log("getActionUrl :" + key);
this.database.all("SELECT * FROM " + this.tableActions).then(
rows => {
console.log(rows);
observer.next(rows[0][this.columnActionUrl]);
observer.complete();
}, error => {
console.log("SELECT ERROR: getActionUrl: ", error);
observer.error(error);
})
});
}
}
我的 AppComponent 发出 http 请求...
//some imports
export class AppComponent {
public resp: Observable<ModelMainGetApiMapRes>;
public constructor(private bs: BackendService, private db: DbService) {
let req = new ModelMainGetApiMapReq()
bs.sendPost("getApiMap", req, false).subscribe(
(res: ModelMainGetApiMapRes) => {
console.log("getapimap response received!");
console.log(JSON.stringify(res));
console.log("apimap version:" + res.api_version);
},
err => {
console.error("error!", err);
}
);
}
//some functions
}
app.component 的控制台输出是
CONSOLE LOG file:///app/shared/backend.service.js:61:20: sendPost:
CONSOLE LOG file:///app/shared/backend.service.js:66:28: key: getApiMap
CONSOLE LOG file:///app/shared/backend.service.js:67:28: url: http://blabla.com/getApiMap
CONSOLE LOG file:///app/app.component.js:55:36: getapimap response received!
CONSOLE LOG file:///app/app.component.js:56:36: {"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
CONSOLE LOG file:///app/tns_modules/tns-core-modules/profiling/profiling.js:10:16: ANGULAR BOOTSTRAP DONE. 7805.849
CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:1486:24: ERROR Error: Uncaught (in promise): TypeError: undefined is not an object (evaluating 'res.api_version')
用你在BackendService.ts中的实际代码:
return new Observable(obs=> {
let obs1 = this.db.getActionUrl(key);
obs1.subscribe(value => {
let url = BackendService.BaseUrl + value;
console.log("key: ", key);
console.log("url: ", url);
var h = BackendService.getHeaders();
obs.next(this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json()));
obs.complete();
...
});
});
您发出了 http observable
this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json())
这就是为什么你得到:{"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
当订阅它时,它是一个可观察的。
最简单的代码解决方案,您可以在订阅第二个可观察对象后发出数据,例如:
return new Observable(obs=> {
let obs1 = this.db.getActionUrl(key);
obs1.subscribe(value => {
let url = BackendService.BaseUrl + value;
console.log("key: ", key);
console.log("url: ", url);
var h = BackendService.getHeaders();
this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json())
.subscribe(data => obs.next(data));
});
});
但更好的解决方案 是使用 switchMap 运算符:(或任何其他 xxxxMap 运算符)
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
...
sendPost(key: string, requestObj: Object):Observable<any>{
return this.db.getActionUrl(key)
.map( value => BackendService.BaseUrl + value)
.switchMap(url => this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
)
.map((res: Response) => res.json()))
}
我想在 observable 中调用 http 请求,它从数据库进行 select 操作。我做了两个服务,DbService 和 BackendService。
BackendService 发出 http post 请求和 returns 响应数据。在我的设计中,BackendService 应该订阅 DbService 以获取 url,然后发出 http post 请求然后 return 响应数据。
BackendService 可以从 DbService 获取 url 并尝试发出 http 请求但不能。响应数据为(Json 格式)
{"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
我不明白这里发生了什么。我的服务和 AppComponent 文件如下。
有BackendService
import { Injectable } from "@angular/core";
import { getString, setString } from "application-settings";
import { Headers, Http, Response, RequestOptions } from "@angular/http";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";
import { DbService } from "./db.service";
@Injectable()
export class BackendService {
static BaseUrl= "http://blabla.com"
constructor(public http: Http, private db: DbService) {
}
sendPost(key: string, requestObj: Object):Observable<any>{
console.log("sendPost: ");
return new Observable(obs=> {
let obs1 = this.db.getActionUrl(key);
obs1.subscribe(value => {
let url = BackendService.BaseUrl + value;
console.log("key: ", key);
console.log("url: ", url);
var h = BackendService.getHeaders();
obs.next(this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json()));
// .catch((error: any) => Observable.throw(error.json())));
obs.complete();
}
, error => {
console.error("send post error: "+ error);
obs.error(error);
}
);
});
}
static getHeaders() {
let headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("SESSION-ID", this.sessionId);
// headers.append("Authorization", BackendService.appUserHeader);
return headers;
}
}
有DbService
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";
import 'rxjs/add/operator/toPromise';
var Sqlite = require("nativescript-sqlite");
@Injectable()
export class DbService {
private tableActions = "actions";
private columnActionName = "name";
private columnActionUrl = "url";
private database: any;
constructor() {
console.log("DbService Constructor");
(new Sqlite("my_app.db")).then(db => {
db.execSQL("CREATE TABLE IF NOT EXISTS " + this.tableActions + " (" + this.columnActionName + " TEXT PRIMARY KEY, " + this.columnActionUrl +" TEXT)").then(id => {
this.database = db;
console.log("DB SERVICE READY");
}, error => {
console.log("CREATE TABLE ERROR", error);
});
}, error => {
console.log("OPEN DB ERROR", error);
});
}
public getActionUrl(key: string):Observable<any>{
return new Observable(observer => {
if (key === "getApiMap") {
observer.next("/getApiMap");
observer.complete();
return;
}
console.log("getActionUrl :" + key);
this.database.all("SELECT * FROM " + this.tableActions).then(
rows => {
console.log(rows);
observer.next(rows[0][this.columnActionUrl]);
observer.complete();
}, error => {
console.log("SELECT ERROR: getActionUrl: ", error);
observer.error(error);
})
});
}
}
我的 AppComponent 发出 http 请求...
//some imports
export class AppComponent {
public resp: Observable<ModelMainGetApiMapRes>;
public constructor(private bs: BackendService, private db: DbService) {
let req = new ModelMainGetApiMapReq()
bs.sendPost("getApiMap", req, false).subscribe(
(res: ModelMainGetApiMapRes) => {
console.log("getapimap response received!");
console.log(JSON.stringify(res));
console.log("apimap version:" + res.api_version);
},
err => {
console.error("error!", err);
}
);
}
//some functions
}
app.component 的控制台输出是
CONSOLE LOG file:///app/shared/backend.service.js:61:20: sendPost:
CONSOLE LOG file:///app/shared/backend.service.js:66:28: key: getApiMap
CONSOLE LOG file:///app/shared/backend.service.js:67:28: url: http://blabla.com/getApiMap
CONSOLE LOG file:///app/app.component.js:55:36: getapimap response received!
CONSOLE LOG file:///app/app.component.js:56:36: {"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
CONSOLE LOG file:///app/tns_modules/tns-core-modules/profiling/profiling.js:10:16: ANGULAR BOOTSTRAP DONE. 7805.849
CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:1486:24: ERROR Error: Uncaught (in promise): TypeError: undefined is not an object (evaluating 'res.api_version')
用你在BackendService.ts中的实际代码:
return new Observable(obs=> {
let obs1 = this.db.getActionUrl(key);
obs1.subscribe(value => {
let url = BackendService.BaseUrl + value;
console.log("key: ", key);
console.log("url: ", url);
var h = BackendService.getHeaders();
obs.next(this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json()));
obs.complete();
...
});
});
您发出了 http observable
this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json())
这就是为什么你得到:{"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
当订阅它时,它是一个可观察的。
最简单的代码解决方案,您可以在订阅第二个可观察对象后发出数据,例如:
return new Observable(obs=> {
let obs1 = this.db.getActionUrl(key);
obs1.subscribe(value => {
let url = BackendService.BaseUrl + value;
console.log("key: ", key);
console.log("url: ", url);
var h = BackendService.getHeaders();
this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
).map((res: Response) => res.json())
.subscribe(data => obs.next(data));
});
});
但更好的解决方案 是使用 switchMap 运算符:(或任何其他 xxxxMap 运算符)
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
...
sendPost(key: string, requestObj: Object):Observable<any>{
return this.db.getActionUrl(key)
.map( value => BackendService.BaseUrl + value)
.switchMap(url => this.http.post(
url,
JSON.stringify(requestObj),
{ headers: h }
)
.map((res: Response) => res.json()))
}