restswagger-uiendpointspring-boot-actuator

Spring boot actuator mapping: add description of endpoints | Spring Boot 3.3.0


I'm using spring doc open api in my spring boot project. In my rest controller I have 2 endpoints that are identical and only differ in params and the content of requestbody. Example:

@PostMapping(value = "/v1/types", params = "type=typeA) 
...
@PostMapping(value = "/v1/types", params = "type=typeB)

Due OpenAPI Spec the endpoint should be unique and params are not considered as part of path. So swagger ui shows only one endpoint, I guess the first one in alphabetical order.

In my case my frontend team needs to know which endpoints to use in my api. So, I find the alternative from actuator the /mappings endpoint, that shows every endpoint in my project, extactly what I need.

My problem is that I need to add some description for each endpoint to describe which objects are required and what exactly the endpoint does.

Is there any way to add some kind of description under the endpoints listed in /actuator/mappings?

FYI: Spring Boot 3.3.0

Itried a lot with Swagger API, like adding several information to the endpoints, like with @Operation, @APIResponses or @Parameter annotations, but it doesn't help to show the same endpoint with different params.

I looked after the official spring boot actuator documentation, but there is no information about it.


Solution

  • You can make use of spring boot actuator like below,

    Add dependency in your pom.xml:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    Create the Custom Endpoint

    @Component
    @Endpoint(id = "custommappings")
    public class CustomMappingsEndpoint {
    
        private final ApplicationContext applicationContext;
    
        @Autowired
        public CustomMappingsEndpoint(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
    
        @ReadOperation
        public Map<String, Object> customMappings() {
            Map<String, Object> mappings = new HashMap<>();
            Map<String, RequestMappingHandlerMapping> allRequestMappings = applicationContext
                    .getBeansOfType(RequestMappingHandlerMapping.class);
            for (RequestMappingHandlerMapping handlerMapping : allRequestMappings.values()) {
                Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
    
                for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
                    RequestMappingInfo requestMappingInfo = entry.getKey();
                    HandlerMethod handlerMethod = entry.getValue();
    
                    // Extract path from PathPatternsRequestCondition
                    PathPatternsRequestCondition pathPatternsCondition = requestMappingInfo.getPathPatternsCondition();
                    Set<String> patterns = pathPatternsCondition != null ? pathPatternsCondition.getPatternValues() : null;
    
                    if (patterns != null && !patterns.isEmpty()) {
                        if (isExcludedPath(patterns.iterator().next())) {
                            continue; // Skip this path
                        }
                    }
    
                    Map<String, Object> endpointInfo = new HashMap<>();
                    endpointInfo.put("path", patterns.iterator().next());
                    endpointInfo.put("methods",
                            requestMappingInfo.getMethodsCondition() != null
                                    ? requestMappingInfo.getMethodsCondition().getMethods()
                                    : "unknown");
                    endpointInfo.put("params",
                            requestMappingInfo.getParamsCondition() != null
                                    ? requestMappingInfo.getParamsCondition().getExpressions()
                                    : "unknown");
                    endpointInfo.put("description", handlerMethod.getMethod().getName());
    
                    mappings.put(handlerMethod.getMethod().getName(), endpointInfo);
                }
            }
    
            return mappings;
        }
    
        private boolean isExcludedPath(String path) {
            // Define paths to exclude
            return path.startsWith("/v3/api-docs") || path.startsWith("/error") || path.startsWith("/swagger-ui.html")
                    || path.startsWith("/v3/api-docs.yaml");
            // Add more paths as needed
        }
    

    Expose custom endpoint in your application.properties

    management.endpoints.web.exposure.include=custommappings
    

    Use below URL to get information about endpoint,

    http://server:port/context-path/actuator/custommappings
    

    The output:

    {"method1":{"path":"/v1/types","methods":["POST"],"description":"method1","params":[{"name":"type","value":"typeA","negated":false}]},"method2":{"path":"/v1/types","methods":["POST"],"description":"method2","params":[{"name":"type","value":"typeB","negated":false}]},"method3":{"path":"/v1/getTypes","methods":["GET"],"description":"method3","params":[]}}