TongchengOpenSource / smart-doc

Smart-doc is a java restful api document generation tool. Smart-doc is based on interface source code analysis to generate interface documentation, completely zero-injection.
https://smart-doc-group.github.io/#/
Apache License 2.0
1.33k stars 263 forks source link

Customizing RequestMappingHandlerMapping to implement multi-version management of interfaces fails to obtain the real path #786

Closed laboratorys closed 3 weeks ago

laboratorys commented 3 weeks ago

Your Environment

Expected Behavior

By handling through RequestMappingHandlerMapping, multi-version support for interfaces is implemented. A custom @ApiVersion(2)annotation is created, which can specify the version, but the real URL cannot be obtained when generating documentation. Expected return:/api/v1/user/test

通过自定义RequestMappingHandlerMapping的处理实现接口多版本号支持,自定义了一个@ApiVersion(2)的注解,可以指定版本,在生成文档时无法获取到真实的url。 期望返回:/api/v1/user/test

Current Behavior

/api/{version}/user/test

Steps to Reproduce

@Data
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition>
{
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v(\\d+)/");

    private int apiVersion;

    ApiVersionCondition(int apiVersion)
    {
        this.apiVersion = apiVersion;
    }

    @Override
    public ApiVersionCondition combine(ApiVersionCondition other)
    {
        return new ApiVersionCondition(other.getApiVersion());
    }

    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request)
    {
        Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if (m.find())
        {
            int version = Integer.parseInt(m.group(1));
            if (version >= getApiVersion())
            {
                return this;
            }
        }
        return null;
    }

    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request)
    {
        return other.getApiVersion() - getApiVersion();
    }

}
public class VersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping
{
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType)
    {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return createRequestCondition(apiVersion);
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method)
    {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return createRequestCondition(apiVersion);
    }

    private RequestCondition<ApiVersionCondition> createRequestCondition(ApiVersion apiVersion)
    {
        if (Objects.isNull(apiVersion))
        {
            return null;
        }
        int value = apiVersion.value();
        Assert.isTrue(value >= 1, "Api Version Must be greater than or equal to 1");
        return new ApiVersionCondition(value);
    }

}
@RestController
@ApiVersion
@RequestMapping("/api/{version}/user")
public class TestController
{

    /**
     * test-v1
     */
    @GetMapping("/test")
    public Body<?> v1()
    {
        return Body.ok("v1");
    }

    /**
     * test-v2
     */
    @GetMapping("/test")
    @ApiVersion(2)
    public Body<?> v2()
    {
        return Body.ok("v2");
    }
}

Context

Unable to obtain the real path. 无法获取到真实路径

shalousun commented 3 weeks ago

The custom annotation "smart-doc" is not supported for parsing. Unlike large model tools, smart-doc may not be able to recognize the intent of a custom annotation. Of course, for your scenario, you can define the API version as a constant and it can be normally parsed in the path. For custom annotations to be supported, it would require internal enterprise development based on smart-doc, but this also has its downsides. After separating from the community, maintenance could become more troublesome. The optimal solution is still to use constants as a replacement.

自定义注解smart-doc是不支持解析的,smart-doc不像大模型工具可能可以识别出自定义注解的意图。当然针对你的这个场景你可以将API 版本定义成常量,然后在path中是可以正常解析的。对于自定义的注解如果要支持只能是基于smart-doc做企业的内部二次开发,但是这个也有坏处,脱离了社区之后维护会比较麻烦。最优的方式还是用常量替代。

laboratorys commented 3 weeks ago

The custom annotation "smart-doc" is not supported for parsing. Unlike large model tools, smart-doc may not be able to recognize the intent of a custom annotation. Of course, for your scenario, you can define the API version as a constant and it can be normally parsed in the path. For custom annotations to be supported, it would require internal enterprise development based on smart-doc, but this also has its downsides. After separating from the community, maintenance could become more troublesome. The optimal solution is still to use constants as a replacement.

自定义注解smart-doc是不支持解析的,smart-doc不像大模型工具可能可以识别出自定义注解的意图。当然针对你的这个场景你可以将API 版本定义成常量,然后在path中是可以正常解析的。对于自定义的注解如果要支持只能是基于smart-doc做企业的内部二次开发,但是这个也有坏处,脱离了社区之后维护会比较麻烦。最优的方式还是用常量替代。

When multiple versions coexist in a single Controller, it is very cumbersome to define version numbers as constants, since you need to concatenate the version number in every interface. Using annotations is a very convenient and elegant method. I’ve seen that the community supports the @version annotation. Is it possible to add an @ApiVersion annotation with a special usage to replace the path variable?

多版本共存在一个Controller中时,用常量去定义版本号是非常麻烦的,需要在每个接口都去拼接版本号,通过注解的方式是非常方便和优雅的,我看到社区是支持@version注解的,是否可以增加一个注解@ApiVersion,给他一个特殊的用法,用以替换pathvariable。 例如

@RestController
@ApiVersion
@RequestMapping("/api/{ApiVersion}/user")
public class TestController
{

    /**
     * test-v1
     * @ApiVersion v1
     */
    @GetMapping("/test")
    public Body<?> v1()
    {
        return Body.ok("v1");
    }

    /**
     * test-v2
     * @ApiVersion v2
     */
    @GetMapping("/test")
    @ApiVersion(2)
    public Body<?> v2()
    {
        return Body.ok("v2");
    }
}
shalousun commented 3 weeks ago

Currently, it seems that all definitions are non-standard. The approach of adding a new @ApiVersion tag also doesn't seem elegant at the moment. The philosophy behind smart-doc is to firstly adhere to industry standards and mainstream open-source framework standardization, followed by serving as an auxiliary tool. Moreover, we have guided users in many documents to follow industry best practices as much as possible. Therefore, support for non-standardization will be very cautious at present unless a particularly good method is found from a design specification perspective. Depending on your scenario, you could either opt for secondary development or adopt the multi-version design specifications of excellent open-source projects like k8s (combining package+version for isolation), which can be well resolved when combined with constants.

目前看起来都是非标准定义。新增一个@ApiVersion tag的方式目前看起来也不优雅。smart-doc的理念的首先是遵循行业以及主流开源框架标准化,然后才是一个辅助工具, 并且我们已在很多文档中去引导用户尽量去遵循行业的最佳实践。因次对这种非标准化的支持目前会非常谨慎,除非找到一个从设计规范上都特别好的方式。 根据你们的场景要么采用二次开发,要么就是采用类似k8s这种行业优秀开源项目的多版本设计规范(package+version结合完成隔离) 结合常量可以很好的解决。

laboratorys commented 3 weeks ago

Thank you for the answer. Unless it’s a very large and complex project, our projects are usually quite simple and the API versions don't iterate frequently. Often, only a few interfaces need version upgrades. Because of this, we have to create new packages and version classes every time, which adds unnecessary overhead. I hope we can have some customizable interfaces to implement personalized features. This way, we can ensure that there’s no conflict with the philosophy of smart-doc while also allowing flexible customization. Resorting to secondary development should only be the last resort.

感谢解答,除非是很庞大复杂的项目,通常我们的项目比较简单,接口版本迭代也并不频繁,往往只有几个接口需要升级版本,位此,我们每次都要创建包和版本类去控制增加了不必要的开销。希望可以开放一些自定义的接口,去实现一些个性化的东西,这样既可以保证不和smart-doc的理念冲突,又可以灵活的定制,二次开发只能是最后不得已的选择了。