Skip to content
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

Servicecomb3.0不支持BigDecimal类型 #4637

Closed
myhaiting opened this issue Dec 10, 2024 · 10 comments
Closed

Servicecomb3.0不支持BigDecimal类型 #4637

myhaiting opened this issue Dec 10, 2024 · 10 comments
Labels

Comments

@myhaiting
Copy link
Contributor

背景

项目从Servicecomb2.8迁移到Servicecomb3.2.3

问题

BigDecimal作为参数或返回值时无法正常解析

1、BigDecimal参数

@GetMapping("/demo") public String echo(@RequestParam BigDecimal v) { return "ok"; }

报错信息

Caused by: java.lang.IllegalStateException: failed to find producer parameter in contract, method=ConsumerEndpoint:echo, parameter name=v. at org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperCreator.processUnknownParameter

2、BigDecimal返回值

@GetMapping("/demo") public BigDecimal echo(@RequestParam String v) { return null; }

报错信息

Caused by: java.lang.Error: not support def type: class io.swagger.v3.oas.models.media.NumberSchema at org.apache.servicecomb.swagger.converter.ConverterMgr.findJavaType(ConverterMgr.java:111)

预期

应该能正常序列化和反序列化

@liubao68 liubao68 added bug 3.x and removed bug labels Dec 11, 2024
@liubao68
Copy link
Contributor

你们前端是怎么调用这个接口的? ?param=333333.3333 这样?

@myhaiting
Copy link
Contributor Author

就是小数用BigDecimal接收,因为计算都是BigDecimal,这样可以少写一个类型转换
主要是原先2.8是正常的,迁移过来就启动不了

@liubao68
Copy link
Contributor

就是小数用BigDecimal接收,因为计算都是BigDecimal,这样可以少写一个类型转换 主要是原先2.8是正常的,迁移过来就启动不了

这个接口主要是用于内部调用吗?我问上面的问题主要是关注下前端是怎么调用这个接口的(比如通过HTTP 测试工具, 浏览器等)。 因为需要考虑REST接口方面的兼容。

@myhaiting
Copy link
Contributor Author

就是小数用BigDecimal接收,因为计算都是BigDecimal,这样可以少写一个类型转换 主要是原先2.8是正常的,迁移过来就启动不了

这个接口主要是用于内部调用吗?我问上面的问题主要是关注下前端是怎么调用这个接口的(比如通过HTTP 测试工具, 浏览器等)。 因为需要考虑REST接口方面的兼容。

是的,都是内部调用

@liubao68
Copy link
Contributor

BigDecimal等作为queryheader参数,还没有确切的序列化方式定义; 作为body参数,需要合适的jackson支持即可。 这个需要详细考虑下规格。 参考: https://servicecomb.apache.org/references/java-chassis/zh_CN/build-provider/http-mapping.html

@RequestParam BigDecimal 这种用法目前被识别为 Query参数聚合为POJO对象, 这显然不是预期的。

还需要再考虑下怎么定义这种场景的序列化表示。

@liubao68
Copy link
Contributor

liubao68 commented Dec 17, 2024

https://swagger.io/docs/specification/v3_0/data-models/data-types/#numbers OpenAPI 数据类型只有 number和integer,无法涵盖 BigInteger和BigDecimal。 但是一般也是用这两种类型表示。 因此使用BigInteger和BigDecimal不是一个符合OpenAPI的好的实践,但也可以考虑支持query/header/form这些简单参数使用。

@myhaiting
Copy link
Contributor Author

https://swagger.io/docs/specification/v3_0/data-models/data-types/#numbers OpenAPI 数据类型只有 number和integer,无法涵盖 BigInteger和BigDecimal。 如果要支持估计只能采用String类型表示。

我临时修改是这样,不是很规范,但能正常工作:

