In interceptor, we have "content", but in middleware, how can I get it ?
Do you know what is express/koa middleware?
Yes. I know. Interceptor and Middleware are all express/koa middleware, right ?
But the "content" is the return value from the "controller"'s action. It's a concept of the routes-controllers.
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle.
Middlewares are like a stack or a chain. They are used to do some operations on request or response, like parsing session cookie and injecting session data from store to req property, and then calling next middleware in the chain (like your request handler).
If you need to do something with data returned from an action, just use interceptor - you have there req, res and content. Interceptor is like a middleware with always calling next by routing-controllers.
What is your use case of interceptor-middleware? What middleware can do that interceptor can't?
@qinyang1980 you can do "interception" using something like this:
function modifyResponseBody(req, res, next) {
var oldSend = res.send;
res.send = function(data){
// arguments[0] (or `data`) contains the response body
arguments[0] = "modified : " + arguments[0];
oldSend.apply(res, arguments);
}
next();
}
app.use(modifyResponseBody);
@19majkel94 I think author asked this question because I told him that I'm going to deprecate "interceptor" functionality. We should discuss this, looks like somebody is using this weird functionality
Thanks.
@19majkel94 so what do you think about interceptors in routing-controllers?
According to commit 172930fc, it's not so much code related with this functionality. The question is how many people use it (we have at least one people already 😆) - maybe do you have a real use-case example? Why have you implemented this?
I implemented this because I wanted globally "intercept" returned value and lets say replace something in there...
@pleerock @19majkel94 I think that intercept is a result of the processor before responsing, in the express framework, middleware can not get the results of a previous middleware, because previous middleware has call method "res.send". overwriting res.send looks bad。so, I think the interceptor is necessary。
before:
@InterceptorGlobal()
export class FormatJsonResultInterceptor implements InterceptorInterface {
async intercept(request: any, response: any, result: any): Promise<any> {
let data = await result;
return wrapperResult(data);
}
}
now:
@Middleware({type: "before"})
export class FormatJsonResultMiddleware implements ExpressMiddlewareInterface {
use(req: any, res: any, next?: (err?: any) => any): any {
var oldJson = res.json;
res.json = function (data) {
arguments[0]=wrapperResult(data);
oldJson.apply(res, arguments);
};
next();
}
}
Additional to @lp6moon 's comment. @pleerock @19majkel94
If I want to use the original json method, I must do like this.
import * as Response from "express/lib/response";
//some codes...
@Get("/time")
time(@Res() res: Response) {
Response.json.call(res, Date.now());
}
It looks stupid.
I don't know it this kind of feature has to be implemented in core of the framework. Intercepting value returned from method can be done with decorators:
const log = (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor<Function>) => {
return {
value: function( ... args: any[]) {
console.log("Arguments: ", args.join(", "));
const result = descriptor.value.apply(target, args);
console.log("Result: ", result);
return result;
}
}
}
class Calculator {
@log
add(x: number, y: number) {
return x + y;
}
}
new Calculator().add(1, 3);
//Arguments: 1, 3
//Result: 4
You can also write class-based version or maybe even inheritance chain with BaseController to achieve a global intercepting feature.
And with this approach you can also intercept errors:
const log = (target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor<Function>) => {
return {
value: function( ... args: any[]) {
try {
console.log("Arguments: ", args.join(", "));
const result = descriptor.value.apply(target, args);
console.log("Result: ", result);
return { result };
} catch (error) {
throw new MyCustomError(error);
}
}
}
}
So you can format them to the payload you want.
I don't know it this kind of feature has to be implemented in core of the framework.
This was, but have been removed not?
Intercepting value returned from method can be done with decorators
Seems fair enough until an official implementation. :)
@19majkel94
You can also write class-based version or maybe even inheritance chain with BaseController to achieve a global intercepting feature.
How? It's complicated.
Actually it's not so complicated. My draft looks like this:
type ReturnWrapper = (returnedValue: any) => any
type WrappedFunction = Function & { isWrapped?: true }
const ReturnedValueWrapper = (returnedValue) => {
return {
value: returnedValue,
randomNumber: Math.random(),
}
}
const ApplyWrapper = (originalMethod: WrappedFunction, valueWrapper: ReturnWrapper) => {
if (originalMethod.isWrapped) {
return function() {
return originalMethod.apply(this, arguments)
}
} else {
const patchedMethod: WrappedFunction = function() {
return valueWrapper(originalMethod.apply(this, arguments))
}
patchedMethod.isWrapped = true
return patchedMethod
}
}
function InterceptAllMethods(wrapper: ReturnWrapper): ClassDecorator {
return (target) => {
for (const methodName in target.prototype) {
if (methodName != "constructor") {
target.prototype[methodName] = ApplyWrapper(target.prototype[methodName], wrapper)
}
}
}
}
@InterceptAllMethods(ReturnedValueWrapper)
class SampleClass {
propertyB = 2;
methodA(param: string) {
throw new Error(param)
}
methodB() {
return this.propertyB
}
}
@InterceptAllMethods(ReturnedValueWrapper)
class InheritedClass extends SampleClass {
propertyC = true;
methodC() {
return `${this.propertyC}`
}
methodD() {
throw new Error("Error throwed");
}
}
const sampleObject = new SampleClass()
try { sampleObject.methodA("test123") } catch (error) { console.log(error) }
console.log(sampleObject.methodB())
console.log(sampleObject.methodB())
const inheritedObject = new InheritedClass()
console.log(inheritedObject.methodB())
console.log(inheritedObject.methodB())
console.log(inheritedObject.methodC())
console.log(inheritedObject.methodD())
The API is simple, just create your own ReturnedValueWrapper function and decorate the class with @InterceptAllMethods. Unfortunately you have to decorate also inherited classes for intercepting their new methods.
I will try to make an method's decorator version with support for error intercepting and publish it as npm package if you would like to 😉
since inteceptors are back now guys you can use interceptors functionality for your purpose.
Thanks!
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
since inteceptors are back now guys you can use interceptors functionality for your purpose.