-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error handling for non-API routes #1784
Comments
I have already spent some time thinking about this problem, here are few solutions for consideration. 1. Implement static middleware as a virtual routeIn LB4, API requests are handled by a Sequence, which is designed with the assumption that the Sequence is going to handle all requests and implement error handling too (including 404 not found). IMO, removing this assumption and changing the design of Sequence to add a third possible outcome - let somebody else handle the request - would be a big step backwards and we should avoid that. Also mounting serve-static middleware at Express layer is bypassing the sequence entirely. This is likely to have unintended consequences that may bite us hard. For example, happens serve-static cannot read a file, e.g. because of a permission problem, then it forward the error via I am wondering: Can we define a special kind of a catch-all route that will:
In my mind, such solution would solve most if not all issues:
2. Move
|
Btw, if the static asset is not found, the request is passed on to the Sequence, and eventually it is caught by the On the other hand, a read permission error is unhandled and actually crashes the app. |
This is true in the current design, where static middleware handlers are executed before our sequence. Such design has severe performance implications. If you mount a static middleware handler at The issue #1785 is keeping track of this problem and possible solutions. One option is to mount static middleware handler after our sequence, in which case 404 errors will be no longer handled by the sequence. |
Will report my findings for each option one after. 1. Implement static middleware as a virtual route Notes:
|
Our routes are extensible. We have I think in this particular case, the handler-function-based
I am afraid I don't agree with this argument. As you can see in RestServer, This router is registered with the internal Express app inside As I was envisioning the solution, we need to make two changes:
class RestServer {
// ...
protected _handleStaticAssetRequest(req, res) {
return new Promise((resolve, reject) => {
this._routerForStaticAssets.handle(req, res, (err) => {
if (err) return reject(err);
// router called next, which means no route was matched
return reject(new HttpError.NotFound());
});
});
}
// somewhere in the code setting up RoutingTable
// the anonymous RouteEntry object should be refactored into a class
this.routingTable.catchAllRoute = <RouteEntry>{
verb: 'get',
path: '*',
spec: { /* TODO: this route should be excluded from the spec */ },
updateBindings(requestContext: Context) {
// no-op
},
invokeHandler(
{request, response}: Context,
args: OperationArgs,
): Promise<OperationRetval> {
return this._handleStaticAssetRequest(request, response);
}
describe(): string {
return "final route to handle static assets";
}
};
} There is one catch in this approach though: when this special route is invoked and the static asset was found, the "invokeHandler" method never returns, therefore the control is never returned back to the sequence. A possible solution: wait until class RestServer {
// ...
protected _handleStaticAssetRequest(req, res) {
return new Promise((resolve, reject) => {
const onFinished = () => resolve();
req.on('finish', onFinished);
this._routerForStaticAssets.handle(req, res, (err) => {
req.removeListener('finish', onFinished);
if (err) return reject(err);
// Express router called next, which means no route was matched
return reject(new HttpError.NotFound());
});
});
}
} The last missing piece I see is how to exclude a route from the generated API spec. Personally, I'd introduce an extension property allowing developers to mark any route as not documented. For example: const spec: OperationObject = {
'x-internal': true,
description: 'return all customers',
parameters: [
// ...
],
responses: {
// ...
},
}; Few alternatives to
|
I stand corrected. I was trying to add a new route to the app after it started. What we need to do is register a router, and then add routes to the router. |
Fixed via #1848 |
@hacksparrow can you please tell me how to solve "Error handling for non-API routes"? I have checked the reference that you have mentioned but could not find a solution. |
In #1611, we added API allowing app developers to serve static assets from their applications. The implementation was focused on enabling the happy path (serve a file that exists and can be fully read) but neglected error handling.
When a static asset is not found and/or cannot not be read, the
reject
error handler is not invoked and a very bare-bone fallback handler is used instead.https://github.com/strongloop/loopback-next/blob/0364b59409ce3691babf5f17b5cd444b4e7f72aa/packages/rest/src/rest.server.ts#L654-L666
Let's find a way that enables LB4 app developers to control the error responses for static assets (and any non-API routes in general).
The text was updated successfully, but these errors were encountered: