routing-controllers
routing-controllers copied to clipboard
question: How do I customize internal HttpErrors with custom Http errors with KoaDriver?
I was sending custom HttpErrors that changes the response to the user but I can't do this when I'm validating request bodies because it's using the one that came with the library.
This is the response I want to throw when a validation fails;
{
"status": false,
"message": "Validation errors.",
"errors": "ValidationError[] from class validator"
}
This is what it currently returns
{
"name": "BadRequestError",
"message": "Invalid body, check 'errors' property for more info.",
"errors": "ValidationError[] from class validator"
}
One way that I could find is to use this but since I'm using Koa I can't use it.
One workaround for this is disabling the validator from @Body()
and calling class-validator inside the request function.
@coneforapine did you find the solution? i have the same issue with the expressjs. I do like this:
export class CreateUserDto {
@IsString({ message: 'lastName_should_be_string'.toUpperCase() })
lastname: string;
}
And this is my custom error handler:
export class Response<T> {
successful: boolean;
message?: string;
data: T;
constructor(success: boolean, data: T, message?: string) {
this.successful = success;
this.data = data;
if (message) this.message = message;
}
}
@Middleware({ type: 'after' })
export class CustomErrorHandler implements ExpressErrorMiddlewareInterface {
error(
error: HttpError | ValidationError | UnauthorizedError,
request: Request,
response: expressResponse,
): void {
logger.error('http error occurred', {
meta: {
...serializeError(error),
},
});
if (error instanceof HttpError) {
response
.status(error.httpCode)
.json(new Response<null>(false, null, error.message));
} else if (error instanceof ValidationError) {
response
.status(400)
.json(new Response(false, { errors: error.constraints }));
} else if (error instanceof UnauthorizedError) {
response
.status(401)
.json(new Response<null>(false, null, 'unauthorized'));
} else {
response
.status(500)
.json(new Response(false, {}, 'INTERNAL_SERVER_ERROR'));
}
}
}
const routingControllersOptions = {
controllers: [ /* controllers listed here */ ],
middlewares: [CustomErrorHandler],
routePrefix: '/api',
defaultErrorHandler: false,
};
and I get :
{
"success": false,
"data": null,
"message": "Invalid body, check 'errors' property for more info.",
}
and this is what I expect to receive :
{
"success": false,
"data": null,
"message": "LASTNAME_SHOULD_BE_STRING",
}
@MichalLytek @jotamorais what do you think. what is my mistake?
I got the problem, When I turn false
to true
in defaultErrorHandler
i get a long response but that is too much for me:
{
"name": "BadRequestError",
"message": "Invalid body, check 'errors' property for more info.",
"stack": "Error: \n at new HttpError (/home/kasir/Salam-Sakhteman/infiniti-platform-server/node_modules/src/http-error/HttpError.ts:16:18)\n at new BadRequestError (/home/kasir/Salam-Sakhteman/infiniti-platform-server/node_modules/src/http-error/BadRequestError.ts:10:5)\n at /home/kasir/Salam-Sakhteman/infiniti-platform-server/node_modules/src/ActionParameterHandler.ts:233:30\n at async ActionParameterHandler.normalizeParamValue (/home/kasir/Salam-Sakhteman/infiniti-platform-server/node_modules/src/ActionParameterHandler.ts:141:15)\n at async Promise.all (index 0)",
"errors": [
{
"target": {
"user": {
"username": "ss-53111700",
"name": "سلام",
"lastname": "تست میکنم",
"phoneNumber": false
},
"serviceType": "5f930a87b509a90012591a00",
"location": {
"province": "5e196d4fc75ee00018183bed",
"city": "5f1d4bc02811b00011d6b331"
},
"provinces": [
"5ef07dcb492cc20018681060",
"5e196d58c75ee00018183bee"
],
"step": "first-sign"
},
"value": {
"username": "ss-53111700",
"name": "سلام",
"lastname": "تست میکنم",
"phoneNumber": false
},
"property": "user",
"children": [
{
"target": {
"username": "ss-53111700",
"name": "سلام",
"lastname": "تست میکنم",
"phoneNumber": false
},
"value": false,
"property": "phoneNumber",
"children": [],
"constraints": {
"matches": "PHONENUMBER_SHOULD_BE_IRANIAN_PHONE_NUMBER"
}
}
]
},
{
"target": {
"user": {
"username": "ss-53111700",
"name": "سلام",
"lastname": "تست میکنم",
"phoneNumber": false
},
"serviceType": "5f930a87b509a90012591a00",
"location": {
"province": "5e196d4fc75ee00018183bed",
"city": "5f1d4bc02811b00011d6b331"
},
"provinces": [
"5ef07dcb492cc20018681060",
"5e196d58c75ee00018183bee"
],
"step": "first-sign"
},
"value": {
"province": "5e196d4fc75ee00018183bed",
"city": "5f1d4bc02811b00011d6b331"
},
"property": "location",
"children": [
{
"target": {
"province": "5e196d4fc75ee00018183bed",
"city": "5f1d4bc02811b00011d6b331"
},
"property": "locationType",
"children": [],
"constraints": {
"isEnum": "LOCATIONTYPE_VALUE_SHOULD_BE_VALID",
"isString": "NAME_SHOULD_BE_STRING"
}
}
]
}
]
}
But my question is why the error is instanceof HttpError
(import { HttpError } from 'routing-controllers';
) not ValidationError
(import { ValidationError } from 'class-validator';
) and the error.errors is of type ValidationError[]
so i did like this:
class CustomValidationError {
errors: ValidationError[];
}
if ('errors' in error && error.errors[0] instanceof CustomValidationError) {
const errorMessages: string[] = [];
for (let temp of error.errors) {
errorMessages.push(
...(temp.constraints
? Object.values(temp.constraints)
: ''),
);
}
response.status(400).json(new ErrorResponse(false, errorMessages));
}
do you know simpler way?
If I remember correctly you can use custom error handlers with express.
If I remember correctly you can use custom error handlers with express.
Hi, I solve it. IDK why, but the ValidationError
was in the error.errors
and it was an array of ValidationError
. so i did this kind of trick to send the error message to the client:
class CustomValidationError {
errors: ValidationError[];
}
/* CustomErrorHandler class - error method */
if (
'errors' in error &&
error.errors[0] instanceof ValidationError
) {
const errorMessages: { [x: string]: string }[] = findProp(
error,
'constraints',
);
response
.status(400)
.json(new ErrorResponse(false, getValues(errorMessages)));
}
function findProp(obj: any, key: string, result: any[] = []): any[] {
const proto = Object.prototype;
const ts = proto.toString;
const hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(result)) {
result = [];
}
for (let i in obj) {
if (hasOwn(i)) {
if (i === key) {
result.push(obj[i]);
} else if (
'[object Array]' === ts.call(obj[i]) ||
'[object Object]' === ts.call(obj[i])
) {
findProp(obj[i], key, result);
}
}
}
return result;
}
function getValues(arrayOfObjects: { [x: string]: string }[]): string[] {
const result: string[] = [];
for (let item of arrayOfObjects) {
result.push(...Object.values(item));
}
return result;
}
and with this trick, I resolve it. HOPE this would be helpful later. :dart:
Closing this as stale.
If the issue still persists, you may open a new Q&A in the discussions tab and someone from the community may be able to help.
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.