[英]Post operation not working with Angular 4
我正在使用Angular 4學習Node.JS。我為簡單的GET / POST請求構建了一個示例Node API。 我的GET操作工作正常,我能夠在Angular中獲取數據。 我的OST操作根本沒有從Angular調用。 如果使用Postman,則可以成功調用POST,並且數據也將插入數據庫中。
這是我的Node POST示例代碼:
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。
這是我的Angular 4代碼:
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');
}
}
怎么了
最終能夠使用Promise解決它並解決了問題。 不確定可觀察的問題到底是什么。
> 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
服務,這樣您就不必為每個請求手動添加auth令牌和標頭。 這是一個可以建立的簡單實現:
首先,讓我們創建一個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;
}
}
然后是一個存儲記錄器,該記錄器跟蹤添加到存儲中的內容(可用於在請求更改時為每個請求更新auth令牌):
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服務,而不是為應用程序的每個部分創建一個新服務。 這將節省大量代碼和精力。 這里是:
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');
}
);
}
}
您會注意到,我還創建了一個接口,用於輸入來自后端的響應,通常是這樣的:
{error: any; data: any; results: number; total: number;}
現在我們已經解決了這些問題,讓我們解決您的原始問題。 關於您的請求為何未運行的最可能的原因是,您沒有訂閱可觀察的http
。 可觀察對象是懶惰的,因此,如果您不通過.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.
});
我還建議向您的api調用添加.first()
,因為這樣做可以防止您在組件被破壞時手動取消訂閱可觀察對象。
因此,要使用上述實現,您只需執行以下操作:
constructor(private appApi: AppApi) {}
...
this.appApi.post('/groups').first().subscribe(() => {
// Do something
});
我希望這是有幫助的。
不要在HTTP POST中對POST數據進行字符串化處理。 只需傳遞對象即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.