发布操作不适用于Angular 4

我正在使用angular 4来学习node.js。我为简单的get / post请求构build了一个样本节点api。 我的get操作工作正常,我能够angular度获取数据。 我的后期操作没有得到任何angular度调用。 如果我使用POSTMAN,我可以成功地调用发布,数据也可以插入到数据库中。

这是我的节点发布示例代码

app.post('/groups', function (req, res, next){ res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type"); res.header("Access-Control-Allow-Methods", "GET, POST","PUT"); console.log('Request received with body' + req.body); //DEV AWS MySQL var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'xxxxxxx', user : 'xxxxxxx', password : 'xxxxxxx', database : 'xxxxxxx', port : 3306 }); connection.connect(); connection.query('CALL storedprocedure(?, ?, ?, ?, ?, ?)', [req.body.group_avatar_image,req.body.name,req.body.display_name,req.body.unique_id,req.body.description,req.body.adzone], function (err, results, fields){ if (err) res.send(results); //res.status(201).send("Groups created successfully"); res.status(201).send(results[0]); }); 

这与POSTMAN正常工作,我可以得到201 …

这是我的angular4代码

  import { Injectable } from '@angular/core'; import { Http, Response,RequestOptions, Request, RequestMethod, Headers} from '@angular/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/do'; import { Group } from './group'; @Injectable() export class GroupsService{ private _GroupsUrl = 'http://localhost:5000/api/groups'; constructor(private _http: Http){}; getGroups(): Observable<Group[]> { let headers = new Headers({ 'Content-Type': 'application/json' }); headers.append('Accept', 'application/json'); headers.append('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT'); headers.append('Access-Control-Allow-Origin', '*'); //headers.append('Access-Control-Allow-Headers', "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"); let options = new RequestOptions({ method: RequestMethod.Post, headers: headers, url:this._GroupsUrl }); //debugger; return this._http.get(this._GroupsUrl) .map((Response: Response) => <Group[]>Response.json()[0]) //.do(data => console.log ('ALL: ' + JSON.stringify(data))) .catch(this.handleError); } CreateGroup(GroupM): Observable<string>{ let headers = new Headers({ 'Content-Type': 'application/json' }); headers.append('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT, OPTIONS'); headers.append('Access-Control-Allow-Origin', 'http://localhost:4200'); headers.append('Access-Control-Allow-Headers', "X-Requested-With, Content-Type"); //let options = new RequestOptions({ method: RequestMethod.Post, headers: headers, body:JSON.stringify(GroupM), url:this._GroupsUrl }); let options = new RequestOptions({ method: RequestMethod.Post}); console.log('Calling ' + this._GroupsUrl + ' with body as :' + JSON.stringify(GroupM) + ' and request options are : ' + JSON.stringify(options)); var req = new Request(options.merge({ url: this._GroupsUrl })); debugger; //return this._http.post(this._GroupsUrl,GroupM) return this._http.post(req.url,JSON.stringify(GroupM),options) .map(res => res.json()) .do(data => console.log ('ALL: ' + JSON.stringify(data))) .catch(this.handleError); } private handleError(error:Response) { console.error(error); return Observable.throw(error.json().error || 'Server Error'); } } 

这里有什么问题?

       

网上收集的解决方案 "发布操作不适用于Angular 4"

最后能够使用诺言解决它,它解决了这个问题。 不知道究竟是什么问题observable。

 > CreateGroup(GroupObj:Group) : Promise<Group>{ return this._http .post(this._GroupsUrl,JSON.stringify(GroupObj),{headers: this.headers}) .toPromise() .then(res => res.json().data as Group) .catch(this.handleError); } 

首先,请帮个忙,包装Angular的Http服务,这样就不必为每个请求手动添加一个authentication令牌和头部信息。 这是一个简单的实现,你可以build立:

首先让我们创build一个Cookies服务,它将作为localStorage不受支持的回退:

 @Injectable() export class Cookies { public static getItem(sKey) { if (!sKey) { return null; } return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; } public static setItem(sKey?, sValue?, vEnd?, sPath?, sDomain?, bSecure?) { if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } let sExpires = ''; if (vEnd) { switch (vEnd.constructor) { case Number: sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; break; case String: sExpires = "; expires=" + vEnd; break; case Date: sExpires = "; expires=" + vEnd.toUTCString(); break; } } document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); return true; } public static removeItem(sKey, sPath?, sDomain?) { if (!this.hasItem(sKey)) { return false; } document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : ""); return true; } public static hasItem(sKey) { if (!sKey) { return false; } return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); } public static keys() { let aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/); for (let nLen = aKeys.length, nIdx = 0; nIdx < nLen; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); } return aKeys; } } 

