【发布时间】:2022-10-24 06:32:12
【问题描述】:
我们有一个用 spring boot 和 openapi 代码生成器构建的 rest api。假设我们的 api 规范中有一条路径 /user:
...
"paths": {
"/user": {
"get": {
"parameters": [{
"name": "id",
"in": "query",
"required": false,
"type": "integer",
"format": "int64"
}],
"responses": { ... }
},
}
// more paths
}
...
对该路径的调用可能是:/user?id=1234。
代码生成器创建一个接口 MyControllerApi:
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "some date")
@Api(value="user")
public interface MyControllerApi {
@ApiOperation(value="", nickname="userGet", response = User.class, /* ... */)
@ApiResponse(/* ... */)
@GetMapping(value="/user", produces = { "application/json" })
ResponseEntity<User> userGet(@ApiParam(value = "id") @RequestParam(value = "id", required = false) Long id);
}
然后控制器看起来像这样:
@RestController
public class MyController implements MyControllerApi
{
@Autowired
UserService service;
@Override
public ResponseEntity<User> userGet(@RequestParam(value = "id") Long id) {
return service.get(id);
}
}
如果请求/user?id=<value>,spring boot会自动检查传入的参数值<value>的类型是否与需要的类型匹配。如果不是 BAD_REQUEST 则返回:
{
"timestamp": "2022-10-19T17:20:48.393+0000",
"status": 400,
"error": "Bad Request",
"path": "/user"
}
我们现在处于想要将null 传递给userGet 的每个参数的情况下,这会导致类型不匹配。更清楚一点:如果请求/user?id=abcd,则应调用userGet,并将id设置为null,以便我们可以返回一些默认用户。有没有办法做到这一点?
当然还有更多的路径和参数,这种行为应该适用于Long 或Boolean 类型的每个查询参数。
这个例子可能没有多大意义,但它也只是一个例子。
提前致谢
在此期间我自己尝试了什么
1.设置spring-bootuseOptional选项...
... 到 pom.xml 中的 true(参见 here)。
这会影响控制器方法中的查询参数是Optional<?> 类型。在我上面的示例中,这将导致:
@Override
public ResponseEntity<User> userGet(Optional<Long> id) {
Long id_val = id.isPresent() ? id.get() : null;
return service.get(id_val);
}
但这也不起作用,spring boot 还会对参数类型不匹配创建BAD_REQUEST 响应。
2.使用请求拦截器
请求拦截器是一种中间件,可以通过实现HandlerInterceptor 创建,使您能够在将请求传递给控制器之前对其进行处理。
我的拦截器如下所示:
public class CustomRequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
HandlerMethod method = (HandlerMethod) handler;
MethodParameter[] handlerParams = method.getMethodParameters();
while (request.getParameterNames().hasMoreElements())
{
// the param name of the query
String paramName = request.getParameterNames().nextElement();
the param value
String paramValue = request.getParameter(paramName);
MethodParameter methodParam = null;
for (MethodParameter mp : handlerParams)
{
// We can access annotations here ...
Annotation anno = mp.getParameterAnnotation(RequestParam.class);
if(anno != null)
{
RequestParam rp = (RequestParam) anno;
// ... and am able to get the param name in the controller
// Check if we found the param
String requestParamName = rp.value();
if (requestParamName.toLowerCase().equals(paramName.toLowerCase()))
{
// and now?
break;
}
}
}
if (methodParam != null)
{
Type type = methodParam.getGenericParameterType();
}
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
到目前为止一切都很好,但是 RequestParam-Object 不包含有关参数类型的任何信息,也不包含方法参数列表中该参数的索引。并且 MethodParameter 不包含参数的名称(因为我认为它是一个已编译的类)。
我想知道的是spring boot本身如何将查询参数映射到控制器方法中的参数.....?
【问题讨论】:
标签: java spring-boot openapi-generator