Post operation not working with Angular 4 - node.js

I'm learning Node.JS with Angular 4. I build a sample Node API for simple GET/POST request. My GET operation works fine and I am able to fetch data in Angular. My OST operation isn't getting called at all from Angular. If I use Postman, I'm able to call POST successfully and data also gets inserted in database.
Here is my sample code for 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]);
});
This works fine with Postman and I get 201.
Here is my Angular 4 code:
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');
}
}
What is wrong here?

Finally able to resolve it using promise and it resolves the issue. Not sure what exactly is the issue with 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);
}

First of all, do yourself a favour and wrap Angular's Http service so that you don't have to manually add an auth token and headers for every request. Here's a simple implementation which you can build on:
First of all let's create a Cookies service which will act as a fallback where localStorage isn't supported:
#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;
}
}
Then a storage logger which keeps track of things added to the storage (useful for updating the auth token for every request when it changes):
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});
}
}
}
}
Then you want to wrap angular's 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));
}
}
And finally you should also add a generic api service which you will call, instead of creating a new service for every part of your application. This will save you a lot of code and effort. Here it is:
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');
}
);
}
}
You'll notice that I've also created an interface which types up my response from the backend, which is usually something like:
{error: any; data: any; results: number; total: number;}
Now that we've taken care of those problems, let's tackle your original question. The most likely reason as to why your request isn't running, is that you're not subscribing to the http observable. Observables are lazy so if you don't subscribe to it via .subscribe or #ngrx/effects, it just won't do anything.
So let's assume that you're calling CreateGroup like this:
this.groupsService.CreateGroup(data);
This won't do anything until you subscribe:
this.groupsService.CreateGroup(data).subscribe(() => {
// Here you can react to the post, close a modal, redirect or whatever you want.
});
I'd also recommend adding a .first() to your api calls as this will prevent you from having to unsubscribe from the observables manually when the component is destroyed.
So to use the implementation as above you'd simply do:
constructor(private appApi: AppApi) {}
...
this.appApi.post('/groups').first().subscribe(() => {
// Do something
});
I hope this is helpful.

Don't stringify the POST data in HTTP POST. Simply pass the object.

Related

How to create mock function and have good coverage

I am finding it difficult to have code coverage and mocking request and few functions
cf.service.ts
import { omit } from "lodash";
var request = require('request');
const callForwardConfig = require('../../config/callForwardConfig').callForwardConfig;
import logger from "../../utils/logger";
import { SetCallforwardAs } from '../../interfaces/callforward.interface';
export async function appSerGetCallForwardingState(sid: string, token: string) {
try {
return await callForwardApiCall(sid, token).then((res) => {
return res;
})
} catch (e: any) {
throw new Error(e);
}
}
function callForwardApiCall(sid: string, token: string) {
let callforwardUrl = callForwardConfig.url.as;
return new Promise((resolve, reject) => {
request(`${callforwardUrl}?userId=${sid}`, {
method: 'get',
strictSSL: false,
mode: 'no-cors',
json: true,
headers: { 'Content-Type': 'application/json', Authorization: token},
}, (err: any, response: any, body: any) => {
if (err) {
reject(JSON.stringify(err))
} else {
resolve(body);
}
})
});
}
export async function putAppserCallForward(token: string, callForwardObj: SetCallforwardAs) {
return await updateCallForwardAs(token, callForwardObj).then((res) => {
return res;
})
}
async function updateCallForwardAs(token: string, callForwardObj: SetCallforwardAs) {
let callforwardUrl = callForwardConfig.url.as;
return await new Promise((resolve, reject) => {
let body = {
clusters: callForwardObj.clusters,
name: callForwardObj.name,
destination: callForwardObj.destination,
user: callForwardObj.user
}
logger.info(`App server update cfwrd Request object - ${JSON.stringify(body)}`)
request(`${callforwardUrl}`, {
method: 'put',
strictSSL: false,
mode: 'no-cors',
json: true,
body: body,
headers: { 'Content-Type': 'application/json', Authorization: token},
}, (err: any, response: any, body: any) => {
if (err) {
logger.error(`App server call forward update failure USER - ${callForwardObj.sid}`, JSON.stringify(err));
reject(JSON.stringify(err));
} else {
if (!body['success'])
logger.error(`App server call forward update failure USER - ${callForwardObj.sid} - Error - ${JSON.stringify(body)}`);
else
logger.info(`App server call forward update success USER - ${callForwardObj.sid}`);
resolve(body);
}
})
});
}
I have written test as below:
import createServer from "../../utils/server";
const appserService = require('../../service/provider/appser.service');
const request = require('request');
const app = createServer();
jest.mock('request');
const sid = 'A121';
describe("appserver service", () => {
it("appSerGetCallForwardingState", async () => {
const callForwardApiCallMock = jest.spyOn(appserService, 'callForwardApiCall');
callForwardApiCallMock.mockImplementation(() => {
return Promise.resolve('success');
});
appserService.appSerGetCallForwardingState(sid, 'token').then((res: any) => {
expect(res).toBe('success');
});
});
it("callForwardApiCall", async () => {
request.get.mockResolvedValue({ "success": "true" });
appserService.callForwardApiCall(sid, 'token').then((res: any) => {
expect(res).toBe({ "success": "true" });
});
});
it("callForwardApiCall error", async () => {
request.get.mockRejectedValue(new Error('error'));
appserService.callForwardApiCall(sid, 'token').then((res: any) => {
expect(res).toBe({ "success": "true" });
});
});
});
I am struggling to have good code coverage at - least 90%.
request object also needs to be mocked, and functions are not being exported like callForwardApiCall also not able to access from test file
Here is the report:

MEAN stack delete request has 404 not found error

My post request and list all request are working fine, but I have problem getting my delete request to work. I have tested in Postman, but still have error. I think my delete url is fine, I can console log and see the item id been selected and show at the end of the url when making the request, I don't know what 's wrong.
delete.component.ts
deleteItem(): void {
console.log(this.currentItem._id);
alert("You had redeemed free food "+this.currentItem.itemToDonate);
this.charityService.deleteItem(this.currentItem._id).subscribe(
() => console.log("All of this food item has been redeemed"),
(err) => console.log(err)
)
}
charity.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { identifierModuleUrl } from '#angular/compiler';
//import { DonateComponent } from '../donate/donate.component';
const AUTH_API = 'http://localhost:3000/api/auth/donate';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json'})
};
#Injectable({
providedIn: 'root'
})
//post new donates to the database, list all items on the beneficiary page
//update quantity after redeem, delete item if the quantity is 0
export class CharityService {
constructor(private http: HttpClient) { }
donate(itemToDonate: string, quantity: number): Observable<any> {
return this.http.post(AUTH_API, {
itemToDonate, quantity
}, httpOptions);
}
listItem(): Observable<any> {
return this.http.get(AUTH_API, {});
}
receive(id: string): Observable<any> {
return this.http.put(`${AUTH_API}/update/${id}`, httpOptions)
.pipe(
catchError((err, caught) => {
console.error(err);
throw err;
})
);
}
getItem(id: string): Observable<any> {
return this.http.get(`${AUTH_API}/${id}`);
}
deleteItem(id: string): Observable<any> {
return this.http.delete(`${AUTH_API}/${id}`)
}
}
route.js
const controller_donate = require("../controllers/donate.controller");
const controller_receive = require("../controllers/receive.controller");
const controller_list = require("../controllers/list.controller");
const controller_delete = require("../controllers/delete.controller");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.post("/api/auth/donate", controller_donate.donate);
app.get("/api/auth/donate", controller_list.donations);
app.put("/api/auth/donate/update/:id", controller_receive.receive);
app.delete("/api/auth/donate/:id", controller_delete.delete);
};
delete.controller.js
const db = require("../models");
const Donate = db.donate;
const { donate } = require("../models");
exports.delete = (req, res) => {
const id = req.params.id;
donate.findByIdAndRemove(id)
.then(data => {
if (!data) {
res.status(404).send({ message: "Cannot delete item" });
} else {
res.status(200).send("This item is been redeemed");
}
})
}

How JWT toke expires work ? Logout automatically when token gets expire in node js and angular 7

I am very to new nodejs and angular 7 I am using jwt token for authentication, I want to redirect automatically to the login page once token get expire. I know similar question have been asked already but I also tired that way I didn't work out for me.
admin.controller.js
const controller = require("./admin.service");
const jwt = require("jsonwebtoken")
module.exports = {
verifyAdmin: (req, res) => {
const sign = jwt.sign({admin_user: req.body}, "mysecretkey", {
expiresIn: "1h"
})
req.body.admin_token = sign
const body = req.body;
controller.adminLogin(body, (err, result) => {
if(err) {
console.log(err)
res.status(500).json({
success: 0,
message: "Database connection error"
})
} else{
if(result[0].length > 0) {
console.log(result[0][0].admin_user)
res.json({
success: 1,
message: result[0],
token: sign
})
} else {
res.json({
success:0,
message: "We cannot find it"
})
}
}
})
}
So someone suggested the using HttpInterceptor is good idea for this I used that too but not wokring.
auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpParams, HttpHeaders, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '#angular/common/http';
import { Teacher } from '../shared/teacher.model';
import { Subject, Observable } from 'rxjs';
#Injectable()
export class AuthService implements HttpInterceptor {
// private adminValidateUrl = "http://localhost:3000/getData";
private adminValidateUrl = "http://localhost:3000/adminApi/verifyAdmin"
private verifyAdminToken = "http://localhost:3000/adminApi/getAdminUserName"
private getTeacherRecordsUrl = "http://localhost:3000/api/getTeacherRecords"
private removeTeacherUrl = "http://localhost:3000/adminApi/removeTeacherRecord"
subject = new Subject<Teacher[]>();
teachers: Teacher[] = []
constructor(private http: HttpClient) { }
headers = new Headers({
'Content-Type': 'application/json',
'Token': localStorage.getItem("admin_token")
});
adminValidation(adminData: any) {
console.log(adminData)
return this.http.post<any>(this.adminValidateUrl, adminData)
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({ headers: req.headers.set("Token", localStorage.getItem("Token")) });
console.log("Sending request with new header now ...");
//send the newly created request
return next.handle(authReq)
.pipe(err => {
// onError
console.log(err);
if (err instanceof HttpErrorResponse) {
console.log(err.status);
console.log(err.statusText);
if (err.status === 401) {
window.location.href = "/login";
}
}
return Observable.throw(err);
}) as any;
}
getAdminUserName() {
const token = localStorage.getItem('admin_token');
return this.http.get<any>(this.verifyAdminToken, {
observe: "body",
headers: new HttpHeaders().set("Authorization", "Bearer " + token)
});
}
getTeacherRecordsFromDB() {
return this.http.get<any>(this.getTeacherRecordsUrl, {
observe: "body"
})
}
removeTeacher(teacher: Teacher) {
const token = localStorage.getItem('admin_token');
return this.http.post<any>(this.removeTeacherUrl, teacher, {
observe: "body",
headers: new HttpHeaders().set("Authorization", "Bearer " + token)
})
}
}
or may be I am not using it right.
so I want a way I which my angular page automatically redirect to login page when token expires with some message of token expire.
Thanks.
Hey You can use following code inside interceptor to redirect page to login when token expired
return next.handle(request).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse
&& (error.status === 403 || error.status === 401)) {
localStorage.removeItem('accessToken');
window.location.href = '/login';
return throwError(error);
} else {
return throwError(error);
}
})
)

Send File from Angular to Nodejs - Cannot read property 'headers' of undefined

