I am trying to exclude a specific parameter (param 4) from caching in my rest spring boot application
I am new to the caching and not sure how can we exclude the specific parameter from caching
#Override
#Cacheable(value = "mykey")
public MyResponse myMethod(RestTemplate restTemplate, String param1, String param2, String param3, String param4){
I am expecting the param 4 field to be excluded from the caching
Key can be created using SpEL in #Cacheable annotation. Parameters can be accessed in SpEL using #aX X is index of argument. In this case.
#Cacheable(value="mykey", key="#a1+#a2+#a3")
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html#key--
Related
We have problems deserializing a query parameter with square bracket notation (?paging[offset]=3) in Jersey.
We're using Jersey JAX-RS and annotating our endpoints and beans with swagger OpenAPI, and have tooling to generate our documentation automatically. We want to follow the JSON:API standard for describing a new API. JSON:API specifies that to implement paging, the API must accept a query parameter in the format : ?paging[offset]=0&paging[limit]=10
Our swagger annotations support this out of the box, allowing us to specify
#Parameter(
description = "paging",
style = ParameterStyle.DEEPOBJECT,
explode = Explode.TRUE)
Which is compatible with the square bracket notation paging[offset] and so on. And it generates the correct documentation for our paging parameter. All is good and great and dandy.
JAX-RS is the problem. There's a #QueryParam annotation in JAX-RS. But, to use a complex object with the #QueryParam annotation, that type must have a constructor with a single String parameter. No problem. Let's add a constructor to our paging bean.
public class PagingBean {
public PagingBean(String stringValue){...}
#XmlElement
public getOffset(){...}
public setOffset(int offset){...}
#XmlElement
public getLimit(){...}
public setLimit(int limit){....}
}
So our endpoint now looks like
#Get("/path")
public Response someEndpoint(
#Parameter(description = "paging",style = ParameterStyle.DEEPOBJECT,explode = Explode.TRUE) #QueryParam("paging") PagingBean paging
){
...
}
But if we hit our api with
GET /rest/path?paging[limit]=10&paging[offset]=5
We can see that the paging request parameter is null. It seems like Jersey didn't even recognize that the paging[... is part of the paging QueryParam. Probably that it expects exactly the paging key, and not a paging\[?-like key.
We can confirm this by injecting a #Context UriInfo ui and checking the request parameters. Their key are paging[offset] and paging[limit]
One solution to this is to flatten our parameters in the endpoint like so
#QueryParam("paging[limit]") pagingLimit,
#QueryParam("paging[offset]") pagingOffset
But this is not very nice to look at.
Ideas on how to deserialize this in Jersey ?
I created a simple Feign Client with Spring Boot like this:
#FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
#RequestMapping("/greeting")
String greeting(#RequestParam String name);
}
But when I try just to start an application I get an error:
java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
First I didn't understand what is the reason and googled a lot but didn't find an answer. Almost excidentely I figured out that it works if to write request param name explicitly:
#RequestParam("name") String name
So my question: is it a bug or could it be configured to not write request params names explicitly?
Both Spring MVC and Spring cloud feign are using the same ParameterNameDiscoverer - named DefaultParameterNameDiscoverer to find parameter name. It tries to find the parameter names with the following step.
First, it uses StandardReflectionParameterNameDiscoverer. It tries to find the variable name with reflection. It is only possible when your classes are compiled with -parameters.
Second, if it fails, it uses LocalVariableTableParameterNameDiscoverer. It tries to find the variable name from the debugging info in the class file with ASM libraries.
The difference between Spring MVC and Feign occurs here. Feign uses above annotations (like #RequestParam) on methods of Java interfaces. But, we use these on methods of Java classes when using Spring MVC. Unfortunately, javac compiler omits the debug information of parameter name from class file for java interfaces. That's why feign fails to find parameter name without -parameter.
Namely, if you compile your code with -parameters, both Spring MVC and Feign will succeed to acquire parameter names. But if you compile without -parameters, only Spring MVC will succeed.
As a result, it's not a bug. it's a limitation of Feign at this moment as I think.
Just use String greeting(#RequestParam("name") String name);
#FeignClient("spring-cloud-eureka-client")
public interface GreetingClient {
#RequestMapping("/greeting")
String greeting(#RequestParam("name") String name);
}
I use upgrade maven-compiler-plugin to solve this plobrem. you can access: https://blog.51cto.com/thinklili/2566864
This worked for me.
#FeignClient(name="session-service", url="${session.host}")
public interface SrocessingProxy {
#RequestMapping(value = "/process/{key}", method = RequestMethod.POST)
public Response processSession(#RequestParam String key, #RequestBody PayloadHolder payload);
}
//Service
#RequestMapping(value = "/process/{key}", method = RequestMethod.POST)
public Response processSession(#RequestParam String key, #RequestBody PayloadHolder payload) {
System.out.print("Key : " + key);
}
I'm trying to migrate code from Jax-Rs (jersey implementation) to a Spring MVC entry point:
jax-rs:
#GET
#Path("{bundle}/bundle{min: (-min)*}.{extension: js|css}")
public Response getBundle(#PathParam("bundle") String bundle, #PathParam("min") String min, #PathParam("extension") String extension)
Spring MVC:
#RequestMapping(method = GET, path = "{bundle}/bundle{min:(-min)?}{extension:\\.(js|css)?}")
public void getBundle(#PathVariable String bundle, #PathVariable String min, #PathVariable String extension)
According to Spring MVC documentation, i can use regex for #RequestMapping. The syntax is similar to jaxrs but the entrypoint doesn't work (404 Not Found).
Example of value for the entrypoint: http://localhost:8080/foo/bundle-min.css
I've found a solution with #RequestMapping(method = GET, path = "{bundle}/bundle**") but i have to parse the string to catch my needed parameter values.
Your regex seems fine but replace those capturing groups with non-capturing groups, like following:
{bundle}/bundle{min:(?:-min)?}{extension:\.(?:js|css)?}
With this regex, if you fire a request to foo/bundle-min.css, the bundle would be foo, the min would be -min and the extension would be .css.
I'm trying to use the #InitBind annotation to map only certain fields on the object in the request body.
I have a spring controller defined in this way:
#RequestMapping(value = "addAddress", method = RequestMethod.POST)
public Object addAddressToPerson(
HttpServletRequest request,
HttpServletResponse res,
#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "surname", required = false) String surname,
#RequestBody personDTO personJson,BindingResult result) {
The client request will be a a json representing a personDTO, but I don't want that field except the address to be mapped in the object for security reasons.
The input will be something like:
{ "address":"123 Street","........}
The personDTO contains many fields, and since spring map all of them directly in a DTO, that can be a problem.
I've seen that a solution is to use a Binder to declase the allowed or Disallowed field, but if I check the personDTO inside the controller, other fields are populate (for example if pass "id":"1234").
Any Hints?
The binder code is the following:
#InitBinder("orderJson")
protected void orderJsonBinder(WebDataBinder binder){
binder.setAllowedFields(new String[]{"address"});
}
Am I missing something?
Best Regards,
Luca.
But you are not binding request parameters to a model attribute bean, you are just asking spring to use an appropriate MessageConverter to convert the request body. As you say it is Json, you will use a MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter with Jackson 1.x). The Spring Reference Manual says for this converter :
[This is an] HttpMessageConverter implementation that can read and write JSON using Jackson's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).
#InitBinder can only configure binding of #ModelAttribute annotated parameters. It is useless here. If Jackson annotations are not enough, you will have to use a custom object mapper.
And I am surprised that you can use a BindingResult after a #RequestBody parameter, because the documentation says that it should follow a #ModelAttribute one.
I have a Spring controller and there is a String parameter I want to inject into this controller. So I'm considering adding a new constructor to the controller which accepts the String as a parameter. Is this a bad practice since Spring controllers are singletons ?
You can inject your String parameter instead:
#Value("${dropbox.access-token")
private String accessToken;