How to get Spring MVC #PathVariable without using an annotation? - spring

Given the spring mvc method like the one below.
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(#PathVariable("ownerId", int ownerId,
#PathVariable("petId") int petid) {
}
Is there some way to write the method so that all the URI templates variables are passed in as a
map to the handler? something along the lines of ?
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(Map<String,Object> allPathVariables) {
Integer ownerId = allPathVariables.get("ownerId");
Integer petId = allPathVariables.get("petId");
}
Is there a way to put all the URI templates in a Map that is passed to a handler method?

Looks like this can't be done with Spring MVC 3.1 there is an issue for it on the spring JIRA which is marked fixed for Spring MVC 3.2 https://jira.springsource.org/browse/SPR-9289

Related

Spring Data - Using property values in projections

I have Spring Data Rest Application where I created a projection like this
#Projection(name = "UserWithAvatar", types = { User.class })
public interface UserWithAvatar {
String getName();
String getEmail();
#Value("${app.url}/pictures/#{target.imageId}")
String getAvatar();
}
The non working part is the getAvatar, it generates a url for seeing the picture.
However this ${app.url} is not working inside the projection.
How would I add this application.properties value in there?
Use #environment.getProperty('app.url') inside #{} block. It works on Spring Boot 2.3.0, i'm not sure about older versions.
Example:
#Value("#{target.pictureUrl ?: #environment.getProperty('app.url') + 'static/default-avatar.png'}")
String getAvatarUrl();

POST / GET Request Param Validation in Spring Boot

I am using spring boot. I want to validated the POST request params. So I have gine through #Validated annotation but this require creating a different class for Every API. How should I write my code?
As for example, this is my api
#RequestMapping("/tags/{tagId}/{tagParentId}")
public Response<Demo> a(#PathVariable int tagId, #PathVariable int tagParentId){
... code
}
#RequestMapping("/data/{courseId}/{instId}")
public Response<Demo> b(#PathVariable int courseId, #PathVariable int instId){
... code
}
How should I change my code to add params validation for there API's such that I do not need to create two different validation class? Just one class and then I can add different functions for different API's.
#Validated should be used, to check that a parameter is syntactical correct.
As you are using int values, this is already done by spring.
If tagId is not a valid int, the client will already receive a Http error code.
The validation, whether there is a tag with the given tagId is implicitly done in your code, you do not need an additional validator for that.
If you read tags for example from the database, and you cannot find a tag for the tagId, you should
return new ResponseEntity(HttpStatus.NOT_FOUND);
from your controller method.
You may need to change the return type of your controller method to a common superclass or just to Object, to allow returning the ResponseEntity.
Its also possible to throw exceptions in the controller methods and to configure spring to return a regarding HttpStatus.
See exception-handling-for-rest-with-spring

Feign Client with Spring Boot: RequestParam.value() was empty on parameter 0

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);
}

How does Spring wire method parameters?

I have this Spring method which just maps a resource onto a .jsp :
#ResourceMapping(value = "display")
public String displayResult() {
return "mypage"
}
If I update the method to :
#ResourceMapping(value = "display")
public String displayResult(javax.portlet.ResourceResponse rr) {
rr.setContentType("text/html;charset=UTF-8");
return "mypage"
}
the variable rr is initialised but what Spring "magic" is occurring in the background to initialise the object javax.portlet.ResourceResponse ?
This is part of the Spring MVC framework. The default rules are explained in the section "Defining #RequestMapping handler methods" of the Spring documentation (Link for 3.2.x)
My guess is that you have a helper library on your classpath which extends the defaults for the annotation #ResourceMapping and the type ResourceResponse
As Aaron pointed out, there default rules for resolving arguments for handler methods explained in a link he provided.
However, making a handler method accept other types of parameters is achieved not by extending #ResourceMapping annotation, but by implementing new WebArgumentResolver prior to Spring 3.1 and HandlerMethodArgumentResolver from 3.1. See http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/web/method/support/HandlerMethodArgumentResolver.html
In your case, javax.portlet.ResourceResponse is probably directly supported by the Spring web mvc portlet framework, though I couldn't find a documentation for this. In your IDE, you can have a look at all classes implementing HandlerMethodArgumentResolver (or WebArgumentResolver)

Restful Service without using Annotations in Spring

I am trying to invoke the Restful Web services using Spring. I searched a lot and was able to make a restful service using different annotations.
Here is a piece of code : -
#Controller #RequestMapping("/service/")
public class RestService {
long currentId = 123;
public RestService() {
}
#ResponseBody
#RequestMapping(value="/get/{id}")
public Rest getRest(#PathVariable("id") String id) {
System.out.println("----invoking getRest, Rest id is: " + id);
Rest c = new Rest();
long idNumber = Long.parseLong(id);
c.setId(idNumber);
c.setName("John");
c.setAge("12.6");
return c;
}
I had a web.xml + (name)-servlet.xml
Above example has just one method and that method is mapped to a url.
Now what I want is not to use annotations at all in a java class. So, is there a way to make rest services without using annotations? How can I configure all the annotations in an XML file and their (all methods in a java class) corresponding URL's?

Resources