如何不通過@Controller編碼方式批量暴露內(nèi)網(wǎng)接口

該文章為原創(chuàng)(轉(zhuǎn)載請注明出處):如何不通過@Controller編碼方式批量暴露內(nèi)網(wǎng)接口 - 簡書 (jianshu.com)

真實(shí)業(yè)務(wù)場景

希望在原有基礎(chǔ)上暴露內(nèi)網(wǎng)接口,且不希望使用nginx做轉(zhuǎn)發(fā)
例如api/xxx/lan/yyy定義為內(nèi)網(wǎng)接口
但是現(xiàn)有接口為service/xxx/yyy 服務(wù)調(diào)用接口

需要達(dá)成目的
自動將原有的 service/xxx/yyy 暴露為 api/xxx/lan/yyy,且不影響原有功能
例如:原有接口

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

方案一(繁瑣耗時,不好維護(hù))

手動方式調(diào)用

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

方案二

自動根據(jù)原有接口,暴露新接口

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);
                        }
                    }
                });
                // 注冊新的映射規(guī)則
                newMap.forEach((mappingInfo, handleMethod) -> mapping.registerMapping(mappingInfo, handleMethod.getBean(), handleMethod.getMethod()));
            }
            return bean;
        }
    }

最終效果

新增配置,自動將service/xxx/yyy根據(jù)規(guī)則暴露為新的api/xxx/lan/yyy

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

前端訪問api/xxx/lan/yyy請求,請求會反射調(diào)用到service/xxx/yyy對應(yīng)的Controller的方法

該文章為原創(chuàng)(轉(zhuǎn)載請注明出處):如何不通過@Controller編碼方式批量暴露內(nèi)網(wǎng)接口 - 簡書 (jianshu.com)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容