该文章为原创(转载请注明出处):如何不通过@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)