然后是一个存储logging器,用于跟踪添加到存储的事物(当更改每个请求时更新身份validation令牌时有用):

 import {Cookies} from '@services/cookies.service'; @Injectable() export class StorageLogger { private logger = new BehaviorSubject<any>(null); public logger$ = this.logger.asObservable(); set(key: string, value: any): void { try { localStorage.setItem(key, JSON.stringify(value)); } catch(err) { Cookies.setItem(key, JSON.stringify(value)); } this.get(key); } get(key: string) { let item: any; try { item = JSON.parse(localStorage.getItem(key)); } catch(err) { item = JSON.parse(Cookies.getItem(key)); } this.logger.next({value: item, key: key}); } remove(keys: string[]) { try { for (const key of keys) { localStorage.removeItem(key); this.logger.next({value: null, key: key}); } } catch(err) { for (const key of keys) { Cookies.removeItem(key); this.logger.next({value: null, key: key}); } } } } 

那么你想包装angular的Http

 @Injectable() /* Wrapper for Angular's Http class, let's us provide headers and other things on every request */ export class HttpClient implements OnDestroy { constructor( private http: Http, private storageLogger: StorageLogger ) { this.getToken(); this.storageSubscription = this.storageLogger.logger$.subscribe( (action: any) => { if (action && action.key === tokenIdKey) { this.getToken(); } } ); } private storageSubscription: Subscription; private token: string; ngOnDestroy() { this.storageSubscription.unsubscribe(); } getToken(): void { try { this.token = localStorage.getItem(tokenIdKey); } catch(error) { this.token = Cookies.getItem(tokenIdKey); } } convertJSONtoParams(json: any): URLSearchParams { const params: URLSearchParams = new URLSearchParams(); for (const key in json) { if (json.hasOwnProperty(key) && json[key]) { if (json[key].constructor === Array && !json[key].length) { continue; } else { params.set(key, json[key]); } } } return params; } getRequestOptions(params?: any): RequestOptions { const headers = new Headers(); // headers.append('Content-Type', 'application/x-www-form-urlencoded'); headers.append('Content-Type', 'application/json'); this.createAuthorizationHeader(headers); return new RequestOptions({ headers: headers, search: params ? this.convertJSONtoParams(params) : null }); } createAuthorizationHeader(headers: Headers): void { headers.append('Authorization', this.token); } checkResponseStatus(err: any) { if (err.status === 401) { // If we want we can redirect to login here or something else } return Observable.of(err); } get(url: string, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.get(host + url, options).catch((err: Response) => this.checkResponseStatus(err)); } post(url: string, data: any, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.post(host + url, data, options).catch((err: Response) => this.checkResponseStatus(err)); } put(url: string, data: any, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.put(host + url, data, options).catch((err: Response) => this.checkResponseStatus(err)); } delete(url: string, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.delete(host + url, options).catch((err: Response) => this.checkResponseStatus(err)); } patch(url: string, data: any, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.patch(host + url, data, options).catch((err: Response) => this.checkResponseStatus(err)); } head(url: string, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.head(host + url, options).catch((err) => this.checkResponseStatus(err)); } options(url: string, params?: any): Observable<Response> { const options: RequestOptions = this.getRequestOptions(params); return this.http.options(host + url, options).catch((err: Response) => this.checkResponseStatus(err)); } } 

最后,你还应该添加一个通用的api服务,而不是为你的应用程序的每个部分创build一个新的服务。 这将为您节省大量的代码和精力。 这里是:

 import {IResponse} from '@interfaces/http/response.interface'; import {HttpClient} from '@services/http/http-client.service'; @Injectable() export class AppApi { constructor(private http: HttpClient) {} get(url: string, params?: any): Observable<IResponse> { return this.http.get(url, params) .map((res: Response) => res.json() as IResponse) .catch((error: any) => { return Observable.throw(error.json().error || 'Server error'); } ); } post(url: string, data: any, params?: any) { return this.http.post(url, data, params) .map((res: Response) => res.json() as IResponse) .catch((error: any) => { return Observable.throw(error.json().error || 'Server error'); } ); } put(url: string, data: any, params?: any) { return this.http.put(url, data, params) .map((res: Response) => res.json() as IResponse) .catch((error: any) => { return Observable.throw(error.json().error || 'Server error'); } ); } delete(url: string, params?: any): Observable<IResponse> { return this.http.delete(url, params) .map((res: Response) => res.json() as IResponse) .catch((error: any) => { return Observable.throw(error.json().error || 'Server error'); } ); } } 

你会注意到我也创build了一个接口,从后端input我的响应,通常是这样的:

 {error: any; data: any; results: number; total: number;} 

现在我们已经处理了这些问题,让我们来解决你的原始问题。 为什么你的请求没有运行,最可能的原因是你没有订阅http observable。 观察对象是懒惰的,所以如果你不通过.subscribe或者@ngrx/effects来订阅它,它就不会做任何事情。

那么让我们假设你是这样调用CreateGroup

 this.groupsService.CreateGroup(data); 

在你订阅之前,这不会做任何事情:

 this.groupsService.CreateGroup(data).subscribe(() => { // Here you can react to the post, close a modal, redirect or whatever you want. }); 

我还build议添加一个.first()你的api调用,因为这将防止你不得不手动取消订阅组件被销毁。

所以要使用上面的实现,你只需要:

 constructor(private appApi: AppApi) {} ... this.appApi.post('/groups').first().subscribe(() => { // Do something }); 

我希望这是有帮助的。

不要在HTTP post中将发布数据串联起来。 只需传递对象。