angular 英雄之旅 - 步骤 8/HTTP - 内存中的 webapi 返回未定义
angular tour of heroes - step 8/HTTP - in-memory webapi returning undefined
问题:内存中的网络 api 会 return 'undefined' 当尝试使用 HeroService.getHeroes() 获取所有英雄时,如本教程第 8 步所述.
InMemoryDataService 实现:
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
// This constant has a LOWERCASE name in the tutorial.
// The code below is copied-pasted from the mock-heroes service file.
const HEROES = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
return { HEROES };
}
genId(heroes: Hero[]): number {
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
}
constructor() { }
}
使用 HTTP 的 HeroesService 实现(与当前问题相关的摘录)
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Hero } from './hero';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { MessageService } from './message.service';
@Injectable({
providedIn: 'root'
})
export class HeroService {
private heroesUrl = 'api/heroes';
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.get<Hero>(url).pipe(
tap(_ => this.log(`fetched hero id=${id}`)),
catchError(this.handleError<Hero>(`getHero id =${id}`))
);
}
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
tap(heroes => this.log('fetched heroes')),
catchError(this.handleError('getHeroes', [])));
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
handleError<T>(operation = 'operation', result?: T): any {
return (error: any): Observable<T> => {
// TODO send the error to remote logging infrastructure
console.error(error);
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result
return of(result as T);
};
}
private log(message: string) {
this.messageService.add(`HeroService: ${message}`);
}
constructor(private http: HttpClient, private messageService: MessageService) { }
}
package.json:
{
"name": "angular-tour-of-heroes",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^6.1.0",
"@angular/common": "^6.1.0",
"@angular/compiler": "^6.1.0",
"@angular/core": "^6.1.0",
"@angular/forms": "^6.1.0",
"@angular/http": "^6.1.0",
"@angular/platform-browser": "^6.1.0",
"@angular/platform-browser-dynamic": "^6.1.0",
"@angular/router": "^6.1.0",
"angular-in-memory-web-api": "^0.6.1",
"core-js": "^2.5.4",
"rxjs": "^6.0.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.7.0",
"@angular/cli": "~6.1.5",
"@angular/compiler-cli": "^6.1.0",
"@angular/language-service": "^6.1.0",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~1.7.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.0",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~5.0.1",
"tslint": "~5.9.1",
"typescript": "~2.7.2"
}
}
运行 应用程序时的错误消息:
HeroService: getHeroes failed: undefined
为什么内存中的网络 api 无法将对 'api/heroes' 的请求映射到我的数据?
TL;DR:您在 InMemoryDataService 实现中定义的 constants/properties 必须与您的 API (see end of paragraph 'HTTP request handling' and next paragraph) 的资源 URI 相匹配。
考虑了大小写(请参阅实施中的评论),这是问题中报告的意外错误的原因。
修复了 InMemoryDataService 实现(带有注释):
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
// SOLUTION & EXPLANATION
// this constant has to have the same name as the resource url the in-memory service will be invoked with.
// e.g. heroes2 => api/heroes2
// note: do not forget that urls are case-sensitive ;) (see
const heroes = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
return { heroes };
}
genId(heroes: Hero[]): number {
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
}
constructor() { }
}
问题:内存中的网络 api 会 return 'undefined' 当尝试使用 HeroService.getHeroes() 获取所有英雄时,如本教程第 8 步所述.
InMemoryDataService 实现:
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
// This constant has a LOWERCASE name in the tutorial.
// The code below is copied-pasted from the mock-heroes service file.
const HEROES = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
return { HEROES };
}
genId(heroes: Hero[]): number {
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
}
constructor() { }
}
使用 HTTP 的 HeroesService 实现(与当前问题相关的摘录)
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Hero } from './hero';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { MessageService } from './message.service';
@Injectable({
providedIn: 'root'
})
export class HeroService {
private heroesUrl = 'api/heroes';
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.get<Hero>(url).pipe(
tap(_ => this.log(`fetched hero id=${id}`)),
catchError(this.handleError<Hero>(`getHero id =${id}`))
);
}
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
tap(heroes => this.log('fetched heroes')),
catchError(this.handleError('getHeroes', [])));
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
handleError<T>(operation = 'operation', result?: T): any {
return (error: any): Observable<T> => {
// TODO send the error to remote logging infrastructure
console.error(error);
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result
return of(result as T);
};
}
private log(message: string) {
this.messageService.add(`HeroService: ${message}`);
}
constructor(private http: HttpClient, private messageService: MessageService) { }
}
package.json:
{
"name": "angular-tour-of-heroes",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^6.1.0",
"@angular/common": "^6.1.0",
"@angular/compiler": "^6.1.0",
"@angular/core": "^6.1.0",
"@angular/forms": "^6.1.0",
"@angular/http": "^6.1.0",
"@angular/platform-browser": "^6.1.0",
"@angular/platform-browser-dynamic": "^6.1.0",
"@angular/router": "^6.1.0",
"angular-in-memory-web-api": "^0.6.1",
"core-js": "^2.5.4",
"rxjs": "^6.0.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.7.0",
"@angular/cli": "~6.1.5",
"@angular/compiler-cli": "^6.1.0",
"@angular/language-service": "^6.1.0",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~1.7.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.0",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~5.0.1",
"tslint": "~5.9.1",
"typescript": "~2.7.2"
}
}
运行 应用程序时的错误消息:
HeroService: getHeroes failed: undefined
为什么内存中的网络 api 无法将对 'api/heroes' 的请求映射到我的数据?
TL;DR:您在 InMemoryDataService 实现中定义的 constants/properties 必须与您的 API (see end of paragraph 'HTTP request handling' and next paragraph) 的资源 URI 相匹配。 考虑了大小写(请参阅实施中的评论),这是问题中报告的意外错误的原因。
修复了 InMemoryDataService 实现(带有注释):
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
// SOLUTION & EXPLANATION
// this constant has to have the same name as the resource url the in-memory service will be invoked with.
// e.g. heroes2 => api/heroes2
// note: do not forget that urls are case-sensitive ;) (see
const heroes = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
return { heroes };
}
genId(heroes: Hero[]): number {
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
}
constructor() { }
}