1

I have spring MVC controller having several APIs, I want to get the URI of API that has been hit from the front end

I have a filter extending oncePerRequestFilter which intercepts every api call, the filter has method which accepts HttpServletRequest,HttpServletResponse and filterChain. I can get the uri using request.getRequestURI() but in case of uri having template path variables lets say uri ="q/v1/ruleset/{rulesetId}" i am getting the final uri, for example API has been hit for rulesetid=23 , the uri i am getting from request.getRequestURI() is "q/v1/ruleset/23" but what i want is uri ="q/v1/ruleset/{rulesetId}", is there any method to get the expected result, I know , i can always get the desired result by some manipulation, but i want to make the things generic, please help

I have controller containing API

    @RequestMapping(value = "/ruleset/{rulesetid}", method = 
              RequestMethod.GET)
    public RuleSet getRuleSet(@PathVariable(value = "rulesetid") 
    final Long ruleSetId) {
         return storeMixin.getRuleSet(ruleSetId);
    }

Filter

    @Component
    @Order(1)
    public class CatalogFilter extends OncePerRequestFilter {


    @Override
    protected void doFilterInternal(HttpServletRequest request, 
    HttpServletResponse response,FilterChain filterChain) throws 
      IOException, ServletException {


         long startTime = System.currentTimeMillis();
         filterChain.doFilter(request, response);
         long elapsed = System.currentTimeMillis() - startTime;
         String name = request.getRequestURI();
         String requestType = request.getMethod();


         Integer httpCode = response.getStatus();

      }

    }
7
  • I doubt you can get it in the filter through an API. You may pre-scan all @RequestMapping annotations, keep it and then in the filter match the pattern. Commented Jul 28, 2019 at 13:26
  • Hello Aditya, Does your requirement is for statistics/metric or some other operation you want to perform Commented Jul 28, 2019 at 14:14
  • @PatelRomil, yes in the filter I am recording api execution time and api count , then pushing the statistics using StatsD, the core problem is that with every value of path variable {rulesetid}, it is making a new metrics, which is unnecessary, thats why I want the templated uri Commented Jul 28, 2019 at 17:53
  • @AdityaKrishnaNamdeo Maybe you have already got the answer but for count and time stats you can use spring boot admin if you wish to do let me know Commented Jul 30, 2019 at 9:59
  • @PatelRomil when I was using spring actuator to collect metrics, the actuator provides default http metrics to measure time and count, but in case of statsD I can't find this functionality, if this functionality exist as you are saying please let me know Commented Jul 31, 2019 at 7:04

5 Answers 5

3

You will not able to do that in a Filter since filter is executed before the handler.

You can implement a HandlerInterceptor and get the path mapping like below

public class LogInterceptor implements HandlerInterceptor {

     @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
          String path = (String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
          System.out.println("path : " + path);
        return true;
      }
}
3

Do this

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } finally {
            String patternMatch = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        }
    }

You need to do the filterChain.doFilter first before you try to do the call to request.getAttribute because that attribute is not set until later in the request lifecycle.

2

You can try this:

String path = (String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
0
0

I doubt you can get it in the filter through an existing API. However, you may pre-scan all @RequestMapping annotations, keep it and then in the filter match the pattern to get the desired result.

0

Spring Boot Admin - Easy To use with GUI

Another way is to use Spring Boot Admin. For that, we have to configure client-server. To avoid the error make sure the version for client-server dependency is the same. We can add the required metric from the dropdown as shown in images.

You can COUNT, TOTAL_TIME for uri:/user/getEmployee/{employeeId} as 3, 0.2264027

Client-Side:

pom.xml

<dependency>
     <groupId>de.codecentric</groupId>
     <artifactId>spring-boot-admin-starter-client</artifactId>
     <version>2.1.4</version>
</dependency>

application.properties

spring.boot.admin.api-path=/instances
spring.boot.admin.client.url=http://localhost:6699
management.endpoints.web.exposure.include=*

Server Side:

application.properties

server.port = 6699
spring.boot.admin.server.url=http://localhost:8889

pom.xml

 <dependency>
         <groupId>de.codecentric</groupId>
         <artifactId>spring-boot-admin-starter-server</artifactId>
         <version>2.1.4</version>
    </dependency>

Add @EnableAdminServer

import de.codecentric.boot.admin.server.config.EnableAdminServer;

@SpringBootApplication
@EnableAdminServer
public class AdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }

}

GUI http://localhost:6699/#/applications enter image description here


Programmatically Approach

If you want to achieve this If you want to achieve this programmatically. I am giving a hint here you may find All Details Here

@RequestMapping(path="/admin/count",method=RequestMethod.POST)
public JSONObject count(@RequestParam(name="url") final String url)//@PathVariable(name="url") final String url
{   
    String finalURL = "http://localhost:8080/actuator/metrics/http.server.requests?tag=uri:" + url + "";
    return sendRequestToURL(finalURL);  
}

We can use Spring Boot /actuator/metrics/http.server.requests to get all endPoints which are executed with their count, exception, outcome, status, total time, etc as follow.

If you want to see details for particular endPoint then you can do it by calling request as follow

localhost:8889/actuator/metrics/http.server.requests?tag=uri:<endPoint>
localhost:8889/actuator/metrics/http.server.requests?tag=uri:/user/asset/getAllAssets
localhost:8889/actuator/metrics/http.server.requests?tag=uri:/user/asset/getAllAssets&tag=status:200
  • You will get COUNT as how many times particular endPoint has been called
  • You will get COUNT as how many times particular endPoint has been
    called with a particular Status
  • To get the average time to execute endPoint you can do TOTAL_TIME/COUNT for particular endPoint as well as for a whole application

More Detailed Explanation

localhost:8889/actuator/metrics/http.server.requests

{
    "name": "http.server.requests",
    "description": null,
    "baseUnit": "seconds",
    "measurements": [
        {
            "statistic": "COUNT",
            "value": 3
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 0.21817219999999998
        },
        {
            "statistic": "MAX",
            "value": 0.1379249
        }
    ],
    "availableTags": [
        {
            "tag": "exception",
            "values": [
                "MethodArgumentTypeMismatchException",
                "None"
            ]
        },
        {
            "tag": "method",
            "values": [
                "GET"
            ]
        },
        {
            "tag": "uri",
            "values": [
                "/{id}.*",
                "/user/asset/getAsset/{assetId}",
                "/user/asset/getAllAssets"
            ]
        },
        {
            "tag": "outcome",
            "values": [
                "CLIENT_ERROR",
                "SUCCESS"
            ]
        },
        {
            "tag": "status",
            "values": [
                "400",
                "404",
                "200"
            ]
        }
    ]
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.