Nuxt3: Error using function inside defineEventHandler() in server/api/ file - nuxtjs3

I am trying to pass some authentication headers to fetch a third party API , but I am getting the following error when running a /server/api/walmart.js file in Nuxt3:
[nuxt] [request error] this[D].init is not a function at new Sign
(https://nuxt-starter-jzgdht.w.staticblitz.com/blitz.331c80ffab288536319518a60349d00207075dad.js:6:1135208)
at Object.createSign
(https://nuxt-starter-jzgdht.w.staticblitz.com/blitz.331c80ffab288536319518a60349d00207075dad.js:6:808197)
at Scheme.sign (./node_modules/node-rsa/src/schemes/pkcs1.js:152:32)
at RSAKey.module.exports.Key.RSAKey.sign
(./node_modules/node-rsa/src/libs/rsa.js:264:40) at
NodeRSA.module.exports.NodeRSA.sign
(./node_modules/node-rsa/src/NodeRSA.js:318:32) at
generateWalmartHeaders (./.nuxt/dev/index.mjs:446:28) at eval
(./.nuxt/dev/index.mjs:458:14) at eval
(./node_modules/h3/dist/index.mjs:364:14) at Object.eval [as handler]
(./node_modules/h3/dist/index.mjs:564:12) at eval
(./node_modules/h3/dist/index.mjs:475:31) [nuxt] [request error]
this[D].init is not a function at new Sign
(https://nuxt-starter-jzgdht.w.staticblitz.com/blitz.331c80ffab288536319518a60349d00207075dad.js:6:1135208)
at Object.createSign
(https://nuxt-starter-jzgdht.w.staticblitz.com/blitz.331c80ffab288536319518a60349d00207075dad.js:6:808197)
at Scheme.sign (./node_modules/node-rsa/src/schemes/pkcs1.js:152:32)
at RSAKey.module.exports.Key.RSAKey.sign
(./node_modules/node-rsa/src/libs/rsa.js:264:40) at
NodeRSA.module.exports.NodeRSA.sign
(./node_modules/node-rsa/src/NodeRSA.js:318:32) at
generateWalmartHeaders (./.nuxt/dev/index.mjs:446:28) at eval
(./.nuxt/dev/index.mjs:458:14) at eval
(./node_modules/h3/dist/index.mjs:364:14) at Object.eval [as handler]
(./node_modules/h3/dist/index.mjs:564:12) at eval
(./node_modules/h3/dist/index.mjs:475:31)
import NodeRSA from 'node-rsa';
const keyData = {
consumerId: '<consumer id removed for this post>',
privateKey: `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAiDffvTlnBcHfDhPjYlJhSk+atPFE6HpFaf4mm/aYBXmOn89A
MMulkkmpu+RGj0SaPrpX/ockoSeMuuEPPd3AQ5uZAnJw9TBnE2/kJrPsHaKyVFGV
hZneksIK/KdP/kpGSuVAkocPdit5zKwliMnc6/GVLpDFvU8K2QPnvYu2Jp8XBDwg
gTu2tzHQkrm0cdCYZklKKqN9NdVLJy+6AL50+vX19nHwDdCYCvnPtH/hXxMwRnIJ
3qibO8owPWh+q/xMld2K2OoUNWpbsxBj/r9Jxu60d429+XcIUu1hyPHG1lDKiK/n
Z4L+7WE4Ez8sEVD0YrE9hRRbLl5Dsvi4XZrG9QIDAQABAoIBAQCFNN5+1JuCbcwK
oDhD9fteB+pp92ZDUQ6AUCDBc6vF7tEiRjGnhf4ryA1LyDeX8qZDoUZbiRyw21Qr
i9qzzR4u/wHp+q+rleG2iDy7/EZx1KA/BGkLdSTKrya/W35GVavXocg7gggErw80
r0MlOQHpWR1hpAE61wjsn30HRpChwow1YZE/6cMIQN2nCJ+JGXuZDoGazYk4HwFD
Fmrtag/FjShYUVgr4QlrPzYzcTCKX1UKQkZ9AED9Q2prKSvoD8ZxOJSaVF0FP1ty
/i9L1I/eJEusSBPXc9v5xJjs7q8RRKZhn3TuvBHos3LDuaQKwPBe+w0vHr8ZVw5t
tuZPiq8BAoGBAOYILkVnnrskg2qzqewHukiKnZHLi+WkJQPQzTHvVMMCEUaxF+6p
Y58tLlaEd0uNH1ntDPya5s2y01/1eZ/8n1U1/SlqKoQ5apkC77eNBklMxixJ0xGP
zu1bj5COba6pXdY+YcW2z7W0ubmPD3YCSD/VUD/IKXwTEAHm2J11ffM9AoGBAJeY
hRbiSQW6GXWHu41qqIYsWfdJpy/A0qhpXjmySq1XFH/ThHDuFmn5RkZVvn7D/Pql
GfO8E55QWjK+SLO7LBRazKP0GNmrKinVMKyo7WUgwrZy3fwEY5wcNaNFB/YL9J4M
OSRp6eV3pnUwQI2NhzTzuHAyJgd/r+I6zMeSTn4ZAoGADGhejpHTRwbmK8g7Hycf
jjAj5axUBHQBJx6JIutk6AvhgK2mu9HZNMnMGRCWGrYm/cPCkpGMZ4YAzsk/4ThQ
I9mAqU43suAh9tTotz7dGvEQM21b/DOEltr8eHCmS+iIzjiZL3/33jY8Wlz0GYpv
+Tl5VadnTXD9yQx5nKysuYUCgYA6PNy8Kth0u8a2ERvrOxNc4EL7ri7tOH11N218
atMnfnGgnciefcjck2f880nId1CDldO/f/xlcGcGYXWanohTlYJSZh752DjNc1pM
qmTw2cITx1MiUylVOr0caROi4XrrELUPGSVDA1FOaegSuVE89XhgmdNkRBh0p7Qt
4zYGWQKBgFGuZgbPl76K3fj5POY5OlPusXdCCqJMHKPug+4e5mG82T1KvLFCj1PQ
Fm0y9PoS3A8SXf+aEeLhRqVrpaU4w5RK8PqeF6IB2hpDrHJ8b+3ERU3J7/KuU8Vw
pmwDIbjWypH4dNJgRMti+RKDb9llup6xP5Q4PQRzvUQdklSCp3D8
-----END RSA PRIVATE KEY-----`,
keyVer: 1,
};
const generateWalmartHeaders = () => {
const { privateKey, consumerId, keyVer } = keyData;
const hashList = {
'WM_CONSUMER.ID': consumerId,
'WM_CONSUMER.INTIMESTAMP': Date.now().toString(),
'WM_SEC.KEY_VERSION': keyVer,
};
const sortedHashString = `${hashList['WM_CONSUMER.ID']}\n${hashList['WM_CONSUMER.INTIMESTAMP']}\n${hashList['WM_SEC.KEY_VERSION']}\n`;
const signer = new NodeRSA(privateKey, 'pkcs1');
const signature = signer.sign(sortedHashString);
const signatureEnc = signature.toString('base64');
return {
'WM_SEC.AUTH_SIGNATURE': signatureEnc,
'WM_CONSUMER.INTIMESTAMP': hashList['WM_CONSUMER.INTIMESTAMP'],
'WM_CONSUMER.ID': hashList['WM_CONSUMER.ID'],
'WM_SEC.KEY_VERSION': hashList['WM_SEC.KEY_VERSION'],
};
};
export default defineEventHandler(() => {
const options = {
method: 'GET',
headers: generateWalmartHeaders(), // <--- the error seems to originate from this
return {
api: 'works',
options: options,
};
});
Am I not using the defineEventHandler() correctly there?

#redshift,
1.The const 'options' in the defineEventHandler() function has not closing tag (unterminated).
Keep in mind we're working in server side here, check your functions within generateWalmartHeaders(). Are they all functioning in the backend ?
Fire this code with hard coded headers , and see if it runs / debug it.
Check Docs - they changed the documentation 20th of June sometime later than u posted this question.
check this example...
export default defineEventHandler((event) => {
return {
api: 'works'
}
})
Check my working example , maybe it will help you get on your way :
const config = useRuntimeConfig()
let environment = process.env.NODE_ENV;
let $endpoint = environment == 'development' ? 'http://dev.endpointisdev/' : 'http://prod.production/';
console.log('ENVIRONMENT =', process.env.NODE_ENV)
console.log('$ENDPOINTS =', $endpoint)
export default defineEventHandler(async(event) => {
const productsRequest = await $fetch(`${$endpoint}` +'somelistrequest', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa(`${config.auth_user}:${config.auth_pw}`),
'guiId': '7552662'
},
body: {
reference : "blablabla", //
productListRequest : {
securityKey : `${config._key}`
}
}
});
return responseObject.list
})
IMPORTANT :
I fire this from my store with something like this ..
Now i've encountered a situation today where deployment to production made this api call fail because it needed to be "camelCase"
let submitOrder = await $fetch("/api/submitOrder", {method: 'POST', body: orderPayload});

Related

What is the best practice to bypass my specific dynamic code evaluation in next.js middleware?

I use next.js middleware to retrieve a data stored inside a cookie, and to check in a db (using strapi) if this specific user exists, or if he needs to register before going further.
// middleware.js
import { getToken } from 'next-auth/jwt';
import qs from 'qs';
import { MY_DB } from './constants';
export async function middleware(request) {
const token = await getToken({
req: request,
secret: process.env.SECRET,
});
const params = qs.stringify({
filters: {
address: {
$eq: token.sub,
},
},
});
const url = MY_DB + '/api/users/?' + params;
const result = await fetch(url, {
method: 'GET',
headers: { accept: 'application/json' },
});
// remaining code checks if the request is empty or not and returns the appropriate page
(...)
building my project returns the following error :
Failed to compile.
./node_modules/.pnpm/function-bind#1.1.1/node_modules/function-bind/implementation.js
Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation
Import trace for requested module:
./node_modules/.pnpm/function-bind#1.1.1/node_modules/function-bind/implementation.js
./node_modules/.pnpm/function-bind#1.1.1/node_modules/function-bind/index.js
./node_modules/.pnpm/get-intrinsic#1.1.3/node_modules/get-intrinsic/index.js
./node_modules/.pnpm/side-channel#1.0.4/node_modules/side-channel/index.js
./node_modules/.pnpm/qs#6.11.0/node_modules/qs/lib/stringify.js
./node_modules/.pnpm/qs#6.11.0/node_modules/qs/lib/index.js
> Build failed because of webpack errors
 ELIFECYCLE  Command failed with exit code 1.
I highly suspect the qs.stringify call given the stacktrace, but how can I overcome this in an elegant way ?

Cloud Task Creation : Error: 3 INVALID_ARGUMENT: Request contains an invalid argument

I'm following thist tutorial : https://cloud.google.com/tasks/docs/tutorial-gcf
To create a Task that would call a cloud function.
I've done quite some tries and still get this error:
If I change the body encoding to something else, I get another error about serialisation method.
It's likely not a permission issues, as I got some before and got rid of it.
The object which is pass to the createTask() is the following :
task: {
httpRequest: {
url: "https://europe-west1-project_id.cloudfunctions.net/FunctionName"
httpMethod: "POST"
oidcToken: {
serviceAccountEmail: "cf-targetFunctionSA#project_id.gserviceaccount.com"
}
body: ""
headers: {
Content-Type: "application/json"
}
}
(or with body: base64 encoded json string.)
The code I use is the following :
'use strict';
const common = require('./common');
const {v2beta3} = require('#google-cloud/tasks');
const cloudTasksClient = new v2beta3.CloudTasksClient();
let projectName = common.getProjectName();
let location = "europe-west3";
let queue = "compute-stats-on-mysql";
const parent = cloudTasksClient.queuePath(projectName, location, queue);
async function createTask(url, serviceAccount, data)
{
const dataBuffer = Buffer.from(JSON.stringify(data)).toString('base64');
const task = {
httpRequest: {
httpMethod: 'POST',
url:url,
oidcToken: {
serviceAccountEmail: serviceAccount,
},
headers: {
'Content-Type': 'application/json',
},
body:dataBuffer,
},
};
try
{
// Send create task request.
common.logDebug(`Before creating task`, {parent:parent,task:task, data:data});
const [response] = await cloudTasksClient.createTask({parent, task});
common.logDebug(`Created task ${response.name}`, {parent:parent,task:task, response:response, data:data});
return response;
}
catch (error)
{
// Construct error for Stackdriver Error Reporting
console.error("error while creating tasks",error);
}
}
module.exports = {
createTask : createTask,
cloudTasksClient:cloudTasksClient
};
The lack of details in the error makes me hit a wall blind...
Any suggestions ?
My service account was missing a part...
it was
"cf-"+functionName+"#"+projectName+".gserviceaccount.com";
instead of
"cf-"+functionName+"#"+projectName+".iam.gserviceaccount.com";
I left out the ".iam" during my numerous test to make it work.
For sure there's room for improvement in the error messages.
I had same problem. In your case I think there is not property scheduleTime into task param.
To me, the scheduleTime.seconds was with a wrong value.

Rxdb Plugin: Using the RxCollectionBase#insert method in a plugin

I am trying to create a plugin for rxdb.
I want to catch the exception raised by insert and return an hash with
{[fieldName: string] => [error:string]}
When using my new method though, I am getting an exception, and it seems like the method is getting called directly on the prototype rather than on each RxColletion<T, T2, T3> instance.
The error i am getting is:
TypeError: Cannot read property 'fillObjectWithDefaults' of undefined
which happens here: https://github.com/pubkey/rxdb/blob/ac9fc95b0eda276110f371afca985f949275c3f1/src/rx-collection.ts#L443
because this.schema is undefined.. The collection I am running this method on does have a schema though..
Here is my plugin code:
export const validatedInsertPlugin: RxPlugin = {
rxdb: true,
prototypes: {
RxCollection(proto: IRxCollectionBaseWithValidatedInsert) {
proto.validatedInsert = async function validatedInsert<T, D>(
doc: T
): Promise<Insert<T>> {
try {
// this is the line that raises:
const product = await proto.insert(doc);
return [true, product];
} catch (e) {
// extract errors
return [false, {} as Errors<T>];
}
};
},
},
overwritable: {},
hooks: {},
};
To answer my own question,
proto.insert is targeting the prototype, which is not what I want.
function(this: RxCollection) is what I want. I have to use this which will target the actual instance.
proto.validatedInsert = async function validatedInsert<T1>(
this: RxCollection,
doc: T1
): Promise<ValidatedInsert<T1>> {
try {
const product = await this.insert(doc); // this, not proto
return [true, product];
} catch (e) {
...

It's possible to get next.js request object globally?

I am using fastify with next.js and I need to include tracing (requestId is the problem so far). What I am doing right now is creating a fastify onRequest hook and generating a requestId value and setting it in request object (could be as a request header as well). What I want is to get access to this request object for two reasons:
In logger object (pino in this case, I want to include the requestId in all custom server-side logs).
In all request that needs to be made to other services need to include the requestId in headers.
Maybe I am missing something trivial and I'm not doing it the best way.
HERE SOME SNIPPETS
This how I am generating the reqId
const fastify = fastifyFactory({
logger, // logger configuration (Pino instance with custom configuration, see below)
genReqId: () => {
return Math.random()
.toString(36)
.slice(-6);
}
});
pino instance
const pino = require('pino');
const logger = pino({
messageKey: 'message',
prettyPrint: true,
changeLevelName: 'severity',
useLevelLabels: true,
base: {
serviceContext: {
service: 'web'
}
},
level:'info'
});
module.exports = {
logger
};
This is a plugin to gets the reqId generated and setting it to a query property within request object
const tracing = function tracing(fastify, opt, next) {
fastify.addHook('onRequest', (req, res, nextRequest) => {
const { id } = req;
const logger = fastify.log.child({ reqId: id });
req.query.reqId = id;
fastify.log = logger; //overrides the current fastify logger to include the reqId in all custom logs
nextRequest();
});
next();
};
tracing[Symbol.for('skip-override')] = true;
module.exports = tracing;
I have no problem when using fastify.log.info(...) because how logger is overrided in each request, it will include the reqId as a child log. The problem is that I want to create a generic logger to use at any part and fastify logger is not available in React components (for example to write logs at getInitialProps). Another important think is tha I need to include this reqId in all request I send to other services (ex: when fetching data), this is why I tried to store this value in request object but need to get it.
Starting from a project build with:
npx create-next-app --example custom-server-fastify custom-server-fastify-app
And changing the server.js with:
const Next = require('next')
const Fastify = require('fastify')
// your pino config
const fastify = Fastify({
logger: {
level: 'info',
prettyPrint: true,
changeLevelName: 'severity',
useLevelLabels: true,
base: {
serviceContext: {
service: 'web'
}
}
},
genReqId: () => { return Math.random().toString(36).slice(-6) }
})
// your plugin
const aPlugin = function yourPlugin (fastify, opts, next) {
fastify.addHook('onRequest', (request, reply, next) => {
request.log.info('hello')
const { id } = request
request.query.reqId = id
next()
})
next()
}
aPlugin[Symbol.for('skip-override')] = true
fastify.register(aPlugin)
[.... other generated code]
const port = parseInt(process.env.PORT, 10) || 3000
[.... other generated code]
fastify.get('/*', (req, reply) => {
console.log('-------->', req.id, req.query.reqId) // both your id is ok
return app.handleRequest(req.req, reply.res).then(() => {
reply.sent = true
})
[.... other generated code]
})
Then:
npm run dev
# another console
curl http://localhost:3000/
It will print out:
[1558441374784] INFO : Server listening at http://127.0.0.1:3000
serviceContext: {
"service": "web"
}
> Ready on http://localhost:3000
[1558441405416] INFO : incoming request
serviceContext: {
"service": "web"
}
reqId: "2i810l"
req: {
"method": "GET",
"url": "/",
"hostname": "localhost:3000",
"remoteAddress": "127.0.0.1",
"remotePort": 57863
}
req id ----> 2i810l
--------> 2i810l 2i810l
[ event ] build page: /
[ wait ] compiling ...
[1558441406171] INFO : request completed
serviceContext: {
"service": "web"
}
reqId: "2i810l"
res: {
"statusCode": 200
}
responseTime: 753.012099981308
So I think the misunderstanding is in the request object that is the Fastify request and not the Node.js "low level" request object, that could be accessed with request.req.
Moreover, running fastify.log = logger; is dangerous because it means that each request override and create a new logger and change the logger for the fastify instance, this is not safe, and as shown it is not necessary.
If you want more child logger (per route prefix per example) I suggest exploring/using the onRegister hook.
EDIT:
Now the custom hook print:
[1558443540483] INFO : hello
serviceContext: {
"service": "web"
}
reqId: "zjuhw2"

Angular2 ( 2.2.1 ) Http post request progress bar [duplicate]

Is there currently a way within Angular 2 to retrieve the progress (i.e. percentage done) of an ajax call, using the angular2/http module?
I use the following code to make my HTTP calls:
let body = JSON.stringify(params);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, body, options)
.timeout(10000, new Error('Timeout exceeded during login'))
.toPromise()
.then((res) => {
...
}).catch((err) => {
...
});
The goal is to write a synchronisation system. The post will return a lot of data, and I want to give the user an indication on how long the syncing will take.
Currently (from v. 4.3.0, when using new HttpClient from #ngular/common/http) Angular provides listening to progress out of the box. You just need to create HTTPRequest object as below:
import { HttpRequest } from '#angular/common/http';
...
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
});
And when you subscribe to to request you will get subscription called on every progress event:
http.request(req).subscribe(event => {
// Via this API, you get access to the raw event stream.
// Look for upload progress events.
if (event.type === HttpEventType.UploadProgress) {
// This is an upload progress event. Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(`File is ${percentDone}% uploaded.`);
} else if (event instanceof HttpResponse) {
console.log('File is completely uploaded!');
}
});
More info here.
You could leverage the onprogress event provided by XHR (see this plunkr: http://plnkr.co/edit/8MDO2GsCGiOJd2y2XbQk?p=preview).
This allows to get hints about the progress of the download. This isn't supported out of the box by Angular2 but you can plug it by extended the BrowserXhr class:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor(private service:ProgressService) {}
build(): any {
let xhr = super.build();
xhr.onprogress = (event) => {
service.progressEventObservable.next(event);
};
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
See this question for more details:
Angular2 / RxJS - updating variable after getting data from Http observable
When you make http cals in angular2, it returns an Observable of type Response, this response is created inside class called XHRConnection where all the magic happens.
The XHRConnection builds the response by listening to XMLHttpRequest's load event, this means it will return one response at the end of the request.
Now to be able to alter this behavior we need to make our connection class listen to the progress event.
So we need to create custom Connection class, to handle the response as we see fit.
I did it this way,
Take note that my php API returns multi response in a single request and this responses are plain strings.
my_backend.ts
import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Observer} from "rxjs/Observer";
import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";
import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";
import {ResponseOptions} from "angular2/src/http/base_response_options";
import {Request} from "angular2/src/http/static_request";
import {Response} from "angular2/src/http/static_response";
import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";
import {Headers} from 'angular2/src/http/headers';
import {isPresent} from 'angular2/src/facade/lang';
import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"
export class MyConnection implements Connection {
readyState: ReadyState;
request: Request;
response: Observable<Response>;
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
this.request = req;
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
let _xhr: XMLHttpRequest = browserXHR.build();
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
// save the responses in array
var buffer :string[] = [];
// load event handler
let onLoad = () => {
let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
//_xhr.respons 1 = "Loading data!"
//_xhr.respons 2 = "Loading data!Ready To Receive Orders."
// we need to fix this proble
// check if the current response text contains the previous then subtract
// NOTE: I think there is better approach to solve this problem.
buffer.push(body);
if(buffer.length>1){
body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');
}
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
let url = getResponseURL(_xhr);
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
let state:number = _xhr.readyState;
if (status === 0) {
status = body ? 200 : 0;
}
var responseOptions = new ResponseOptions({ body, status, headers, url });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
let response = new Response(responseOptions);
//check for the state if not 4 then don't complete the observer
if(state !== 4){
//this will return stream of responses
responseObserver.next(response);
return;
}
else{
responseObserver.complete();
return;
}
responseObserver.error(response);
};
// error event handler
let onError = (err: any) => {
var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.error(new Response(responseOptions));
};
if (isPresent(req.headers)) {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
}
_xhr.addEventListener('progress', onLoad);
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.send(this.request.text());
return () => {
_xhr.removeEventListener('progress', onLoad);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
};
});
}
}
#Injectable()
export class MyBackend implements ConnectionBackend {
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
createConnection(request: Request):MyConnection {
return new MyConnection(request, this._browserXHR, this._baseResponseOptions);
}
}
And in the app.component.ts
import {Component, provide} from 'angular2/core';
import {HTTP_PROVIDERS,XHRBackend} from 'angular2/http';
import {MyBackend} from './my_backend';
#Component({
selector: 'my-app',
providers: [
HTTP_PROVIDERS,
MyBackend,
provide(XHRBackend, {useExisting:MyBackend})
]
.
.
.
Now calling http.get will return a steam of responses.
#Bartek Chichoki's answer is correct but it was not working for my case.
Adding observe: 'events' did the trick for me
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true,
observe: 'events'
});
Hope it helps
I strongly recomend using this
https://www.npmjs.com/package/angular-progress-http
otherwise messing around with xhr will make you miss sessions cookies and other stuffs
besides it'll be more portable and way easier to implement

Resources