I am currently developing an application in Angular2 which is wrapped by in a NodeJS instance which talks to an API. I am currently implementing some file upload functionality and cannot get a function which captures the API file upload request in the NodeJS layer to show that it is catching the files. There is no 'files' property on the 'req' object.
Here is my code:
import { Component } from "#angular/core";
import { routes } from "../../../routes";
import { FilesService } from "../../../services/files.service";
#Component({
selector : 'file-upload',
moduleId : module.id,
templateUrl : '/app/views/files/file-upload.html',
})
export class FileUploaderDirective {
private _filesToUpload: Array<File> = [];
constructor(
private _filesService: FilesService
) {
}
fileChangeEvents(fileInput: any) {
this._filesToUpload = <Array<File>> fileInput.target.files;
}
upload() {
this._filesService.sendFile(routes.api.files, [], this._filesToUpload)
.then((result) => {
console.log(result);
}, (error) => {
console.log(error);
});
}
}
MY file upload Angular2 service:
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
#Injectable()
export class FilesService {
constructor() {
}
sendFile(url: String, vars: Array<String>, files: File[]): Promise<any> {
return new Promise((resolve, reject) => {
let formData: FormData = new FormData(),
xhr: XMLHttpRequest = new XMLHttpRequest();
for (let i = 0; i < files.length; i++) {
formData.append("uploads[]", files[i], files[i].name);
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.response));
} else {
reject(xhr.response);
}
}
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
}
The NodeJS route that catches the api request and forwards it to a controller function in NodeJS:
router.post('/upload', function(req, res, next) {
filesRoutesControllerObjectInstance.upload(req, res, next);
});
And the function which is supposed to catch the request and send the files to the API:
var ApiBase_RequestLayer = require('../ApiBase_RequestLayer'),
Config = require(global.appRoot + '/Config'),
util = require('util');
function Files() {
Files.super_.call(this);
this.requestBaseUrl = Config.brain.url + '/upload';
}
Files.prototype.upload = function(req, res) {
if(req) {
}
};
util.inherits(Files, ApiBase_RequestLayer);
module.exports = Files;
When I debug the request there is no files present on the request when I debug the 'req' object in the NodeJS 'uoload' route and the controller. As you can see I am attempting to send them using the FormData Angular2 functionality. Can anyone see what I am doing wrong here.
Related
I have the following Angular and Node JS as follows
Interceptor
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import { AuthService } from "../Services/auth.service";
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService : AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
//console.log(this.authService.getAuthToken())
const authToken = this.authService.getAuthToken();
const authRequest = req.clone({
headers: req.headers.set("Authorization", authToken)
});
console.log("authRequest");
return next.handle(authRequest);
}
}
service
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { api_url } from '../Models/global-url.model';
import { LoginModel } from '../Models/login_details.model';
import { ResponseFromServer } from '../Models/response.model';
#Injectable({
providedIn: 'root'
})
export class AuthService {
private token : string;
//Password is asish for all users
constructor(private http: HttpClient) { }
checkUserLogin(loginDetails: LoginModel) {
console.log(loginDetails);
this.http.post<{response: any}>(api_url+"login/loginUser", loginDetails).subscribe((result: ResponseFromServer) => {
console.log(result.token);
this.token = result.token;
console.log(this.token);
});
}
getAuthToken() {
return this.token;
}
}
User Defined Middleware in Node JS :-
const jwt = require('jsonwebtoken');
const s_token = require('../tokens/auth-token');
//const authFunction = (req, res, next) => {
module.exports = (req, res, next) => {
console.log(req);
var message = '';
try {
const token = req.headers.authorization;
console.log(token);
jwt.verify(token, s_token);
next();
} catch (err) {
message = "Auth Failed";
console.log(err); //JsonWebTokenError: Error jwt must be provided => user is not logged in
res.status(401).json(message);
// res.json(message); //Check the error message that occurs in browser console, while using this without status
}
}
login.js in Node Router :-
router.post('/loginUser', async (req, res, next) => {
const loginDetails = req.body;
console.log(loginDetails);
var { userId, stored_password,userEmailId,token,status_code } = '';
var message = '';
var response = '';
//console.log(loginDetails);
query = `SELECT * FROM tbl_users WHERE (tum_email = $1 OR tum_mobile = $1)`;
params = [loginDetails.username];
// await db.query(query, params, (err, result) => {
// if(err) {
// console.log(err);
// response = 'f0';
// message = "Internal Server Error. Please reload the page and try again.";
// } else if(result.rows.length) {
// //console.log(result.rows.length);
// userId = result.rows[0].tum_email;
// password = result.rows[0].tum_password;
// response = 's1';
// message = "";
// } else {
// response = 'f1';
// message = "User with the given user id does not exist. Please register here";
// }
// });
try {
const result = await db.query(query, params);
if(result.rowCount == 0 ) {
response = 'f1';
message = "User with the given user id does not exist. Please register here";
} else {
userId = result.rows[0].tum_id;
userEmailId = result.rows[0].tum_id;
stored_password = result.rows[0].tum_password;
try {
if ((await argon2.verify(stored_password, loginDetails.password))) {
//password matches
response = 'success';
const session_data = {
userId: userId,
email: userEmailId
}
token = jwt.sign(session_data, s_token, {expiresIn:'1hr'});
//console.log(token);
} else {
response = 'f2';
message = "Entered password is wrong. Please enter the correct password, or reset it";
}
} catch (err) {
console.log(err);
response = 'f0';
message = "Internal Server Error. Please reload the page and try again, or contact an Administrator";
}
}
} catch (err) {
console.log(err);
response = 'f0';
message = "Internal Server Error. Please reload the page and try again, or contact an Administrator";
}
const json_object = {
token: token,
response: response,
message:message
}
if(token != '') {
status_code = 200;
} else {
status_code = 401;
}
res.status(status_code).json(json_object);
//console.log("response ="+response+" & message = "+ message);
});
login.component.ts
import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { AuthData } from 'src/app/Models/auth_data.model';
import { LoginModel } from 'src/app/Models/login_details.model';
import { ResponseFromServer } from 'src/app/Models/response.model';
import { AuthService } from 'src/app/Services/auth.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
isSubmitted = false;
isValid = true;
isLoading = false;
response_from_server = new ResponseFromServer();
constructor(private authService: AuthService) {
}
ngOnInit(): void {
this.response_from_server.response = 's1';
}
loginUser(loginData: NgForm) {
this.isSubmitted = true;
if(loginData.invalid) {
this.isValid = false;
//console.log("Validation Errors");
return;
}
const loginDetails : LoginModel = {
username : loginData.value.username,
password: loginData.value.password
}
this.authService.checkUserLogin(loginDetails);
}
}
Whenever I try to login , the error TypeError: Cannot read properties of undefined (reading 'length') is thrown.
The data is not even sent to the server side. It is stuck before return next.handle(authRequest);.
I tried console.log() almost everywhere to see where I am getting the mistake, and to which part, the data movement is getting done. Looks like the email and password are not even going through, to the Node JS server. Using console.log(result.token) in login.service.ts does not have any value.
Where am I going wrong ?
The problem is most likely happening because your trying to add the Authorization header before the user is logged-in.
In that situation authToken is undefined and you are assigning it to the header anyways.
You could solve it just adding a guard in your intercept method to first check if you have an authToken before attaching it to the request.
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authToken = this.authService.getAuthToken();
if(!authToken) { // <--- not logged-in skip adding the header
return next.handle(req);
}
const authRequest = req.clone({
headers: req.headers.set("Authorization", authToken)
});
return next.handle(authRequest);
}
Cheers
I have a problem because I am trying to implement file upload using multipart / form-data on my NodeJS server. When I call upload, the file I upload appears in the temporary server folder, but my request does not continue and my client is waiting for a response (in this case the uploadFile method is never running).
upload.router.ts
import {Router} from '../common/router';
import * as restify from 'restify';
class UploadRouter extends Router {
uploadFile = (req, resp, next) => {
console.log(req);
resp.json('test');
};
applyRoutes(application: restify.Server) {
this.basePath = '/upload';
application.post(`${this.basePath}`, this.uploadFile);
}
}
export const uploadRouter = new UploadRouter();
server.ts
export class Server {
application: restify.Server;
initRoutes(routers: Router[]): Promise<any> {
return new Promise((resolve, reject) => {
try {
const options: restify.ServerOptions = {
name: environment.project.name,
version: environment.project.version
};
if (environment.security.enableHTTPS) {
options.certificate = fs.readFileSync(environment.security.certificate);
options.key = fs.readFileSync(environment.security.key);
}
this.application = restify.createServer(options);
this.connector = blockchainConnector(environment.blockchain.connector);
const corsOptions: corsMiddleware.Options = {
preflightMaxAge: 10,
origins: ['*'],
allowHeaders: ['*'],
exposeHeaders: []
};
const cors: corsMiddleware.CorsMiddleware = corsMiddleware(corsOptions);
this.application.pre(cors.preflight);
this.application.use(cors.actual);
this.application.use(restify.plugins.queryParser());
this.application.use(restify.plugins.bodyParser());
this.application.use(restify.plugins.acceptParser(this.application.acceptable));
this.application.use(restify.plugins.fullResponse());
this.application.use(restify.plugins.multipartBodyParser({
multiples: true,
mapParams: true,
mapFiles: true,
keepExtensions: true,
uploadDir: environment.directory.tempDir
}));
this.application.use(mergePatchBodyParser);
this.application.use(tokenParser);
// routes
for (let router of routers) {
router.applyRoutes(this.application, this.connector);
indexRouter.addRouter(router);
}
indexRouter.applyRoutes(this.application);
this.application.listen(environment.server.port, () => {
resolve(this.application);
});
this.application.on('restifyError', handleError);
} catch (error) {
reject(error);
}
})
}
bootstrap(routers: Router[] = []): Promise<Server> {
return this.initRoutes(routers).then(() => this);
}
shutdown() {
this.application.close();
}
}
I realize that this is 8 months later, but it looks like you forgot to call next() in uploadFile
am trying to download pdf file from local folder that structures like
assets/test.pdf.
server.js
app.get('/ePoint', (req,res)=>{
// some dumb code :P
});
demo.ts
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Headers } from '#angular/http';
import {Observable} from 'rxjs';
fileDownload() {
const headers = new HttpHeaders();
headers.append('Accept', 'application/pdf');
this._http.get('http://localhost:3000/ePoint', { headers: headers })
.toPromise()
.then(response => this.saveItToClient(response));
}
private saveItToClient(response: any) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition');
const parts: string[] = contentDispositionHeader.split(';');
const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: 'application/pdf' });
saveAs(blob, filename);
}
i dont know where i did mistake. in browser network console. its shows 200 ok. but in normal browser console shows as below attachment
Note: i referred for ts file from here
helps much appreciated
try this...
component.ts
downloadDocument(documentId: string) {
this.downloadDocumentSubscription = this.getService.downloadScannedDocument(documentId).subscribe(
data => {
this.createImageFromBlob(data);
},
error => {
console.log("no image found");
$("#errorModal").modal('show'); //show download err modal
});
}
createImageFromBlob(image: Blob) {
console.log("mylog", image);
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(image, "download." + (image.type.substr(image.type.lastIndexOf('/') + 1)));
else {
var url = window.URL.createObjectURL(image);
window.open(url);
}
}
service.ts
downloadScannedDocument(documentId: string): Observable<any> {
let params = new HttpParams();
if (documentTypeParam == false)
params = params.set('onlyActive', 'false');
let fileResult: Observable<any> = this.http.get(`${this.apiBaseUrl}/${documentId}`, { responseType: "blob", params: params });
return fileResult;
}
I am working for mean stack application. I am able to connect express api and angular component, but i want to pass parameters to the api service.
Please find the code below for clearer idea,
Component Code
constructor(private _dataService: DataService){
var parametervalue = "Monthly";
this._dataService.getexternalSourceDetailFiltered().subscribe((data) => {
this.source.load(data);
});}
DataService Code
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class DataService {
result;
constructor(private _http: Http) { }
getStudents(){
return this._http.get('/external_sources').map(result =>
this.result = result.json().data);
}
getexternalSourceDetail(){
return this._http.get('/external_sources_details').map(result =>
this.result = result.json().data);
}
getexternalSourceDetailFiltered(){
return this._http.get('/external_sources_details').map(result =>
this.result = result.json().data);
}
}
Express API Code
router.get('/external_sources_details_filtered',(req,res) =>{
connection((db) => {
var intId = parseInt(0);
var query ={'frequency.Monthly':{$exists:true}};
var projection = {_id:0,sourceID:1,SourceName:1, Outstanding:1};
db.collection('external_sources').find(query).project(projection).
toArray().then((external_sources_details_filtered) => {
response.data = external_sources_details_filtered;
res.json(response);
})
})
})
How would i pass parametervalue from the component so that i can use it in express API to pass parameter to call mongodb using dynamic parameter
SOLUTION: Being totally new i searched around and found a solution:
i used URLSearchParams to set the parameter to pass through the express API.
Here is the the code for better understanding,
Component Code:
constructor(private _dataService: DataService){
var param = new URLSearchParams();
param.append('frequency','Monthly');
this._dataService.getexternalSourceDetailFiltered(param).subscribe((data) => {
this.source.load(data);
});
}
Data Service Code
getexternalSourceDetailFiltered(parameterValue:any ){
return this._http.get('/external_sources_details_filtered',
{
params:parameterValue}).map(result => this.result = result.json().data);
}
Express API js Code
router.get('/external_sources_details_filtered',(req,res) =>{
let parameterValue;
connection((db) => {
if(req.query.frequency != '')
{
parameterValue = String( 'frequency.'+ req.query.frequency);
}
else
{
parameterValue = String( 'frequency');
}
console.log(parameterValue);
var query = {[parameterValue] :{$exists:true}};
var projection = {_id:0,sourceID:1,SourceName:1, Outstanding:1};
db.collection('external_sources').find(query).project(projection).toArray().then((external_sources_details_filtered) => {
response.data = external_sources_details_filtered;
res.json(response);
})
})
I am getting the following error when returning from a http service and attempting to push to response onto an array :
Cannot read property 'messages' of undefined
This is my chat.component.ts file :
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ChatService } from './chat.service';
#Component({
selector: 'chat-component',
template: `
<div *ngIf="messages">
<div *ngFor="let message of messages">
{{message.text}}
</div>
</div>
<input [(ngModel)]="message" /><button (click)="sendMessage()">Send</button>
`,
providers: [ChatService]
})
export class ChatComponent implements OnInit, OnDestroy {
messages = [];
connection;
message;
loading;
constructor(private chatService: ChatService) { }
sendMessage() {
this.chatService.sendMessage(this.message);
this.message = '';
}
ngOnInit() {
this.chatService.initPlaylist().subscribe(tracks => {
tracks.forEach(function(item) {
this.messages.push({
message: item.trackID,
type: "new-message"
});
});
})
this.connection = this.chatService.getMessages().subscribe(message => {
this.messages.push(message);
})
}
ngOnDestroy() {
this.connection.unsubscribe();
}
}
This is my chat.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Rx';
import * as io from 'socket.io-client';
#Injectable()
export class ChatService {
private url = 'http://localhost:1337';
private socket;
constructor(private http: Http) {
}
sendMessage(message) {
this.socket.emit('add-message', message);
}
initPlaylist() {
return this.http.get(this.url + '/playlist')
.map(this.extratData)
.catch(this.handleError);
}
getMessages() {
let observable = new Observable(observer => {
this.socket = io(this.url);
this.socket.on('message', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
})
return observable;
}
private extratData(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
I currently have a form on the front end, in which users can add a message, this is then pushed onto this.messages and through socket.io sent out to all connected sockets.
What I am now doing is storing messages in a mongodb via an express app using mongoose.
On page load, I would like to retrieve these messages from the document store, and push them onto this.messages - so the view is updated with previous messages, then socket.io should take over on new messages, adding them to the array.
As this is an initial call, once on load, I am not using socket.io to grab these, instead I have an api route setup through express, returning json that looks as follows :
[
{
"_id": "58109b3e868f7a1dc8346105",
"trackID": "This is my message...",
"__v": 0,
"status": 0,
"meta": {
"played": null,
"requested": "2016-10-26T12:02:06.979Z"
}
}
]
However when I get to this section of code within chat.component.ts, everything breaks down with the previously mentioned error..
this.chatService.initPlaylist().subscribe(tracks => {
tracks.forEach(function(item) {
this.messages.push({
message: item.trackID,
type: "new-message"
});
});
})
I using Angular 2, Socket.io, ExpressJS and MongoDB.
don't use function () use instead () => (arrow function) for this.... to keep pointing to the local class instance
tracks.forEach((item) => {
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions