Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.

Commit 94426aa

Browse files
authored
Merge pull request #808 from shalousun/master
fix: Fix the issue where exported documents display incorrectly in tools like Postman when the content-type is form-data and urlencoded.
2 parents 934bf5d + 5ed8f3c commit 94426aa

File tree

14 files changed

+91
-632
lines changed

14 files changed

+91
-632
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ need to inject annotations into the code like `Swagger`.
3434
package).
3535
- Support for generating multiple formats of documents: `Markdown`,`HTML5`,`Word`,`Asciidoctor`,`Postman Collection 2.0+`,`OpenAPI 3.0`.
3636
- Support the generation of `Jmeter` performance testing scripts
37+
- Support for generating `Javadoc` documentation for `Java` classes.
3738
- Support for exporting error codes and data dictionary codes to API documentation.
3839
- The debug html5 page fully supports file upload and download testing.
3940
- Support `Apache Dubbo RPC`.

README_CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ $\color{red}{你给我的star,胜过所有读过的诗—smart-doc}$
3737
- 支持生成`Jmeter`性能测试脚本。
3838
- 支持`Maven``Gradle`插件式轻松集成。
3939
- 支持`Apache Dubbo RPC`接口文档生成。
40+
- 支持`Java``Javadoc`文档生成。
4041
- 支持基于`Git`管理项目的变更增量文档生成。
4142
- `debug`接口调试`html5`页面完全支持文件上传,下载(`@download tag`标记下载方法)测试。
4243

