美文网首页
如何不通过@Controller编码方式批量暴露内网接口

如何不通过@Controller编码方式批量暴露内网接口

作者: uhfun | 来源:发表于2022-10-27 09:34 被阅读0次

    该文章为原创(转载请注明出处):如何不通过@Controller编码方式批量暴露内网接口 - 简书 (jianshu.com)

    真实业务场景

    希望在原有基础上暴露内网接口,且不希望使用nginx做转发
    例如api/xxx/lan/yyy定义为内网接口
    但是现有接口为service/xxx/yyy 服务调用接口

    需要达成目的
    自动将原有的 service/xxx/yyy 暴露为 api/xxx/lan/yyy,且不影响原有功能
    例如:原有接口

    @Controller
    @RequestMapping("/service")
    public class ServiceController {
        @PostMapping("/xxx/yyy")
        public Object yyy(@RequestBody Object body) {
        }
    }
    

    方案一(繁琐耗时,不好维护)

    手动方式调用

    @Controller
    @RequestMapping("/api")
    public class ApiLanController {
        @Autowired
        private ServiceController serviceController;
        @PostMapping("/xxx/lan/yyy")
        public Object yyy(@RequestBody Object body) {
            serviceController.yyy(body);
        }
    }
    

    方案二

    自动根据原有接口,暴露新接口

    package xxx;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.boot.context.properties.bind.Bindable;
    import org.springframework.boot.context.properties.bind.Binder;
    import org.springframework.core.env.Environment;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    import static java.util.Collections.emptySet;
    
    class ForwardingRequestMapping implements BeanPostProcessor {
            private final Set<String> paths;
            public ForwardingRequestMapping(Environment environment) {
                paths = Binder.get(environment).bind("xxx.mapping-lan-forwarding", Bindable.setOf(String.class)).orElse(emptySet());
            }
    
            private static RequestMappingInfo buildNewRequestMappingInfo(RequestMappingInfo info, RequestMappingInfo.BuilderConfiguration config) {
                RequestMappingInfo.Builder builder = RequestMappingInfo
                        .paths(changePatterns(info.getPatternsCondition().getPatterns().toArray(new String[0])))
                        .methods(info.getMethodsCondition().getMethods().toArray(new RequestMethod[0]))
                        .params(info.getParamsCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new))
                        .headers(info.getHeadersCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new))
                        .consumes(info.getConsumesCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new))
                        .produces(info.getProducesCondition().getExpressions().stream().map(Object::toString).toArray(String[]::new))
                        .mappingName(info.getName());
                builder.customCondition(info.getCustomCondition());
                return builder.options(config).build();
            }
            private static String[] changePatterns(String[] patterns) {
                String[] newPatterns = new String[patterns.length];
                for (int i = 0, patternsLength = patterns.length; i < patternsLength; i++) {
                    // serviceController service/xxx/yyy 暴露为 api/xxx/lan/yyy
    //                newPatterns[i] = 
                }
                return newPatterns;
            }
    
            public static RequestMappingInfo.BuilderConfiguration builderConfiguration(RequestMappingHandlerMapping mapping) {
                RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
                config.setUrlPathHelper(mapping.getUrlPathHelper());
                config.setPathMatcher(mapping.getPathMatcher());
                config.setSuffixPatternMatch(mapping.useSuffixPatternMatch());
                config.setTrailingSlashMatch(mapping.useTrailingSlashMatch());
                config.setRegisteredSuffixPatternMatch(mapping.useRegisteredSuffixPatternMatch());
                config.setContentNegotiationManager(mapping.getContentNegotiationManager());
                return config;
            }
    
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof RequestMappingHandlerMapping) {
                    RequestMappingHandlerMapping mapping = ((RequestMappingHandlerMapping) bean);
                    Map<RequestMappingInfo, HandlerMethod> newMap = new HashMap<>();
                    RequestMappingInfo.BuilderConfiguration config = builderConfiguration(mapping);
                    mapping.getHandlerMethods().forEach((info, handleMethod) -> {
                        for (String pattern : info.getPatternsCondition().getPatterns()) {
                            if (paths.contains(pattern)) {
                                RequestMappingInfo newInfo = buildNewRequestMappingInfo(info, config);
                                newMap.put(newInfo, handleMethod);
                            }
                        }
                    });
                    // 注册新的映射规则
                    newMap.forEach((mappingInfo, handleMethod) -> mapping.registerMapping(mappingInfo, handleMethod.getBean(), handleMethod.getMethod()));
                }
                return bean;
            }
        }
    

    最终效果

    新增配置,自动将service/xxx/yyy根据规则暴露为新的api/xxx/lan/yyy

    xxx.mapping-lan-forwarding:
        - service/xxx/yyy
    

    前端访问api/xxx/lan/yyy请求,请求会反射调用到service/xxx/yyy对应的Controller的方法

    该文章为原创(转载请注明出处):如何不通过@Controller编码方式批量暴露内网接口 - 简书 (jianshu.com)

    相关文章

      网友评论

          本文标题:如何不通过@Controller编码方式批量暴露内网接口

          本文链接:https://www.haomeiwen.com/subject/zuixtdtx.html