如何在Mustache模板中基于operationId实现ServiceImpl条件逻辑生成?
问题与解决方案
问题描述
通过继承AbstractJavaCodegen自定义代码生成器,使用Mustache模板生成Java应用的ServiceImpl.java文件时,遇到以下问题:
- 需求是根据Swagger规范中定义的
operationId(可选值:createOrder、patchOrder、deleteOrder、retrieveOrder、listOrder),让每个方法体内仅包含对应操作的逻辑 - 当前生成的所有方法会包含所有操作的逻辑,不符合预期
- 此前尝试在
preprocessOpenAPI中预处理数据,但因将属性放到全局additionalProperties中,导致所有方法都能读取到所有操作标记,未达到效果
解决方案
方案1:直接通过Mustache模板条件逻辑实现(无需修改Codegen代码)
利用OpenAPI Generator内置的Mustache条件辅助函数{{#is}},直接在模板中判断当前方法的operationId,仅渲染对应逻辑块。
修改后的Mustache模板:
{{#operations}} public class {{classname}}ServiceImpl implements {{classname}}Service { {{#operation}} @Override public ResponseEntity<{{>returnTypes}}> {{operationId}}({{#allParams}}{{>optionalDataType}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) { {{#is operationId "createOrder"}} // Logic for createOrder operation {{/is}} {{#is operationId "patchOrder"}} // Logic for patchOrder operation {{/is}} {{#is operationId "deleteOrder"}} // Logic for deleteOrder operation {{/is}} {{#is operationId "retrieveOrder"}} // Logic for retrieveOrder operation {{/is}} {{#is operationId "listOrder"}} // Logic for listOrder operation {{/is}} return new ResponseEntity<>(HttpStatus.OK); } {{/operation}} }
方案2:自定义Codegen预处理(修正原代码逻辑)
原代码的问题是将操作标记放到了全局的additionalProperties中,导致所有方法都能读取到所有标记。正确的做法是将标记添加到单个Operation对象的扩展属性中,这样每个方法只会读取到自身对应的标记。
修正后的preprocessOpenAPI代码:
@Override public void preprocessOpenAPI(OpenAPI openAPI) { super.preprocessOpenAPI(openAPI); // 保留原有枚举配置、标题、端口处理逻辑 if (!interfaceOnly && SPRING_BOOT.equals(library) && containsEnums()) { supportingFiles.add(new SupportingFile("converter.mustache", (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "EnumConverterConfiguration.java")); } if (!additionalProperties.containsKey(TITLE)) { String title = openAPI.getInfo().getTitle(); if (title != null) { title = title.trim().replace(" ", "-"); if (title.toUpperCase(Locale.ROOT).endsWith("API")) { title = title.substring(0, title.length() - 3); } this.title = camelize(sanitizeName(title), LOWERCASE_FIRST_LETTER); } additionalProperties.put(TITLE, this.title); } if (!additionalProperties.containsKey(SERVER_PORT)) { final URL url = URLPathUtils.getServerURL(openAPI, serverVariableOverrides()); additionalProperties.put(SERVER_PORT, URLPathUtils.getPort(url, 8080)); } // 核心修正:将操作标记绑定到单个Operation的扩展属性 if (openAPI.getPaths() != null) { for (final Map.Entry<String, PathItem> entry : openAPI.getPaths().entrySet()) { final PathItem path = entry.getValue(); if (path.readOperations() != null) { for (final Operation operation : path.readOperations()) { String operationId = operation.getOperationId(); // 先清除可能存在的旧扩展属性 operation.getExtensions().remove("x-create-operation"); operation.getExtensions().remove("x-patch-operation"); operation.getExtensions().remove("x-delete-operation"); operation.getExtensions().remove("x-retrieve-operation"); operation.getExtensions().remove("x-list-operation"); // 根据operationId添加对应扩展属性 if (operationId.startsWith("create")) { operation.addExtension("x-create-operation", Boolean.TRUE); } else if (operationId.startsWith("patch")) { operation.addExtension("x-patch-operation", Boolean.TRUE); } else if (operationId.startsWith("delete")) { operation.addExtension("x-delete-operation", Boolean.TRUE); } else if (operationId.startsWith("retrieve")) { operation.addExtension("x-retrieve-operation", Boolean.TRUE); } else if (operationId.startsWith("list")) { operation.addExtension("x-list-operation", Boolean.TRUE); } // 保留原有标签处理逻辑 if (operation.getTags() != null) { final List<Map<String, String>> tags = new ArrayList<>(); for (final String tag : operation.getTags()) { final Map<String, String> value = new HashMap<>(); value.put("tag", tag); tags.add(value); } if (!operation.getTags().isEmpty()) { operation.setTags(Collections.singletonList(operation.getTags().get(0))); } operation.addExtension("x-tags", tags); } } } } } }
对应修改Mustache模板中的标记引用(添加x-前缀,匹配扩展属性):
{{#operations}} public class {{classname}}ServiceImpl implements {{classname}}Service { {{#operation}} @Override public ResponseEntity<{{>returnTypes}}> {{operationId}}({{#allParams}}{{>optionalDataType}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) { {{#x-create-operation}} // Logic for createOrder operation {{/x-create-operation}} {{#x-patch-operation}} // Logic for patchOrder operation {{/x-patch-operation}} {{#x-delete-operation}} // Logic for deleteOrder operation {{/x-delete-operation}} {{#x-retrieve-operation}} // Logic for retrieveOrder operation {{/x-retrieve-operation}} {{#x-list-operation}} // Logic for listOrder operation {{/x-list-operation}} return new ResponseEntity<>(HttpStatus.OK); } {{/operation}} }
总结
- 方案1更轻量化,无需修改Codegen代码,直接通过模板条件判断即可实现需求
- 方案2适合需要对Operation做更复杂自定义处理的场景,确保每个方法仅能访问自身对应的操作标记
内容的提问来源于stack exchange,提问作者Ankit
相关产品推荐
相关产品推荐

