typestack / routing-controllers

Create structured, declarative and beautifully organized class-based controllers with heavy decorators usage in Express / Koa using TypeScript and Routing Controllers Framework.
MIT License
4.4k stars 393 forks source link

0.6.10 Not compatiable with typeorm-routing-controllers-extensions #89

Closed laukaichung closed 7 years ago

laukaichung commented 7 years ago

It seems that all of the @EntityFromXXX decorators no longer work with the latest 0.6.10 version when the parseJson option is enable. There is a JsonParse error when transforming the string into a class.

For example, client side:

let formData = new FormData();
formData.append('json_data') = {id:5}
axios.post('/file',formData)

server side:

@JsonResponse()
@Post(`/file`)
upsert(@EntityFromBodyParam('json_data',{parseJson:true} ) item: Item) {
     return item;
}

It will throw:

(node:23646) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ParameterParseJsonError: Given parameter json_data is invalid. Value ("{\"id\":5}") cannot be parsed to JSON
(node:23646) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

The error seems to come from

if (!(value instanceof paramMetadata.format) && paramMetadata.format !== Object && paramMetadata.format && this.driver.useClassTransformer) {

in ParamHandler.js of the routing controllers:

   ParamHandler.prototype.parseValue = function (value, paramMetadata) {
        return __awaiter(this, void 0, void 0, function () {
            var valueObject, parsedValue_1, options, options;
            return __generator(this, function (_a) {
                try {
                    valueObject = typeof value === "string" ? JSON.parse(value) : value;

                    if (!(value instanceof paramMetadata.format) && paramMetadata.format !== Object && paramMetadata.format && this.driver.useClassTransformer) {
                        options = paramMetadata.classTransformOptions || this.driver.plainToClassTransformOptions;
                        parsedValue_1 = class_transformer_1.plainToClass(paramMetadata.format, valueObject, options);
                    }
                    else {
                        parsedValue_1 = valueObject;
                    }
                    if (paramMetadata.validate || this.driver.enableValidation) {
                        options = paramMetadata.validationOptions || this.driver.validationOptions;
                        return [2 /*return*/, class_validator_1.validateOrReject(parsedValue_1, options)
                                .then(function () { return parsedValue_1; })
                                .catch(function (validationErrors) {
                                var error = new BadRequestError_1.BadRequestError("Invalid " + paramMetadata.type + ", check 'details' property for more info.");
                                error.details = validationErrors;
                                throw error;
                            })];
                    }
                    else {
                        return [2 /*return*/, parsedValue_1];
                    }
                }
                catch (er) {
                    throw new ParameterParseJsonError_1.ParameterParseJsonError(paramMetadata.name, value);
                }
                return [2 /*return*/];
            });
        });
    };
MichalLytek commented 7 years ago

Error comes from this line:

private async parseValue(value: any, paramMetadata: ParamMetadata) {
        try {
            const valueObject = typeof value === "string" ? JSON.parse(value) : value;

The parameter is parsed as JSON and it's invalid - you have escape chars in the JSON string. This line looks bad for me: formData.append('json_data') = {id:5} This isn't a correct call, check here. You should do sth like this:

formData.append('json_data', JSON.stringify({ id: 5 }));
pleerock commented 7 years ago

@stonecold123 any updates on this issue?

pleerock commented 7 years ago

okay Im closing this

laukaichung commented 7 years ago

My solution in the latest 0.1 version is adding this validation option as the second argument in BodyParam(), like this:

@BodyParam("json_data",{validate:{skipMissingProperties:true}})

The BadRequest error returned from the controller isn't clear enough. I set a break point in the catch block in this part to understand the problem:

    RoutingControllers.prototype.executeAction = function (actionMetadata, action, interceptorFns) {
        var _this = this;
        // compute all parameters
        var paramsPromises = actionMetadata.params
            .sort(function (param1, param2) { return param1.index - param2.index; })
            .map(function (param) { return _this.parameterHandler.handle(action, param); });
        // after all parameters are computed
        return Promise.all(paramsPromises).then(function (params) {
            // execute action and handle result
            var allParams = actionMetadata.appendParams ? actionMetadata.appendParams(action).concat(params) : params;
            var result = actionMetadata.methodOverride ? actionMetadata.methodOverride(actionMetadata, action, allParams) : actionMetadata.callMethod(allParams);
            return _this.handleCallMethodResult(result, actionMetadata, action, interceptorFns);
        }).catch(function (error) {
            // otherwise simply handle error without action execution
            return _this.driver.handleError(error, actionMetadata, action);
        });
    };
github-actions[bot] commented 3 years ago

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.