I have a validation pipe that checks if a user-sent JSON data is valid. Validator is working great, but I can not catch the error and then send it to the client. I know about exeptionFactory
property in ValidationPipe
constructor, but I still can not catch an error and it still logs in the console.
[Nest] 11820 - 01/07/2023, 11:12:25 PM ERROR [WsExceptionsHandler] Bad Request Exception
BadRequestException: Bad Request Exception
Here is a code
@SubscribeMessage(Stream.Transactions)
@UseGuards(JwtAuthGuard)
@UsePipes(new ValidationPipe())
handleTransactions(
clien: any,
@MessageBody() data: TransactionObject,
) {
let req = this.streamService.transaction(data)
return { event: Stream.Transactions, data: req }
}
CodePudding user response:
I think you can create a filter to get the error and return some specific data. But you can do it in a couple ways: Creating a Websocket exception filter to catch the error or use the exceptionFactory you mention in your question to generate a WsException
and catch into the filter.
The main problem (if I'm not wrong) is the pipe does not return a WsException
but a BadRequestException
.
So to use an exception filter, how the exception is Bad Request
, you can use this one:
@Catch(BadRequestException)
export class BadRequestExceptionsFilter extends BaseWsExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// Here you have the exception and you can check the data
const wsException = new WsException(exception.getResponse())
super.catch(wsException, host);
}
}
Note how this code follows the documentation
And now not only you can get and read the exception but also you can create a properly WsException
.
To use this you can add @UseFilters(BadRequestExceptionsFilter)
into your gateway.
Another way is to catch WS and HTTP exceptions and handle properly you want, something similar to this example. The idea can be to catch the HTTP exception only to get Bad Request
so your desired context will be always WS
:
@Catch(WsException, HttpException)
export class WsAndHttpExceptionFilter {
public catch(exception: HttpException, host: ArgumentsHost) {
// Here you have the exception and you can check the data
const ctx = host.switchToWs()
const client = ctx.getClient() as WebSocket;
client.send(JSON.stringify({ /* ... */ }))
}
}
Or also you can try to create the exceptionFactory
to return the WsException
.
- Into the decorator:
@UsePipes(new ValidationPipe({
exceptionFactory(validationErrors: ValidationError[] = []) {
// Here are the errors
if (this.isDetailedOutputDisabled) {
return new WsException();
}
const errors = this.flattenValidationErrors(validationErrors);
return new WsException(errors);
}
}))
Check how factories are done into the project and how this code tries to follow the same way but returning WsException
.
- Or in a class extending
ValidationPipe
:
@Injectable()
export class WSValidationPipe extends ValidationPipe {
createExceptionFactory() {
return (validationErrors: ValidationError[] = []) {
// Here are the errors
if (this.isDetailedOutputDisabled) {
return new WsException();
}
const errors = this.flattenValidationErrors(validationErrors);
return new WsException(errors);
}
}
}
By the way, you can also @Catch(WsException)
(and only this exception which is clearer) once are thrown if it is util for you to return the data you want:
@Catch(WsException)
export class WebsocketExceptionsFilter extends BaseWsExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToWs()
const client = ctx.getClient() as WebSocket;
const data = ctx.getData();
client.send(JSON.stringify({
event: 'error',
ok: false,
error: exception.getError(),
data: data // Or whatever you want to add
}))
}
}
I can't test now all this code but I've used some of these snippets and works for me, hope it helps.