I'm having issues passing my data with a file to my nodejs backend. I'm currently using azure functions to run my nodejs code. Currently when I pass the data with file, I'm getting a Cannot read property 'headers' of undefined I'm adding the header in the options so I don't really understand why I'm getting the error.` Working with files is definitely one of my weaknesses so I appreciate any help!
import { Injectable, OnDestroy } from "#angular/core";
import { Subject, Observable } from "rxjs";
import {
HttpClient,
HttpParams,
HttpRequest,
HttpHeaders,
HttpEvent,
HttpEventType
} from "#angular/common/http";
import { map, takeUntil, switchMap } from "rxjs/operators";
import { Router } from "#angular/router";
import { environment } from 'src/environments/environment';
import { AuthService } from '../auth.service';
import { SendAppealModel } from './send-appeal.model';
#Injectable({ providedIn: "root" })
export class SubmitAppealService implements OnDestroy {
destroy = new Subject();
constructor(private http: HttpClient, private router: Router, private authService: AuthService) { }
ngOnDestroy() {
this.destroy.next();
this.destroy.complete();
}
submitAppeal(
username: string,
email: string,
file: File
) {
let form = new FormData();
form.append('file', file);
form.append('username', username);
form.append('email', email);
console.log("FILE OUTPUT");
console.log(file);
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
const api = environment.azure_function_url + `/PATCH-Send-Appeal`;
const req = new HttpRequest('PATCH', api, form, options);
return this.http.request(req)
.pipe(
map((res: HttpEvent<any>) => {
if (res.type === HttpEventType.Response) {
return res.body.id.toString();
} else if (res.type === HttpEventType.UploadProgress) {
// Compute and show the % done:
const UploadProgress = +Math.round((100 * res.loaded) / res.total);
return UploadProgress;
}
})
);
}
}
azure function
const multer = require('multer');
const upload = multer({ dest: 'public/uploads/' }).single('file');
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
upload();
console.log(req.file);
var filename = path.basename("../" + req.file.path);
console.log("filename");
console.log(req.file.destination);
console.log(__dirname);
var form = new formidable.IncomingForm();
console.log("form");
console.log(form);
context.res = {
status: 200,
headers: {
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Set-Cookie',
'Access-Control-Max-Age': '86400',
Vary: 'Accept-Encoding, Origin',
'Content-Type': 'application/json',
},
};
context.done();
};
I'm assuming you are getting that error because your headers aren't actually making it to your azure function.
Currently you have this:
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
You can't do that. headers.append doesn't do an in-place update. It returns a new HttpHeaders object. So, you actually need this:
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'multipart/form-data');
headers = headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
Per comments, I see one other thing that looks a little off to me. This may be part of the issue. Try updating your HTTP call to this:
const req = new HttpRequest('PATCH', api, form, options);
return this.http.patch(api, form, options)
.pipe(
map((res: HttpEvent<any>) => {
if (res.type === HttpEventType.Response) {
return res.body.id.toString();
} else if (res.type === HttpEventType.UploadProgress) {
// Compute and show the % done:
const UploadProgress = +Math.round((100 * res.loaded) / res.total);
return UploadProgress;
}
})
);
You might also set a breakpoint in your azure function on the first line to inspect the request object and make sure your HttpHeaders are making it in.

NestJs : How to implement node.js Post and Get logic in NestJs

I'm trying to implement node.js Spotify Authorization flow in NestJs.
But HttpService Post and Get functions doesn't work as in node.js.
Node.js working example:
var request = require('request'); // "Request" library
app.get('/callback', function(req, res) {
var authOptions = {
url: 'https://some-url.com/api/token',
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (Buffer.from(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
// I'm trying to implement this post in NestJS
request.post(authOptions, function(error, response, body) {
var options = {
url: 'https://api.spotify.com/v1/me',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
request.get(options, function(error, response, body) {
console.log(body);
});
}
I'm using HttpService Post method in NestJS
and that doesn't work:
constructor(private httpService: HttpService) {}
#Get('callback')
callback(#Request() req, #Res() res): any {
let code = req.query.code || null;
const url = 'https://some-url.com/api/token';
const form = {
code: code,
redirect_uri: this.redirect_uri,
grant_type: 'authorization_code'
}
const headers = {
'Authorization': 'Basic ' + (Buffer.from(this.client_id + ':' + this.client_secret))
}
// doesn't work
this.httpService.post( url, form, { headers: headers }).pipe(
map((response) => {
console.log(response);
}),
);
}
In NestJS, you do not need to send req, res object to your function parameter. Nest Js provide build-in decorator for req.body, req.query and req.param as #Body, #Query, and #Param. I write down to call post method and get method. You can also use put, patch, delete, and other methods. Please make a data transfer object file in your module.
for further reference, you can check this: https://docs.nestjs.com/controllers
export class yourController {
constructor(private readonly httpService: HttpService) {}
#Post('your-route-name')
public postMethod(#Body() yourDTO: YourDTOClass): Promise<interface> {
try {
return this.httpService.method(yourDTO);
} catch (err) {
throw new HttpException(err, err.status || HttpStatus.BAD_REQUEST);
}
}
#Get('your-route-name')
find(#Query() query: QueryDTO): Promise<interface> {
try {
return this.httpService.methodName(query);
} catch (err) {
throw new HttpException(err, err.status || HttpStatus.BAD_REQUEST);
}
}
}
You should put return before this.httpService.post(...). Normally you would have to subscribe to the Observable returned by the post method but NestJS handles this for you through the #Get() decorator.
You should prefix your controller with "async" and use "await" followed by "toPromise()"...
constructor(private httpService: HttpService) {}
#Get('callback')
async callback(#Request() req, #Res() res): any {
// ... remaining code here
const response =
await this.httpService.post(url, form, { headers: headers }).toPromise();
return response;
}
Add this imports to the controller:
import { Observable } from 'rxjs';
import { take, tap, map } from 'rxjs/operators';
Then try this:
constructor(private httpService: HttpService) {}
#Get('callback')
callback(#Request() req, #Res() res): Observable<any> {
let code = req.query.code || null;
const url = 'https://some-url.com/api/token';
const form = {
code: code,
redirect_uri: this.redirect_uri,
grant_type: 'authorization_code'
}
const headers = {
'Authorization': 'Basic ' + (Buffer.from(this.client_id + ':' +
this.client_secret))
}
return this.httpService.post( url, form, { headers: headers }).pipe(
// Take first result to complete the observable..
take(1),
// [OPTIONAL] Some debug log to see the response.
tap((response: { data: any }) => {
console.log(`Response: ${JSON.stringify(response.data)}`);
})
// Map the response object to just return its data.
map((response: { data: any }) => response.data),
);
}

Resources