How do I get templated values in uri tag for http-client-requests metrics using Spring-Boot's RestTemplate? - spring

We are using Spring Boot 2.1.4 and the micrometer-registry-prometheus dependency to capture metrics.
We have a case where a Spring Boot service uses a RestTemplate to call out to another service. The metrics being generated by this call contain the actual values in the URI instead of the templated values.
For example, in the /actuator/prometheus endpoint, I see entries like this:
http_client_requests_seconds_count{clientName="someClient",method="GET",status="200",uri="/person/lookup?firstName=Tony&lastName=Soprano",} 1.0
Based on the documentation, I would expect to see the variable names rather than the values, like this:
http_client_requests_seconds_count{clientName="someClient",method="GET",status="200",uri="/person/lookup?firstName={firstName}&lastName={lastName}",} 1.0
Is there a way to get the default http.client.requests metric values to use the templated values for the URI tag?
The Spring documentation at https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-http-clients says this about the uri tag:
Request’s URI template prior to variable substitution, if possible (for example, /api/person/{id})
How do we make it possible for the variable substitution to take place?

I assume you are using the RestTemplateBuilder to build your RestTemplate as otherwise you wouldn't be getting the metrics registered.
Are you actually passing a templated url into RestTemplates exchange methods and pass along the params for subsbitution? Works-for-me on 2.1.4.RELEASE and 2.2.1.RELEASE.
template.getForObject("http://localhost:" + this.serverPort + "/hello/{id}",
String.class, Collections.singletonMap("id", "loop"));
Results in:
http_client_requests_seconds_count{application="micrometered2",clientName="localhost",method="GET",outcome="SUCCESS",status="200",uri="/hello/{id}",} 23.0

Related

Spring Boot 2.2.5. How to retrieve a PathVariable parameter inside a Spring Filter

The problem I want to solve.
I need to apply a specific logic to all restful endpoints where the url belongs to a specific sub path: let's say "/api/employee/{id}". This means all the links which start with this path should apply a logic based on the employee ID, which I am trying to apply directly in Spring Boot filter in order to avoid to spread the logic everywhere.
The problem I face.
I am able to get the query parameters from the ServletRequest, but the PathVariables are not available in the Filter.
Any idea how this could be parsed?
Would be much appreciated :)
The PathVariables are simply the URI. You cann call getRequestURI()
From the docs:
java.lang.String getRequestURI()
Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request. The web container does not decode this String. For example:
First line of HTTP request Returned Value
POST /some/path.html HTTP/1.1 /some/path.html
GET http://foo.bar/a.html HTTP/1.0 /a.html
HEAD /xyz?a=b HTTP/1.1 /xyz
https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getRequestURI--

Get full URL with parameters in Thymeleaf?

I've got a paginated list of cars, on a Spring Boot server, with the parameters sort, range, desc, page, etc to filter and sort by and am generating the URL in Thymeleaf that looks like:
example.com/cars?page=5&sort=mileage
I am wanting to be able to add more parameters to a URL with a few of these already but I'm quite new to this and don't really know how to get the current URL with all the parameters to add more params without losing the previous ones look like
example.com/cars?page=5&sort=mileage&desc=true
I've found an answer to do something like this on Spring but would ideally want to do it on the Thymeleaf template, is this possible?
Get full current url thymeleaf with all parameters
I found that you can get hold of specific parameters in Thymeleaf using ${param.sort} to get hold of the param sort, could something similar to this get hold of all the params currently?
Thanks
If someone is still looking for a thymeleaf template only solution, you could use ${#request.getRequestURI()} with ${#request.getQueryString()} and add your additional parameters via concatenation:
<a th:href="#{${url}}" th:with="url=${#request.getRequestURI()+'?'+#request.getQueryString()+'&foo=bar'}">Link</a>
If you need to escape query parameters, you can use #uris.escapeQueryParam():
<a th:href="#{${url}}" th:with="url=${#request.getRequestURI()+'?'+#request.getQueryString()+'&foo='+#uris.escapeQueryParam('b a r')}">Link</a>
Some further details:
You have to use th:with, otherwise the parser will throw TemplateProcessingException: Access to request parameters is forbidden in this context. in newer thymeleaf versions.
It also works when the current query is empty, the url generator will create a valid url, in my example including one ? and no & in the query part.

How to prevent of converting header name into lower case - Spring boot?

I am developing a java based web application in spring-boot where I am sending http-header from server to client end.
Server-side I have used Spring-boot in form of REST API, whereas at client end we have simple plain HTML5/Angular framework.
My query is, while I am sending any header from server then at client end I always get it into lowercase vs actual.
For example, I am setting header something like,
header.add("KK-ACTUAL-VALUE", "sfsfjDFFDHTsdfJKKA");
At client end, it gives,
kk-actual-value : "sfsfjDFFDHTsdfJKKA"; (Header name converts into lower case!)
Now, the question is, how to prevent it ?
I want to have the same header name what is actually passed by Server/API.
Looks like tomcat container turns the headers to lowercase. Since spring boot uses an embedded tomcat is been affected by it. More info here: https://github.com/mitre/HTTP-Proxy-Servlet/issues/65
As the Http standard specifies:
Each header field consists of a case-insensitive field name followed
by a colon (":") ...
https://www.rfc-editor.org/rfc/rfc7230#section-3.2
To make the header case-sensitive you can set it to the MultiValueMap, and then pass it as a parameter to the HttpHeaders constructor.
The code snippet can be something like this:
MultiValueMap <String, String> headers = new LinkedMultiValueMap<>();
headers.add("KK-ACTUAL-VALUE", "sfsfjDFFDHTsdfJKKA");
HttpHeaders responseHeaders = new HttpHeaders(headers);

Spring REST API with swagger - map of values in request param

I have a Spring Boot based REST API application with the following endpoint (Written in Kotlin)
#RequestMapping(value = ["/search"], method = [RequestMethod.GET])
#ApiOperation("Check whether any of the map values exists. Returns string 'true' if stamp exists, else 'false'")
fun checkExists(
#ApiParam("information about the stamp as key-value pairs (example: ds=2017-11-34&hh=05)", required = true)
#RequestParam searchValues: Map<String, String>
): Boolean {
return service.checkExists(searchValues)
}
And I know Spring supports sending a dynamic map of key value pairs as documented here.
I am also using Swagger to document the API definitions, and further more, I am using swagger-codegen-cli to generate the client library using which someone can connect with this REST API.
Now, the issue is, I am not able to send a map of values from the swagger generated client to the Spring REST API (even though Spring supports it). Starting from Swagger OpenAPI 3, they've added support for Object types in the specification. But this works in a different way than I need. For example with just Spring and RequestParam of type Map
http://localhost:8080/search?foo=A&bar=B
is parsed as a map of key value pairs
key="foo",value="A"
key="bar",value="B"
But, When I send a Map Object from the swagger client with the same key-value pairs
Map<String, String> values = new HashMap<>();
values.put("foo","A");
values.put("bar","B");
return out = clientApi.checkExistsUsingGET(values);
This sends a request to the REST API in form of
http://localhost:8080/search?searchValues={foo=A,bar=B}
and the map in Spring side ends up as
key="searchValues",value="{foo=A,bar=B}"
I've been struggling to get the swagger client api to send the request in a way the Spring API is intended to work with Map of values, but I am not able to figure a solution.
Am I doing using the client API in a wrong way?, or this just can't be done with swagger?
Any suggestions/opinions appreciated!
This is not yet supported by swagger-ui. See this issue https://github.com/swagger-api/swagger-ui/issues/2241

Is it possible to proxy an interface with multiple methods going to different endpoints

I have an interface with methods foo() and bar() that I'd like to go to endpoints direct:foo and direct:bar. In the proxy configuration you are only allowed to enter one endpoint and I have not found any way to get the name of the method called in code to be able to route based on that name.
Am I missing some document somewhere?
Look at the info at http://camel.apache.org/message-endpoint.html related to 'toD'
I think you are using a Camel version > 2.15
Revert to old behaviour which does not bind parameters to body and then you will have accesso to BeanInvocation object that will tell you which method was called.
// Create Proxy
MyAuditService service = new ProxyBuilder(context)
.endpoint("direct:analyzeMethodCall") // dispatcher endpoint
.binding(false) // false: gives you BeanInvocation, true gives you parameter
.build(MyAuditService.class);
Then in your route from direct:analyzeMethodCall use a Processor to analyze the BeanInvocation object and call direct:foo or direct:bar. You must set the body explicitly.

Resources