我有一个文件,其中包含必须在REST API上公开的URL。所有URL代表不同的资源,必须分别进行记录。该文件包含数百个网址,例如:
/p1
/p1/p2
/p1/p2/p3
/t1
/t1/t2
/t1/t2/t3
我想自动/以编程方式生成REST端点,以便能够调用:
GET on https://host/p1/
GET on https://host/p1/p2
GET on https://host/p1/p3
... and so on ...
处理请求的逻辑在每个路径上都是相似的,因此/ p1,/ p1 / p2,/ t1等可以由接收整个路径的单个函数来处理。为了处理请求,我有一个像这样的函数:
function handleRequest(url) {
// this function should be called for every GET request on any of those endpoints
// perform the business logic here
}
数据存储在树型数据结构中,因此在树中获取路径并返回该路径指向的节点下的数据是有意义的。但是,所有这些路径都是单独的资源。
我正在用Java编写代码,但是该语言目前并不重要。我会在Spring中生成这样的REST端点:
@RequestMapping(
path = "/t1",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json"
)
public ResponseEntity getT1() {
// handle request here
}
但是,由于必须通过API公开的资源数量非常多,因此在处理请求的逻辑相同的情况下,要编写数百或数千次上述函数非常困难。
我发现的一种解决方案是使用PathPatterns。这将需要具有URL的RequestMapping,例如“ / commonPath / **”。该解决方案的缺点是,由于存在单个入口点,因此我无法通过Swagger自动为API生成文档。
如何自动生成所有这些端点?
修改
文档基本上可以归结为具有一种自动告知用户可以调用的可用端点的方法。我不想手动编写/维护此文件,因为URL的数量非常多。使用Swagger,这非常简单。只需注释端点,就会为API客户端自动生成一个用户界面。
答案 0 :(得分:2)
我希望了解有关您的方案的更多详细信息,但是这是基于当前时间的给定信息的可能选项。
解决方案1:如果输入文件是静态文件,并且需要基于静态输入文件公开API,则此解决方案可能会很好地工作。只需编写一个程序即可基于该文件生成控制器类。
@RestController
public class MyMultiPathApiController {
@AutoWired
private RestApiHandlerService restApiHandlerService
@RequestMapping(
value = { "/p1", "/p1/p2", "/p1/p2/p3" },
method = GET)
public ResponseEntity<?> myMultiRestApi() {
return restApiHandlerService.handleApiLogic();
}
}
或者,请求映射也接受正则表达式。根据您提供的示例,您还可以提出一个正则表达式。
解决方案2(推荐的解决方案):
如果期望文件发生更改,并且希望动态公开API,则需要调整spring框架的RequestMappingHandlerMapping
类。扩展类RequestMappingHandlerMapping
并编写要编写的逻辑。您可以在应用程序启动时读取文件并将其缓存。您可以参考有关编写自定义RequestMappingHandlerMapping类here的一些示例。
我相信这些信息会有所帮助。如果您有任何后续问题,请告诉我。
答案 1 :(得分:0)
答案 2 :(得分:0)
感谢@Lokesh的回答。不幸的是,我无法完全理解RequestMappingHandlerMapping并无法构建完整的工作示例。答案类似于评论中的@Andrew S建议。
我构建了一个类,该类通过2个模板文件为所有路径生成代码,并将生成的java类保存在项目中。
我创建了一个类模板,该模板只是一个文本文件:
package a.b.c.d;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@Api (description = "Resource controller")
@RestController
@RequestMapping (path = "REQUEST_MAPPING_BASE_PATH")
public class Resources {
REQUEST_METHODS
}
然后我创建了一个方法模板,它也是一个文本文件:
@RequestMapping (
path = "REQUEST_MAPPING_PATH",
method = RequestMethod.REQUEST_HTTP_METHOD,
produces = MediaType.ALL_VALUE,
consumes = MediaType.ALL_VALUE,
headers = "Accept=application/json"
)
@ApiOperation (
value = "REQUEST_METHOD_DESCRIPTION",
response = ResponseEntity.class
)
public ResponseEntity REQUEST_METHOD_NAME (HttpServletRequest request) {
String path = request.getRequestURI();
return new GraphResourceHandler(path).execute();
}
GraphResourceHandler是为每个路径执行逻辑的类:
public class GraphResourceHandler {
private final String path;
public GraphResourceHandler(String path) {
this.path = path;
}
public ResponseEntity execute() {
//TODO implement logic
}
public String getPath() {
return path;
}
}
使用所有资源生成.java文件非常简单。我只会提供伪代码:
class GenerateResources {
public static void main(String[] args) {
readFileContainingPaths();
// holds method definitions as string
String methods = "";
foreach(resource in paths) {
//simply perform string replacement for variables in method template
// eg. replace REQUEST_METHOD_NAME with a custom name
methods += generateMethodFromResource(resource);
}
// replace REQUEST_METHODS variable to the contents of 'methods' variable
addResourceMethodsToClassTemplate()
writeResultingStringClassToJavaFile()
}
}
编译项目,然后运行。当然,每当您向文件添加新值时,都必须重新运行此类,但我认为可以进一步自动化。优点是我不必编写数千行代码,而且Swagger知道如何自动生成资源。