src/main/java/com/ly/doc/builder/PostmanJsonBuilder.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,14 @@ private static BodyBean buildBodyBean(ApiMethodDoc apiMethodDoc) {
193193
BodyBean bodyBean;
194194
if (apiMethodDoc.getContentType().contains(MediaType.APPLICATION_JSON)) {
195195
bodyBean = new BodyBean(Boolean.FALSE);// Json request
196-
bodyBean.setMode(DocGlobalConstants.POSTMAN_MODE_RAW);
196+
bodyBean.setMode(convertContentTypeToPostmanType(apiMethodDoc.getContentType()));
197197
if (apiMethodDoc.getRequestExample() != null) {
198198
bodyBean.setRaw(apiMethodDoc.getRequestExample().getJsonBody());
199199
}
200200
} else {
201201
if (apiMethodDoc.getType().equals(Methods.POST.getValue())) {
202202
bodyBean = new BodyBean(Boolean.TRUE); // Formdata
203-
bodyBean.setMode(DocGlobalConstants.POSTMAN_MODE_FORMDATA);
203+
bodyBean.setMode(convertContentTypeToPostmanType(apiMethodDoc.getContentType()));
204204
if (CollectionUtil.isNotEmpty(apiMethodDoc.getRequestExample().getFormDataList())) {
205205
bodyBean.setFormdata(apiMethodDoc.getRequestExample().getFormDataList());
206206
} else {
@@ -268,4 +268,29 @@ private static void postManCreate(ApiConfig config, ProjectDocConfigBuilder conf
268268
FileUtil.nioWriteFile(data, filePath);
269269
}
270270

271+
/**
272+
* Converts the request Content-Type to its corresponding Postman request type.
273+
*
274+
* Postman is a popular API testing tool that supports various request types. This method aims to map common
275+
* Content-Types to the formats recognized by Postman, facilitating more accurate HTTP request simulations.
276+
*
277+
* @param contentType The MIME type of the data in the request, indicating how it should be processed.
278+
* @return The Postman request type corresponding to the given Content-Type.
279+
*
280+
* Note: This mapping covers typical use cases and Postman's supported range; it may not include all possible Content-Types.
281+
*/
282+
private static String convertContentTypeToPostmanType(String contentType) {
283+
switch (contentType) {
284+
case "application/json":
285+
case "application/xml":
286+
return "raw";
287+
case "multipart/form-data":
288+
return "formdata";
289+
case "application/x-www-form-urlencoded":
290+
return "x-www-form-urlencoded";
291+
default:
292+
return "none";
293+
}
294+
}
295+
271296
}

src/main/java/com/ly/doc/builder/openapi/AbstractOpenApiBuilder.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.ly.doc.builder.ProjectDocConfigBuilder;
2929
import com.ly.doc.constants.ComponentTypeEnum;
3030
import com.ly.doc.constants.MediaType;
31+
import com.ly.doc.constants.Methods;
3132
import com.ly.doc.constants.ParamTypeConstants;
3233
import com.ly.doc.factory.BuildTemplateFactory;
3334
import com.ly.doc.model.*;
@@ -221,7 +222,14 @@ public Map<String, Object> buildBodySchema(ApiMethodDoc apiMethodDoc, boolean is
221222
// for request
222223
String requestRef;
223224
String randomName = ComponentTypeEnum.getRandomName(ApiConfig.getInstance().getComponentType(), apiMethodDoc);
224-
if (apiMethodDoc.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
225+
if (Methods.POST.getValue().equals(apiMethodDoc.getType()) &&
226+
(apiMethodDoc.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE) || apiMethodDoc.getContentType().equals(MediaType.MULTIPART_FORM_DATA_VALUE))) {
227+
schema.put("type", ParamTypeConstants.PARAM_TYPE_OBJECT);
228+
Map<String, Object> propertiesAndRequirments = buildProperties(apiMethodDoc.getRequestParams(), new HashMap<>(), Boolean.FALSE);
229+
schema.put("properties", propertiesAndRequirments.get("properties"));
230+
schema.put("required", propertiesAndRequirments.get("required"));
231+
return schema;
232+
} else if (apiMethodDoc.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
225233
requestRef = componentKey + OpenApiSchemaUtil.getClassNameFromParams(apiMethodDoc.getQueryParams());
226234
} else {
227235
requestRef = componentKey + OpenApiSchemaUtil.getClassNameFromParams(apiMethodDoc.getRequestParams());
@@ -409,7 +417,7 @@ private Map<String, Object> buildPropertiesData(ApiParam apiParam, Map<String, O
409417
// array object file map
410418
propertiesData.put("description", apiParam.getDesc());
411419
if (StringUtil.isNotEmpty(apiParam.getValue())) {
412-
propertiesData.put("example", StringUtil.removeDoubleQuotes(apiParam.getValue()));
420+
propertiesData.put("example", apiParam.getValue());
413421
}
414422

415423
if (!"object".equals(openApiType)) {
@@ -467,8 +475,8 @@ private Map<String, Object> buildPropertiesData(ApiParam apiParam, Map<String, O
467475
propertiesData.put("description", apiParam.getDesc() + "(object)");
468476
}
469477
}
470-
if (apiParam.getExtensions() != null && !apiParam.getExtensions().isEmpty()){
471-
apiParam.getExtensions().entrySet().forEach( e-> propertiesData.put("x-"+e.getKey(), e.getValue()));
478+
if (apiParam.getExtensions() != null && !apiParam.getExtensions().isEmpty()) {
479+
apiParam.getExtensions().entrySet().forEach(e -> propertiesData.put("x-" + e.getKey(), e.getValue()));
472480
}
473481

474482
return propertiesData;

src/main/java/com/ly/doc/builder/openapi/OpenApiBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public static void buildOpenApi(ApiConfig config, JavaProjectBuilder projectBuil
8282
public void openApiCreate(ApiConfig config, List<ApiDoc> apiDocList) {
8383
this.setComponentKey(getModuleName());
8484
Map<String, Object> json = new HashMap<>(8);
85-
json.put("openapi", "3.0.3");
85+
json.put("openapi", "3.1.0");
8686
json.put("info", buildInfo(config));
8787
json.put("servers", buildServers(config));
8888
Set<OpenApiTag> tags = new HashSet<>();
@@ -104,7 +104,7 @@ public void openApiCreate(ApiConfig config, List<ApiDoc> apiDocList) {
104104
private static Map<String, Object> buildInfo(ApiConfig apiConfig) {
105105
Map<String, Object> infoMap = new HashMap<>(8);
106106
infoMap.put("title", apiConfig.getProjectName() == null ? "Project Name is Null." : apiConfig.getProjectName());
107-
infoMap.put("version", "1.0.0");
107+
infoMap.put("version", "v1.0.0");
108108
return infoMap;
109109
}
110110

@@ -243,7 +243,7 @@ Map<String, Object> getStringParams(ApiParam apiParam, boolean hasItems) {
243243
parameters = new HashMap<>(20);
244244
//add mock value for parameters
245245
if (StringUtils.isNotEmpty(apiParam.getValue())) {
246-
parameters.put("example", StringUtil.removeDoubleQuotes(apiParam.getValue()));
246+
parameters.put("example", apiParam.getValue());
247247
}
248248
if (!hasItems) {
249249
parameters.put("name", apiParam.getField());

src/main/java/com/ly/doc/model/DocJavaMethod.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public class DocJavaMethod {
3939

4040
private Map<String, JavaType> actualTypesMap;
4141

42+
private String methodType;
43+
4244
private boolean download;
4345

4446
/**
@@ -189,4 +191,13 @@ public DocJavaMethod setDetail(String detail) {
189191
this.detail = detail;
190192
return this;
191193
}
194+
195+
public String getMethodType() {
196+
return methodType;
197+
}
198+
199+
public DocJavaMethod setMethodType(String methodType) {
200+
this.methodType = methodType;
201+
return this;
202+
}
192203
}

src/main/java/com/ly/doc/template/IRestDocTemplate.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ default List<ApiDoc> processApiData(ProjectDocConfigBuilder projectBuilder, Fram
104104
if (apiConfig.isSortByTitle()) {
105105
// sort by title
106106
Collections.sort(apiDocList);
107+
return apiDocList;
107108
} else if (setCustomOrder) {
108109
ATOMIC_INTEGER.getAndAdd(maxOrder);
109110
// while set custom oder
@@ -114,6 +115,8 @@ default List<ApiDoc> processApiData(ProjectDocConfigBuilder projectBuilder, Fram
114115
}
115116
});
116117
return tempList.stream().sorted(Comparator.comparing(ApiDoc::getOrder)).collect(Collectors.toList());
118+
} else {
119+
apiDocList.stream().peek(p -> p.setOrder(ATOMIC_INTEGER.getAndAdd(1))).collect(Collectors.toList());
117120
}
118121
return apiDocList;
119122
}
@@ -398,6 +401,7 @@ default List<ApiMethodDoc> buildEntryPointMethod(
398401
int methodOrder = 0;
399402
for (DocJavaMethod docJavaMethod : docJavaMethods) {
400403
JavaMethod method = docJavaMethod.getJavaMethod();
404+
401405
// handle request mapping
402406
RequestMapping requestMapping = baseMappingHandler.handle(projectBuilder, baseUrl,
403407
method, frameworkAnnotations,
@@ -408,6 +412,7 @@ default List<ApiMethodDoc> buildEntryPointMethod(
408412
if (Objects.isNull(requestMapping.getShortUrl())) {
409413
continue;
410414
}
415+
docJavaMethod.setMethodType(requestMapping.getMethodType());
411416
ApiMethodDoc apiMethodDoc = new ApiMethodDoc();
412417
// fill contentType by annotation's consumes parameter
413418
String mediaType = requestMapping.getMediaType();
@@ -604,7 +609,9 @@ default ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, Proje
604609
boolean isRequestBody = false;
605610
boolean required = false;
606611
boolean isRequestParam = false;
607-
if (annotations.isEmpty()) {
612+
if (annotations.isEmpty()
613+
&& (Methods.GET.getValue().equals(docJavaMethod.getMethodType())
614+
|| Methods.DELETE.getValue().equals(docJavaMethod.getMethodType()))) {
608615
isRequestParam = true;
609616
}
610617
for (JavaAnnotation annotation : annotations) {
@@ -662,7 +669,7 @@ default ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, Proje
662669
.setField(paramName)
663670
.setType(ParamTypeConstants.PARAM_TYPE_FILE)
664671
.setId(paramList.size() + 1)
665-
.setQueryParam(true)
672+
.setQueryParam(false)
666673
.setRequired(required)
667674
.setVersion(DocGlobalConstants.DEFAULT_VERSION)
668675
.setDesc(comment.toString());
@@ -676,7 +683,10 @@ default ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, Proje
676683
continue;
677684
}
678685

679-
boolean queryParam = !isRequestBody && !isPathVariable;
686+
boolean queryParam = Methods.GET.getValue().equals(docJavaMethod.getMethodType()) &&
687+
Methods.DELETE.getValue().equals(docJavaMethod.getMethodType()) &&
688+
!isRequestBody &&
689+
!isPathVariable;
680690
if (JavaClassValidateUtil.isCollection(fullyQualifiedName) || JavaClassValidateUtil.isArray(fullyQualifiedName)) {
681691
String[] gicNameArr = DocClassUtil.getSimpleGicName(typeName);
682692
String gicName = gicNameArr[0];
@@ -730,7 +740,7 @@ default ApiMethodReqParam requestParams(final DocJavaMethod docJavaMethod, Proje
730740
.setField(paramName)
731741
.setType(ParamTypeConstants.PARAM_TYPE_FILE)
732742
.setId(paramList.size() + 1)
733-
.setQueryParam(true)
743+
.setQueryParam(false)
734744
.setRequired(required)
735745
.setVersion(DocGlobalConstants.DEFAULT_VERSION)
736746
.setHasItems(true)
@@ -828,7 +838,7 @@ else if (javaClass.isEnum()) {
828838
String.valueOf(required), Boolean.FALSE, new HashMap<>(16), builder, groupClasses, 0, Boolean.FALSE, null));
829839
}
830840
}
831-
return ApiParamTreeUtil.buildMethodReqParam(paramList, queryReqParamMap, pathReqParamMap, requestBodyCounter);
841+
return ApiParamTreeUtil.buildMethodReqParam(paramList, queryReqParamMap, pathReqParamMap, docJavaMethod.getMethodType());
832842
}
833843

834844
default ApiRequestExample buildReqJson(DocJavaMethod javaMethod, ApiMethodDoc apiMethodDoc,

src/main/java/com/ly/doc/template/JavadocDocBuildTemplate.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public List<JavadocApiDoc> renderApi(ProjectDocConfigBuilder projectBuilder, Col
7575
if (apiConfig.isSortByTitle()) {
7676
// sort by title
7777
Collections.sort(apiDocList);
78+
return apiDocList;
7879
} else if (setCustomOrder) {
7980
ATOMIC_INTEGER.getAndAdd(maxOrder);
8081
// while set custom oder
@@ -85,6 +86,8 @@ public List<JavadocApiDoc> renderApi(ProjectDocConfigBuilder projectBuilder, Col
8586
}
8687
});
8788
return tempList.stream().sorted(Comparator.comparing(JavadocApiDoc::getOrder)).collect(Collectors.toList());
89+
} else {
90+
apiDocList.stream().peek(p -> p.setOrder(ATOMIC_INTEGER.getAndAdd(1))).collect(Collectors.toList());
8891
}
8992
return apiDocList;
9093
}

src/main/java/com/ly/doc/template/RpcDocBuildTemplate.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public List<RpcApiDoc> renderApi(ProjectDocConfigBuilder projectBuilder, Collect
7777
if (apiConfig.isSortByTitle()) {
7878
// sort by title
7979
Collections.sort(apiDocList);
80+
return apiDocList;
8081
} else if (setCustomOrder) {
8182
ATOMIC_INTEGER.getAndAdd(maxOrder);
8283
// while set custom oder
@@ -87,6 +88,8 @@ public List<RpcApiDoc> renderApi(ProjectDocConfigBuilder projectBuilder, Collect
8788
}
8889
});
8990
return tempList.stream().sorted(Comparator.comparing(RpcApiDoc::getOrder)).collect(Collectors.toList());
91+
} else {
92+
apiDocList.stream().peek(p -> p.setOrder(ATOMIC_INTEGER.getAndAdd(1))).collect(Collectors.toList());
9093
}
9194
return apiDocList;
9295
}

src/main/java/com/ly/doc/utils/ApiParamTreeUtil.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.stream.Collectors;
2828

2929
import com.ly.doc.constants.DocGlobalConstants;
30+
import com.ly.doc.constants.Methods;
3031
import com.ly.doc.model.ApiMethodReqParam;
3132
import com.ly.doc.model.ApiParam;
3233
import com.ly.doc.model.ApiReqParam;
@@ -87,16 +88,18 @@ private static List<ApiParam> getChild(int id, List<ApiParam> apiParamList, int
8788
}
8889

8990
/**
90-
* buildMethodReqParam handle configParam
91+
* Constructs method request parameters based on the parameter list, query parameter map, and path parameter map.
92+
* This method categorizes parameters into path, query, and body parameters, and treats all as query parameters
93+
* if the request method type is GET or DELETE.
9194
*
92-
* @param paramList unConfigParam
93-
* @param queryReqParamMap configQueryParam
94-
* @param pathReqParamMap configPathParam
95-
* @param requestBodyCounter hasRequestBody
96-
* @return ApiMethodReqParam
95+
* @param paramList List of parameters containing all unconfigured parameter information.
96+
* @param queryReqParamMap Mapping of configured query parameters.
97+
* @param pathReqParamMap Mapping of configured path parameters.
98+
* @param methodType The request method type, determining whether to treat all parameters as query parameters.
99+
* @return An instance of ApiMethodReqParam built with categorized parameter information.
97100
*/
98101
public static ApiMethodReqParam buildMethodReqParam(List<ApiParam> paramList, final Map<String, ApiReqParam> queryReqParamMap,
99-
final Map<String, ApiReqParam> pathReqParamMap, int requestBodyCounter) {
102+
final Map<String, ApiReqParam> pathReqParamMap,String methodType) {
100103
List<ApiParam> pathParams = new ArrayList<>();
101104
List<ApiParam> queryParams = new ArrayList<>();
102105
List<ApiParam> bodyParams = new ArrayList<>();
@@ -107,7 +110,7 @@ public static ApiMethodReqParam buildMethodReqParam(List<ApiParam> paramList, fi
107110
}
108111
param.setId(pathParams.size() + 1);
109112
pathParams.add(param);
110-
} else if (param.isQueryParam() || requestBodyCounter < 1) {
113+
} else if (param.isQueryParam() || Methods.GET.getValue().equals(methodType)|| Methods.DELETE.getValue().equals(methodType)) {
111114
if (queryReqParamMap.containsKey(param.getField())) {
112115
param.setConfigParam(true).setValue(queryReqParamMap.get(param.getField()).getValue());
113116
}

0 commit comments

Comments
 (0)