In Express, streaming responses are easy to implement since res is a stream object.
Inside a Feathers custom service method, however, how can I stream something as a response?
You might be able to implement a custom response format which pipes res.data if it is a stream, and then resolve your service method with a stream. There is also a discussion about streaming data here.
You can add a custom service middleware before or after a specific service, and then you can use res.
Example:
const todoService = {
async get(id) {
return {
id,
description: `You have to do ${id}!`
};
}
};
function streamResponse(req, res, next) {
res.write('Streams are awesome!');
res.end;
}
app.use('/todos', todoService, streamResponse);
Related
I am trying to create an endpoint in a HTTP API that receives data periodically from remote devices.
There is a technological shift happening in this project where devices have previously reported data in XML whereas future implementations will shift towards JSON.
I am writing this API in NestJS (7.x) and TypeScript. Data will be coming in through the same endpoint (POST /) and data format is differentiated by the Content-Type header.
#Controller()
export class IngressController {
constructor(private readonly ingressService: IngressService) {
}
/* ... */
#Post('')
#Header('Cache-Control', 'none')
#HttpCode(HttpStatus.NO_CONTENT)
public async receive(
#Headers('Content-Type') contentType: string,
#Req() req: any,
#Body() body: string,
): Promise<InsertResponse> {
if (IngressController.isJson(contentType)) {
return { inserted: await this.ingressService.insertJsonString(req.body) };
}
if (IngressController.isXml(contentType)) {
return { inserted: await this.ingressService.insertXmlString(req.body) };
}
throw new BadRequestException(contentType, 'Unsupported Content-Type');
}
/* ... */
}
Future devices will report data in JSON (indicated by the Content-Type: application/json header in the HTTP request), legacy devices report in XML (Content-Type: application/xml).
It works splendidly for JSON. However, my problem is that req.body (or body respectively) is an empty object in the XML case. I presume the NestJS middleware is doing something and getting confused by XML, but I have found no hints as to allow XML payloads side-by-side with JSON. I don't mind parsing it manually.
As you suspected NestJS has a built-in bodyparser that will not be able to parse xml. What you could do is to plug in a custom middleware where you decide whether to parse the request body as xml or pass the request on the the next handler.
Something like this should work (I'm using express-xml-bodyparser in this example):
import {NestFactory} from '#nestjs/core';
import {AppModule} from './app.module';
import {Request} from "express";
const xmlParser = require('express-xml-bodyparser');
const xmlParserMidleware = xmlParser();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use((req: Request, res: any, next: any) => {
if (req.path.includes("/api/json-or-xml-handler") && req.header('Content-Type')?.includes('xml')) {
return xmlParserMidleware(req, res, next);
}
next();
});
await app.listen(8020);
}
bootstrap();
Then, in your controller body will either be the parsed json-object or an object representation of your xml:
#Controller()
export class TestControllerController {
#Post('/api/json-or-xml-handler')
receive(#Body() body: any) {
console.log(body);
// ...
}
}
I try to implement a WooCommerce webhook functionality for feathers. To authenticate the request I need to verify the signature of the raw request body like so
const isSignatureValid = (
secret: string,
body: any,
signature?: string
): boolean => {
const signatureComputed = crypto
.createHmac("SHA256", secret)
.update(new Buffer(JSON.stringify(body), "utf8"))
.digest("base64");
return signatureComputed === signature ? true : false;
};
Currently my signature never verifies. My guess is that this is due to the fact that req.body is not the acutual raw body of the request but some already parsed version with some featherjs goodness added.
Question is: How do I obtain the raw request body in a standard feathers app (created with feathers cli)?
Not sure if this is the most idiomatic way to do this, but I came up with the following:
In of express.json() in the main app.ts file it is possible to add the raw, unparsed body to the req object. This is handy, as for some webhooks (woocommerce, stripe), you only need the raw body to verifiy the signature, but otherwise work with the parsed JSON.
export interface IRequestRawBody extends http.IncomingMessage {
rawBody: Buffer;
}
app.use(
express.json({
verify: (req: IRequestRawBody, res, buf) => {
const rawEndpoints: string[] = ["/wc-webhook"];
if (req.url && rawEndpoints.includes(req.url)) {
req.rawBody = buf;
}
}
})
);
// Initialize our service with any options it requires
app.use('/country', function (req, res, next) {
console.log(req);
next();
}, new Country(options, app)
);
We can get request body as above in the express middleware of service class.
This is an extension of the original answer from #florian-norbert-bepunkt since for me it didn't work out of the box... I also needed a string version of the body in order to calculate the security hash.
app.use(express.json({
verify: (req, res, buf) => {
const rawEndpoints = ["/someAPI"];
if (req.url && rawEndpoints.includes(req.url)) {
req.feathers = {dataRawBuffer: buf};
}
}
}));
After that, you can access the original buffer from context as
context.params.dataRawBuffer
I can confirm that this works on feathersJS v4, maybe this will help someone.
So I've searched and I think a saw the entire internet but no solution regarding the issue I encounter.
I have multiple http request which I want to mock. All request have the same url but deviate based on the requestPayload which contain a graphQl query. Based on this query I want to return a specific json file. All proxy settings I have found can handle parameters but do not handle responses based on requestPayload.
Have you taken a look over this functionality?
https://webpack.js.org/configuration/dev-server/#devserver-before
as far as webpack-dev-server is an instance of express app you are able to setup it in the before/after hooks. Hooks get app (server) instance as a first argument.
so for your case your webpack development config would look like:
module.exports = {
//...
devServer: {
before: function(app) {
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get('/some/path/graphql', function(req, res) {
var query = req.body;
// ...your custom logic of
// specific query handling goes here
if (condition(query)) {
res.json({ mockedResponse: 'foo' });
} else {
res.json({ mockedResponse: 'bar' });
}
});
}
}
};
UPD: keep in mind if you're using proxy config for devServer you might want to use after hook instead of before to let your requests be proxified if needed.
I'm using Winston and Morgan for all the back-end logging in Sails.js and I need to be able to log the responses from HTTP get requests. I need to log them in a file. My logFile currently takes shows all the http requests but it does not show the responses. I have searched all the options for Morgan and Winston and can't find a way/option to do this. I was just wondering if any of you had any advice on how to accomplish this?
Thanks!
You can write a middleware function for ExpressJS that will log the body once a response is sent. Basing it off of Node's http module to see how Connect (and therefore Express) manages the response body (which is a stream): you can hook into the two methods that write to that stream to grab the chunks and then concat/decode them to log it. Simple solution and could be made more robust but it shows the concept works.
function bodyLog(req, res, next) {
var write = res.write;
var end = res.end;
var chunks = [];
res.write = function newWrite(chunk) {
chunks.push(chunk);
write.apply(res, arguments);
};
res.end = function newEnd(chunk) {
if (chunk) { chunks.push(chunk); }
end.apply(res, arguments);
};
res.once('finish', function logIt() {
var body = Buffer.concat(chunks).toString('utf8');
// LOG BODY
});
next();
}
And then set it before any routes are assigned in the main app router:
app.use(bodyLog);
// assign routes
I would assume you could also use this as an assignment for a variable in Morgan but I haven't looked into how async variable assignment would work.
Well, i'm having some difficult to understand the use of angular.toJson. I completely understand that it changes to a json object...
But, how can i send this obj to the server ?
The server already gives a json obj when 'GET', but how use it to 'POST' and others?
sorry, i'm new :)
You can create factory in your app:
var app = angular.module('myApp', []);
app.factory('requestsFactory', ['$http', function ($http) {
return {
postData: function (data) {
var url = // some url to send your data
return $http.post(data, url);
};
};
}];
And now, you can post your data from controllers:
app.controller('yourController', ['$scope', 'requestsFactory', function ($scope, requestsFactory) {
...
requestFactory.postData(anyData).success(function (result) {
// if server send any response
}
...
}]);
Also you can use $http for GET, PUT, DELETE requests. Click here for more information
You don't actually need to call angular.toJson() yourself when posting data to a server or retrieving data from the server using the $http service.
This is the default behaviour that Angular does for you.
From the docs:
Angular provides the following default transformations:
Request transformations ($httpProvider.defaults.transformRequest and
$http.defaults.transformRequest):
If the data property of the request configuration object contains an
object, serialize it into JSON format.
Response transformations
($httpProvider.defaults.transformResponse and
$http.defaults.transformResponse):
If XSRF prefix is detected, strip it (see Security Considerations
section below).
If JSON response is detected, deserialize it using a
JSON parser.