How to add additional text in annotation parameter value - spring-boot

I want to create a StudentApiController annotation for RequestMapping. It needs to add auto prefix for the RequestMapping.
Example usage 1:
#StudentApiController(value="/payment")
class PaymentEndpoint
mapping value needs to be "/student/payment"
Example usage 2:
#StudentApiController(value="/exam")
class ExamEndpoint
mapping value needs to be "/student/exam"
This is my code:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RequestMapping
annotation class StudentApiController(
val value: String = "",
#get:AliasFor(annotation = RequestMapping::class, attribute = "value")
val aliasValue: String = "/student" + value
)
I am getting this error:
Default value of annotation parameter must be a compile-time constant

Are you expecting all your endpoints to start with /student?
If yes then you can use spring.mvc.servlet.path=/student or server.servlet.context-path=/student property in your application.properties.
If not then may be you can add logic in interceptor to prefix annotation value provided through your annotation. I haven't tried this for prefixing something in preHandle(), but I have tried it for postHandle() and it works.
Refer this link it appends thymeleaf's custom layout using custom annotation through interceptor. You can implement same logic for your requirement.
Edit 1
You may refer this answer which provides another approach/es to handle your requirement.

Related

Spring boot model class property validation

I am trying to map a json object to a Spring boot model class now the contract says for a property it have only a certain set of allowed values(not more than 3).
Example:
Suppose that json has field "name" and the contract says allowed values for field "name" are john,todd,phil
Anything other than john,todd,phil wont be accepted.
Is there any way to achive this constraint using any annotations
You can use following solutions
Solution 1:
Using #Pattern annotation with regex , if you want to use case insensitive use appropriate flags
#Pattern(regexp = "john|todd|phil", flags = Pattern.Flag.CASE_INSENSITIVE)
Solution 2:
By creating a enum class type with allowed values
public enum {
JOHN, TODD, PHIL
}
In your model class use #Enumerated(EnumType.STRING) on name filed

Unable to set header as optional in OpenApi using Spring with annotations

I'm using Java 11.0.2, Spring Boot 2.2.6 and Spring OpenApi Core 1.1.49 to create an OpenApi documentation using annotations.
During the request for creating a merchant in the Controller I need to have a custom header property, but which needs to be optional. Based on Swagger documentation for Parameter Object, the field "required" (Determines whether this parameter is mandatory. If the parameter location is "path", this property is REQUIRED and its value MUST be true. Otherwise, the property MAY be included and its default value is false.) by default for header is false, but below you can see that for some reason is true (nevertheless that I configured this option to "false").
Java - part ot Controller method
public ResponseDto create(#Parameter(in = ParameterIn.HEADER, required = false, schema = #Schema(type = "string", format = "uuid"), name = "X-Request-Correlation-Id", #RequestHeader("X-Request-Correlation-Id") #Nullable String headerXRequestId, ... <
This result in OpenApi yaml file - autogenerated with the information from the annotations
parameters:
- name: X-Request-Correlation-Id
in: header
required: true
schema:
type: string
format: uuid
Can you point out the problem, because I can't find a solution in the documentation or anywhere else?!
Found the solution - the problem wasn't in OpenApi annotation #Parameter, but in Spring binding annotation #RequestHeader, which binds the header field to the method parameter. #RequestHeader has also field "required" and by default, it's set on "true" and it overrides the one in #Parameter. So the solution was the following syntax - #RequestHeader(name = "X-Request-Correlation-Id", required = false).

Renaming APIs in swagger with Spring

I understand that when documenting an API with Swagger in Spring, I can change the description for the API by adding #Api annotation, but when I add it as follows
#Api(value= "NEW_NAME", description="NEW_DESCRIPTION")
Only the description is changed, not the name.
as seen here
Further, I'm not sure where the default name and description are coming from, before adding the API, the name seems to be derived from the controller name, but the description; which to me looks natural and human like almost like hard coded String with capitalization and all.
I ran a search on the code, and I wasn't able to find those Strings. Where's Swagger getting those values from?
thanks
The attribute you are looking for is: tags. So you can avoid grouping by controller name.
From Javadoc of #Api tags:
Tags can be used for logical grouping of operations by resources or any other qualifier.
For example:
#Api(value = "/customers", tags = "customers", description = "Manage Customer")
By default Springfox creates API with name as {controller-name}-controller and description as {Controller Name} Controller (cf. How to change the default Controller Name in Swagger Spring ).
It seems that the current way to do this is:
#Api(description = "Manage cars", tags = { "Cars" })
Default -
{controller-name}-controller
For Custom Name Add -
#Tag(name="YOUR CUSTOM NAME HERE")
on the Controller Class
Example -
#RestController
#Tag(name="1. Project Resource")
public class ProjectResource {...}
Result -

Spring framework + Hazelcast , how to enable/disable cache using #Cacheable annotation

I am using Spring framework and hazelcast cache to cache REST APi at service layer. The api I am caching has #Cacheable annotation with cachename and keygenerator which works fine. I am looking for best way to enable/disable caching using application property or consul property. For that I am trying to pass the property in condition attribute of #Cachable annotation but is not working. With this approach I will end up passing same value in multiple place (wherever I am caching at API level). Is there any good way to handle such operation.
As an example here is a code snippet
#Cacheable(cacheNames = CacheName.MyCache1,keyGenerator = "customKeyGen")
public CachingObject myFirstAPI(String param1, String param2) {
}
Here the hazelcast cache will use customKeyGen and put value (CachingObject) returned by myFirstAPI . If I have to disable this operation , my current approach is to pass some value (read from application property) as condition so that it evaluate the flag/condition before creating cache and cache the value only if the condition is true i.e. cache is enabled, e.g.
#Cacheable(cacheNames = CacheName.MyCache1,keyGenerator = "customKeyGen", condition="${enableCache}")
public CachingObject myFirstAPI(String param1, String param2) {
}
In my case the expression language I am passing in condition throwing exception , which I will figure out why (It is currently throwing SpelEvaluationException, Property or field 'enableCache' cannot be found on object of type 'org.springframework.cache.interceptor.CacheExpressionRootObject' )
My question is , is this correct way to enable/disable caching ? Please suggest.
Try spring.cache.type == none. See https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html#boot-features-caching-provider-none

How does Spring bind form values into class variables?

If I use a form:form object in Spring, I can use the commandName in order to let Spring inject the class variable values.
However, I wonder, how does the controller catch this value?
#RequestMapping(method = RequestMethod.POST, value = "/form")
public String postForm(#ModelAttribute("item") Item item, ModelMap model)
{
return "result";
}
In the above code, the Item is injected. However, even changing the name of this variable (or removing the modelattribute), doesn't affect that this variable is injected with the form values.
Will spring just inject the values in the first model class found, from the form? How does Spring know that it has to inject the form into the Item item parameter?
At first I thought the variable in the controller (POST) should have the name of commandName of the form, but it does work with other names as well, strangely enough.
There is a dedicated section in the Spring Documentation describing the usage of #ModelAttribute on method arguments.
This process is known as Data Binding on submit and is following some conventions:
If #ModelAttribute is explicitely declared with a name on an argument (your case). In this case the submitted data of the form are copied over automatically under this name. You can check yourself that it is already there in your ModelMap model by invoking/inspecting model.get("item").
If there is no #ModelAttribute argument annotation at all, then the attribute name is assumed from the type essentially in your case type Item converts to attribute name item (camelCase notation) that is created for you holding a new Item with the form data-bind'ed fields. That is also there in the ModelMap (same check as above: model.get("item"))
Key point to realise is in all these cases DataBinding occurs before hitting your Post form RequestMapping.

Resources