public static boolean isBean(Type type) {
if (type == null) {
return false;
}
JavaType javaType = TypeFactory.defaultInstance().constructType(type);
if (javaType.isContainerType() || javaType.isEnumType() || javaType.isTypeOrSubTypeOf(DynamicEnum.class)) {
return false;
}
Class<?> cls = javaType.getRawClass();
if (ClassUtils.isPrimitiveOrWrapper(cls)) {
return false;
}
return (cls != String.class
&& cls != Date.class
&& cls != LocalDate.class
&& cls != LocalDateTime.class
&& cls != byte[].class
&& cls != File.class
&& !cls.getName().equals("org.springframework.web.multipart.MultipartFile")
&& !Part.class.isAssignableFrom(cls));
}

改成:

return (cls != String.class
        && cls != Date.class
        && cls != LocalDate.class
        && cls != LocalDateTime.class
        && cls != BigDecimal.class
        && cls != byte[].class
        && cls != File.class
        && !cls.getName().equals("org.springframework.web.multipart.MultipartFile")
        && !Part.class.isAssignableFrom(cls));

private static void initTypeFormatMap() {
TYPE_FORMAT_MAP.put(genTypeFormatKey("boolean", ""),
TypeFactory.defaultInstance().constructType(Boolean.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int32"),
TypeFactory.defaultInstance().constructType(Integer.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int64"),
TypeFactory.defaultInstance().constructType(Long.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "float"),
TypeFactory.defaultInstance().constructType(Float.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "double"),
TypeFactory.defaultInstance().constructType(Double.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("string", ""),
TypeFactory.defaultInstance().constructType(String.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date"),
TypeFactory.defaultInstance().constructType(LocalDate.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date-time"),
TypeFactory.defaultInstance().constructType(LocalDateTime.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "password"),
TypeFactory.defaultInstance().constructType(String.class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "byte"),
TypeFactory.defaultInstance().constructType(Byte[].class));
TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "binary"),
TypeFactory.defaultInstance().constructType(Part.class));
}

加了一行

TYPE_FORMAT_MAP.put(genTypeFormatKey("number", ""),
            TypeFactory.defaultInstance().constructType(BigDecimal.class));

liubao68 added a commit to liubao68/servicecomb-java-chassis that referenced this issue Dec 17, 2024
@liubao68
Copy link
Contributor

你的方案感觉是可行的。 我也是按照你的方案改的。

@liubao68
Copy link
Contributor

liubao68 commented Dec 17, 2024

上述修改方案测试代码的swagger如下。 意味着在servicecomb内部使用 BigDecimalBigInteger是没有问题的。 但是考虑异构框架的互操作(尽管目前没有)的时候,还是会有些语义不清晰。 Open API number, integer类型无法表示 BigDecimalBigInteger, 会存在精度损失。

openapi: 3.0.1
info:
  title: swagger definition for org.apache.servicecomb.demo.springmvc.server.BigNumberSchema
  version: 1.0.0
servers:
- url: /bigNumber
paths:
  /decimal:
    post:
      operationId: bigDecimal
      parameters:
      - name: decimalHeader
        in: header
        required: true
        schema:
          type: number
      - name: decimalQuery
        in: query
        required: true
        schema:
          type: number
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                decimalForm:
                  type: number
      responses:
        "200":
          description: response of 200
          content:
            application/json:
              schema:
                type: number
            application/protobuf:
              schema:
                type: number
            text/plain:
              schema:
                type: number
  /integer:
    post:
      operationId: bigInteger
      parameters:
      - name: intHeader
        in: header
        required: true
        schema:
          type: integer
      - name: intQuery
        in: query
        required: true
        schema:
          type: integer
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                intForm:
                  type: integer
      responses:
        "200":
          description: response of 200
          content:
            application/json:
              schema:
                type: integer
            application/protobuf:
              schema:
                type: integer
            text/plain:
              schema:
                type: integer
components: {}

@liubao68
Copy link
Contributor

上述swagger按照理解也是符合swagger语义的,没问题,理解错了。 see: https://swagger.io/docs/specification/v3_0/data-models/data-types/#numbers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants