nacos提供对配置文件内容的格式校验

523 阅读1分钟

在工作中遇到了一个需求,在后端提供一个接口来主动校验配置文件内容格式是否正确。
思路:debug nacos源码 学习nacos底层是怎么实现的。
nacos中有一个com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData

private List<PropertySource<?>> loadNacosData(String dataId, String group,
   String fileExtension) {
  String data = null;
  try {
   data = configService.getConfig(dataId, group, timeout);
   if (StringUtils.isEmpty(data)) {
    log.warn(
      "Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
      dataId, group);
    return Collections.emptyList();
   }
   if (log.isDebugEnabled()) {
    log.debug(String.format(
      "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
      group, data));
   }
   //解析过程
   return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
     fileExtension);
  }
  catch (NacosException e) {
   log.error("get data from Nacos error,dataId:{} ", dataId, e);
  }
  catch (Exception e) {
   log.error("parse data from Nacos error,dataId:{},data:{}", dataId, data, e);
  }
  return Collections.emptyList();
 }

com.alibaba.cloud.nacos.parser.NacosDataParserHandler#parseNacosData

public List<PropertySource<?>> parseNacosData(String configName, String configValue,
   String extension) throws IOException {
  if (StringUtils.isEmpty(configValue)) {
   return Collections.emptyList();
  }
  if (StringUtils.isEmpty(extension)) {
   extension = this.getFileExtension(configName);
  }
  for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) {
   if (!canLoadFileExtension(propertySourceLoader, extension)) {
    continue;
   }
   //封装配置内容信息
   NacosByteArrayResource nacosByteArrayResource;
   if (propertySourceLoader instanceof PropertiesPropertySourceLoader) {
    // PropertiesPropertySourceLoader internal is to use the ISO_8859_1,
    // the Chinese will be garbled, needs to transform into unicode.
    nacosByteArrayResource = new NacosByteArrayResource(
      NacosConfigUtils.selectiveConvertUnicode(configValue).getBytes(),
      configName);
   }
   else {
    nacosByteArrayResource = new NacosByteArrayResource(
      configValue.getBytes(), configName);
   }
   nacosByteArrayResource.setFilename(getFileName(configName, extension));
   //调用load方法进行解析
   List<PropertySource<?>> propertySourceList = propertySourceLoader
     .load(configName, nacosByteArrayResource);
   if (CollectionUtils.isEmpty(propertySourceList)) {
    return Collections.emptyList();
   }
   return propertySourceList.stream().filter(Objects::nonNull)
     .map(propertySource -> {
      if (propertySource instanceof EnumerablePropertySource) {
       String[] propertyNames = ((EnumerablePropertySource) propertySource)
         .getPropertyNames();
       if (propertyNames != null && propertyNames.length > 0) {
        Map<String, Object> map = new LinkedHashMap<>();
        Arrays.stream(propertyNames).forEach(name -> {
         map.put(name, propertySource.getProperty(name));
        });
        return new OriginTrackedMapPropertySource(
          propertySource.getName(), map, true);
       }
      }
      return propertySource;
     }).collect(Collectors.toList());
  }
  return Collections.emptyList();
 }

自己模仿nacos写了一个简单的解析方法

public class CheckConfigFormatImpl implements CheckConfigFormat {

    Map<String,PropertySourceLoader> propertySourceLoaderMap;

    @PostConstruct
    public void init() {
        propertySourceLoaderMap = new ConcurrentHashMap<>();
        YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();
        propertySourceLoaderMap.put("yml",yamlPropertySourceLoader);
        propertySourceLoaderMap.put("yaml",yamlPropertySourceLoader);

        NacosJsonPropertySourceLoader nacosJsonPropertySourceLoader = new NacosJsonPropertySourceLoader();
        propertySourceLoaderMap.put("json",nacosJsonPropertySourceLoader);

        PropertiesPropertySourceLoader propertySourceLoader = new PropertiesPropertySourceLoader();
        propertySourceLoaderMap.put("properties",propertySourceLoader);
    }

    @Override
    public Result<?> checkContentValid(ConfigRequest.PublishApplicationConfigRequestDTO request) {
        if(!propertySourceLoaderMap.containsKey(request.getType())){
            return Result.ok().setErrorMsg(String.format("暂时无法解析 %s 文件",request.getType()));
        }
        PropertySourceLoader propertySourceLoader = propertySourceLoaderMap.get(request.getType());
        NacosByteArrayResource resource = new NacosByteArrayResource(NacosConfigUtils.selectiveConvertUnicode(request.getContent()).getBytes());
        try {
            propertySourceLoader.load(request.getDataId(), resource);
        } catch (IOException e) {
            return Result.ok().setErrorMsg(e.getMessage());
        }
        return Result.ok();
    }

}

nacos底层的思想还是将各种配置文件转化成map形式的。