Pont是一个很好的前后端一体化工具,能够帮助自动生成API代码。
今天正在整Pont的时候,发现个别API参数在pont里无法导出,经过反复的对比发现有一个神坑,很容易踩中。例如下面这个PostMapping,很习惯的,给它添加了@ApiParam参数。
@PostMapping("testPost")
@ApiOperation(value = "后台管理员登录aaaa")
public String testPost(
@ApiParam("登录ID--》") String loginId,
String userPsw, String validateCode, String sid,@ApiIgnore RequestSite adminSite, HttpServletResponse response){
return loginId + " " + userPsw;
}
然后在knife4j里也工作的很好。
但是实际生成的Pont接口api.d.ts如下:
declare namespace API {
/**
* Common Admin Controller
*/
export namespace commonAdmin {
/**
* 后台管理员登录
* /admin/common/doLogin
*/
export namespace doLogin {
export class Params {}
export type Response = string
export const init: Response
export function request(params: Params, body: string, options?: any): Promise<Response>
}
啥,写过@ApiParam注解后,参数就导不出来啦?难道是鼓励我们不要写注释😅
看了下swagger的docs接口,生成的API json如下:
"/admin/common/testPost": {
"post": {
"tags": ["common-admin-controller"],
"summary": "后台管理员登录aaaa",
"operationId": "testPostUsingPOST",
"parameters": [{
"name": "adminToken",
"in": "header",
"description": "adminToken 后台Token",
"required": false,
"type": "string"
}, {
"in": "body",
"name": "loginId",
"description": "登录ID--》",
"required": false,
"schema": {
"type": "string"
}
}, {
"name": "sid",
"in": "query",
"description": "sid",
"required": false,
"type": "string"
}, {
"name": "userPsw",
"in": "query",
"description": "userPsw",
"required": false,
"type": "string"
}, {
"name": "validateCode",
"in": "query",
"description": "validateCode",
"required": false,
"type": "string"
}],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Forbidden"
},
"404": {
"description": "Page Not Found"
},
"500": {
"description": "Server Internal Error"
}
},
"deprecated": false
}
},
再写多几个反复编译测试后。发现一个规律:
pont的param这里只处理query类型的参数
所以,对于Get请求,@ApiParam注解后,类型还是query,这里是没有问题;但是对于Post请求,@ApiParam注解后,类型会从query变成form,于是乎就无法导出到Param了。
而body部分,实际也还是会处理,仔细看有body导出的,接口的request里会多一个body参数,用来执行此部分的请求,例如上面的:
export function request(params: Params, body: string, options?: any): Promise<Response>
就是说,还是会执行,但是放到body请求了。但是mvc代码里没有@RequestBody来接收,实际执行还是有问题的
解决办法:
1)在@ApiParam上再加多一个@RequestParam注解
2)如果嫌注解太多难看,也可以抽离到ApiImplicitParam统一书写,ApiImplicitParam自带paramType可以指定为query
3)仔细核对swagger的类型,以前没有太留意这个,在生成API代码这里踩坑了
--------------- [ 2024/03/25 更新] ------------------
前面修改后,还是会有问题。当使用multipart/form-data上传文件时,发现数据始终传不上去。经过检查,发现axios最终上传的data为空。而FormData参数放到了param里面,因此,需要调整一下。当API里consumes有multipart/form-data时,把form参数放到data,否则才是放params。
调整后的 pontTemplates.ts如下:
import { Interface, BaseClass, Property, CodeGenerator, Surrounding } from 'pont-engine'
export default class MyGenerator extends CodeGenerator {
getInterfaceContentInDeclaration(inter: Interface) {
const requestParams = inter.getRequestParams()
const paramsCode = inter.getParamsCode('Params')
return `
export ${paramsCode}
export type Response = ${inter.responseType}
export const init: Response;
export function request(${requestParams}): Promise<Response>;
`
}
getBaseClassInDeclaration(base: BaseClass) {
const originProps = base.properties
base.properties = base.properties.map((prop) => {
return new Property({
...prop,
required: false
})
})
const result = super.getBaseClassInDeclaration(base)
base.properties = originProps
return result
}
getInterfaceContent(inter: Interface) {
//const method = inter.method.toUpperCase()
const axiosParamKey = 'GET' == inter.method.toUpperCase() ? 'params' : 'data' // 如果是GET,放param,如果是POST/PUT,放data
const requestParams = inter.getRequestParams(this.surrounding)
const paramsCode = inter.getParamsCode('Params', this.surrounding).replace(/([A-Za-z0-9_]+):/g, '$1?:') //适配严格模式
const formAsData = inter.consumes?.includes('multipart/form-data') ? 'form' : 'params' // 如果是FormData,则data是form
return `
/**
* @desc ${inter.description}
*/
import * as defs from '../../baseClass';
import { request as service } from '../../../utils/service';
export ${paramsCode}
export const init = ${inter.response.getInitialValue()};
export function request(${requestParams}, jsonPost: boolean = false) {
return service({url:"${inter.path}", ...${inter.getRequestContent()}, ${axiosParamKey}: ${formAsData}, jsonPost})
}
`
}
}
这样就可以正常上传了