-
Notifications
You must be signed in to change notification settings - Fork 508
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
setting status codes is not working within Promise #94
Comments
I found a workaround that seem not to be documented. You can throw an
In a controller: In a data service class:
What is happening here? First the controller function checks the input parameter and if there is an invalid input value it sets the error code 400. Next, since all input values are valid, we set the error code 200 for success. If there is an error down the road the success state will be replaced with an error code. |
We are facing the same issue. It seems as if status codes are only applied upon the next reponse. Throwing an error could be a solution to this problem when dealing with 4** codes but still doesn't provide a solution for object creation (201) |
express.js create error handle middleware const statuses = require("statuses");
export function errorHandler() {
return function (err: any, req: express.Request, res: express.Response, next: express.NextFunction) {
let status = err.status || 500;
if (status < 400) {
status = 500;
}
const body: any = {
statusCode: status,
};
// Internal server errors
if (status >= 500) {
console.error(err.stack);
body.name = "ServerError";
body.message = err.message || statuses[status];
res.status(status).json(body);
return;
}
// Client errors
body.message = err.message || statuses[status];
if (err.name) body.name = err.name;
res.status(status).json(body);
};
} |
We may have the same problem here. We're trying to make a controller to send 201 as a status when creation through a POST operation is successful.
When logged through Morgan, Express shows a return status of 201, but whatever the client (cURL, Chrome, Postman), the status code stays 200 ... |
@jpramondon This is not a very elegant solution but a workaround until the issue is fixed. |
Thank you for your reply @ryo-nbx. I think I'm going to test the latest version (1.2.0) first. There seem to be an interesting commit that we should try. |
@jpramondon You don't need to throw a 2xx status with my solution. You just set it as default status before doing any further evaluation and processing. You only throw error status (4xx). |
No problem @ryo-nbx. |
@isman-usoh This commit resolves part of the issue - it works if controller's method returns Promise which gets resolved immediately (like in your tests), but as soon as you add a delay for Promise resolving - it fails. @Get('customNomalStatusCode')
public async customNomalStatusCode(): Promise<TestModel> {
const that = this;
const service = new ModelService();
const promise = service.getModelPromise();
return new Promise<TestModel>(resolve => {
that.statusCode = 201;
resolve(promise);
});
} But next code will return status code 200: @Get('customNomalStatusCode')
public async customNomalStatusCode(): Promise<TestModel> {
const that = this;
const service = new ModelService();
const promise = service.getModelPromise();
return new Promise<TestModel>(resolve => {
setTimeout(() => {
that.statusCode = 201;
resolve(promise);
}, 1000);
});
} Or example from real life - doesn't work either, returns 200: @Post()
public async createItem( @Body() a: CreateItemDto ): Promise<Item> {
let that = this;
return library.create(a).then(createdId => {
that.statusCode = 201;
return library.getItemById(createdId);
});
} The only possible solution is to retrieve status code from Controller after Promise has been resolved, here is the possible way how function promiseHandler(promise: any, getStatusCode: () => any, response: any, next: any) {
return promise
.then((data: any) => {
let statusCode = getStatusCode();
if (data) {
response.status(statusCode || 200).json(data);
} else {
response.status(statusCode || 204).end();
}
})
.catch((error: any) => next(error));
} And here is how express route template should be changed to make it work: app.{{method}}('{{../../basePath}}/{{../path}}{{path}}',
.........more code here.......
const promise = controller.{{name}}.apply(controller, validatedArgs);
function getStatusCode() {
let statusCode: any;
if (controller instanceof Controller) {
statusCode = (controller as Controller).getStatus();
}
return statusCode
}
promiseHandler(promise, getStatusCode, response, next);
}); I can create a PullRequest if you approve this. |
I really didn't notice the behaviour you mention as I only ever used the fix either in a unit test or in a very minimal API, which apparently does not generate enough delay in the promise resolution. @amozh, this really some good idea. The only effective way is indeed to resolve the status code after the Promise is completely terminated. I like the idea of accessing the status from inside the Promise resolution, based on an status accessor in the Controller. |
- update route schema and update type - change swagger enum inline type to enum definitions - add new rule for tslint - fix set status not work lukeautry#94 - support custom response header lukeautry#89
- update route schema and update type - change swagger enum inline type to enum definitions - add new rule for tslint - fix set status not work lukeautry#94 - support custom response header lukeautry#89
- Support Query array type - Support custom response header (#89) - Support tsoa config yaml format (#133) - Support generate swagger yaml format - Support string, number and boolan UnionType (#5) - Support AnyKeyword (#114) - update route schema and update type - Change swagger enum inline type to enum definitions - Add new rule for tslint - Fix set status not work #94 - Fix validate - Set query array collectionFormat, default to multi - Create file for Circle CI
- Support Query array type - Support custom response header (#89) - Support tsoa config yaml format (#133) - Support generate swagger yaml format - Support string, number and boolan UnionType (#5) - Support AnyKeyword (#114) - update route schema and update type - Change swagger enum inline type to enum definitions - Add new rule for tslint - Fix set status not work #94 - Fix validate - Set query array collectionFormat, default to multi - Create file for Circle CI
Since In
But you'll notice that the first
These solutions will work for you. In particular, I believe that the details in #382 will help you. I'm closing the ticket for house keeping purposes. If someone wants to add more information to the readme about this, that would be awesome. But for now, this issue should be easy to find by people. |
When defining error and success codes for API functions I realized a problem when using Promises. When setting a status code within a promise this code is not returned by the function.
For example this controller function:
@Tags('Car')
@SuccessResponse('200', 'successful')
@Response('404', 'not found')
@Response('407', 'internal error, could not process request')
@Get('{id}')
public async getCarById (@Path('id') id: string ) : Promise<Car> {
return service.getCar(id).then( (car): any => {
if (!car) {
// not found
this.setStatus(404);
}
return car;
}).catch(function (error) {
console.log(error);
// internal error
this.setStatus(407);
return null;
});
}
This function calls a method at a database service and gets a Promise. After evaluating the result of this promise a response code might be set. Calling this function with a missing ID should cause a 404 error. But in reality it returns 204. The console prompts a 404 object but this is not handled by the generated routes.
I tracked down the problem to the generated routes.ts file and its promiseHandler function.
function promiseHandler(promise: any, statusCode: any, response: any, next: any) {
The method takes a promise and status codes from the controller. This tells me that custom status codes are only used when they are set before the promiseHandler function call. If you set a code within a controller function (and not within a promised function) it will be used. If you use a Promise and set a code within it, this setStatus() call is executed after the promiseHandler() call and so the status code will be ignored.
I looked for a solution and there seems to be a simple one but it requires to change TSOA template code.
I think this should work:
tsoa/src/routeGeneration/templates/express.ts
...
export function RegisterRoutes(app: any) {
{{#each controllers}}
{{#each actions}}
...
const promise = controller.{{name}}.apply(controller, validatedArgs);
let tController = undefined;
if (controller instanceof Controller) {
tController = (controller as Controller);
}
promiseHandler(promise, tController, response, next);
...
function promiseHandler(promise: any, controller: Controller, response: any, next: any) {
return promise
.then((data: any) => {
let statusCode = undefined;
if (controller) {
statusCode = controller.getStatus();
}
if (data) {
response.json(data);
response.status(statusCode || 200);
} else {
response.status(statusCode || 204);
response.end();
}
})
.catch((error: any) => next(error));
}
My solution would be to let the generated promiseHandler() function ask the controller for status codes after retrieving a result from the promise.
The text was updated successfully, but these errors were encountered: