How to properly manage PathVariables with spring - java

I'm hoping this isn't too simple of a question. I'm new to the java web services world and cant seem to get access to my PathVariables in my controller. I'm using STS and it's not complaining that my syntax is wrong.
So more important than the correct answer, I'd really like to know why this isn't working.
Here's some example code that I cant get to work:
#RestController
public class TestController {
#RequestMapping("/example")
public String doAThing(
#PathVariable String test
) throws MessagingException {
return "Your variable is " + test;
}
}
If I run a curl against it like so:
curl http://localhost:8080/example?test=foo
I receive the following response:
{"timestamp":1452414817725,"status":500,"error":"Internal Server
Error","exception":"org.springframework.web.bind.MissingPathVariableException","message":"Missing
URI template variable 'test' for method parameter of type
String","path":"/example"}
I know that I have everything else wired up correctly, other controllers work.
I feel like I must be missing some fundamental principal here.
Thanks in advance.

Spring support different ways how to map stuff from the url to method parameters: request parameters and path variables
Request Parameters are taken from url-query parameters (and request body, for example in http-POST requests). The annotation to mark the java method parameter that should take its value from a request parameter is #RequestParam
Path Variables (somtimes called path templates) are parts of the url-path. The annotation to mark the java method parameter that should take its value from a request parameter is #PathVariable
Have a look at this answer of mine, for an example an links to the Spring Reference.
So what your problem is: you want to read a Request Parameter (from the url-query part), but used the annotation for the Path Variables. So you have to use #RequestParam instead of #PathVariable:
#RestController
public class TestController {
#RequestMapping("/example")
public String doAThing(#RequestParam("test") String test) throws MessagingException {
return "Your variable is " + test;
}
}

If you are using path variable, then it has to be part of the URI. As you have not mentioned in the URI but used in the method arguments, spring tries to find out and assign this value from the path URI. But this path variable is not there in the path URI , therefore throwing MissingPathVariableException.
This should work.
#RestController
public class TestController {
#RequestMapping("/example/{test}")
public String doAThing(
#PathVariable String test
) throws MessagingException {
return "Your variable is " + test;
}
}
And your curl request would be like
curl http://localhost:8080/example/foo
//here the foo can be replace with other string values

The reason why it's not working is that there are two ways to pass parameters to a REST API implementation using RestController. One is the PathVariable, the other is RequestParam. Both of them need names to be specified in the RequestMapping annotation.
Check out this excellent resource that explains RequestMapping in detail
Try this for your solution.
#RequestMapping("/example/{test}", method= RequestMethod.GET)
public String doAThing(
#PathVariable("test") String test
) throws MessagingException {
return "Your variable is " + test;
}

The solution for me was:
#RestController
#RequestMapping("/products")
#EnableTransactionManagement
public class ProductController {
#RequestMapping(method = RequestMethod.POST)
public Product save(#RequestBody Product product) {
Product result = productService.save(product);
return result;
}
}

Related

How to test with Postman a controller's method that has one or more objects parameters (and not simple params) in Spring?

I am a newbie in Spring development. I need to create a simple application, a controller that has a method that takes as parameter an object of a custom designed entity class into the project. The prototype looks like this:
#RestController
public class JobsController {
#PostMapping("/search")
public ResponseEntity<?> search() {
log.info("JobsController -> search method");
//JobSearchEntity jobSearchEntity = modelMapper.map(jobSearch, JobSearchEntity.class);
List<JobEntity> jobs = jobService.searchJobs();
//log.info(String.format("Job found: %s ", jobSearch));
return ResponseEntity.ok(jobs);
}
}
Can someone who is more advanced into this staff with Postman testing tell me how to do that , how to test a controller method which takes parameters?
You can use postman to submit parameters in JSON format after adding # requestbody annotation on the method, or submit parameters directly in form without annotation
You can use this example. Is very simple exemple.
#RestController
#RequestMapping("/root")
public class RootController {
private final RootService service;
public RootController(final RootService service) {
this.service = service;
}
#PostMapping("/exemple")
public void createRoot(#RequestBody final RootDto dto) {
service.createRoot(dto);
}
}
Then you can send request to POST host/root/exemple with your JSON.
More exampls you can find here: https://www.baeldung.com/spring-request-response-body
It seems you are missing an honest search on google about the subject.
You can make use of #RequestBody annotation to accept method arguments.
Check these page for examples --
#RequestBody and #ResponseBody annotations in Spring
https://stackabuse.com/get-http-post-body-in-spring/
https://www.twilio.com/blog/create-rest-apis-java-spring-boot
These set of playlist on youtube are very good starter course for SpringBoot -
https://www.youtube.com/c/JavaBrainsChannel/playlists
Postman Tutorial--
https://www.youtube.com/watch?v=VywxIQ2ZXw4
To get data from api is preferred to use GET method :
#RestController
public class JobsController {
#GetMapping("/search")
public ResponseEntity<?> search(#RequestParam("id") String id,#RequestParam("desc") String desc) {
log.info("JobsController -> search method");
//JobSearchEntity jobSearchEntity = modelMapper.map(jobSearch, JobSearchEntity.class);
List<JobEntity> jobs = jobService.searchJobs();
//log.info(String.format("Job found: %s ", jobSearch));
return ResponseEntity.ok(jobs);
}
}
you call this api with post man this way :
#PostMapping used usually to save new data (example : create job )
Take look on rest resource naming guide

Check if a controller has a #RequestHeader dynamically (in a test)

Using SpringBoot I managed to get the list of all controllers dynamically (in a test) using RequestMappingHandlerMapping, but I cannot check if the controller uses the #RequestHeader("language") or not. Is there a way to retrieve this information?
I don't think it's possible from RequestMappingHandlerMapping.
Thanks.
public void randomApi(#PathVariable("user") String user,
#RequestHeader("language") String language){...}
RequestMappingHandlerMapping represents all controller methods, you have to get a particular controller method that you are interested from it first. The easiest way to do it is first give a name to the controller method such as GetRandomApi :
#GetMapping(name = "GetRandomApi" , value= "/random")
public void randomApi(#PathVariable("user") String user, #RequestHeader("language") String language){
}
and then get the controller method by this name :
HandlerMethod hm = mapping.getHandlerMethodsForMappingName("GetRandomApi").get(0);
Please note HandlerMethod represents a controller method and I assume you only has one controller method with this name.
To check if this controller method has a parameter which is annotated with #RequestHeader , you can do something likes:
for( MethodParameter param : hm.getMethodParameters()){
RequestHeader requestHeader = param.getParameterAnnotation(RequestHeader.class);
if(requestHeader != null) {
System.out.println(String.format("parameter index %s is annotated with #RequestHeader with the value %s",
param.getParameterIndex(),
requestHeader.value()));
}
}

Spring restful API, is there a method being used like router to get other method's end points or URL?

#RequestMapping("/accounts")
public class controller {
#GetMapping("/get/{id}")
public final ResponseEntity<?> getHandler(){
}
#PostMapping(value = "/create")
public final ResponseEntity<?> createHandler(){
/*
trying to use some spring library methods to get the url string of
'/accounts/get/{id}' instead of manually hard coding it
*/
}
}
This is the mock code, now I am in createHandler, after finishing creating something, then I want to return a header including an URL string, but I don't want to manually concat this URL string ('/accounts/get/{id}') which is the end point of method getHandler(), so I am wondering if there is a method to use to achieve that? I know request.getRequestURI(), but that is only for the URI in the current context.
More explanation: if there is some library or framework with the implementation of route:
Routes.Accounts.get(1234)
which return the URL for the accounts get
/api/accounts/1234
The idea is, that you don't need to specify get or create (verbs are a big no-no in REST).
Imagine this:
#RequestMapping("/accounts")
public class controller {
#GetMapping("/{id}")
public final ResponseEntity<?> getHandler(#PathVariable("id") String id) {
//just to illustrate
return complicatedHandlerCalculation(id).asResponse();
}
#PostMapping
public final ResponseEntity<?> createHandler() {
//return a 204 Response, containing the URI from getHandler, with {id} resolved to the id from your database (or wherever).
}
}
This would be accessible like HTTP-GET: /api/accounts/1 and HTTP-POST: /api/accounts, the latter would return an URI for /api/accounts/2 (what can be gotten with HTTP-GET or updated/modified with HTTP-PUT)
To resolve this URI, you could use reflection and evaluate the annotations on the corresponding class/methods like Jersey does.
A Spring equivalent could be:
// Controller requestMapping
String controllerMapping = this.getClass().getAnnotation(RequestMapping.class).value()[0];
and
//Method requestMapping
String methodMapping = new Object(){}.getClass().getEnclosingMethod().getAnnotation(GetMapping.class).value()[0];
taken from How do i get the requestmapping value in the controller?

Extracting parameters from URL with POST method

I have something like this :
#RestController
#RequestMapping("/prop")
public class PropController {
#RequestMapping(method = RequestMethod.POST)
public Prop getProp(#ModelAttribute PropForm propForm) {
//calling methods and stuff using propForm
}
}
My PropForm class :
#Data
public class PropForm {
private String att1;
private String att2;
private String att3;
}
Now I am calling this URL :
http://localhost:8080/prop?att1=blabla&att2=blob&att3=test
I want to extract the parameters from the URL and put them in my propForm.
I've tried replacing #ModelAttribute by #RequestBody and then by #RequestParam. It's still not working, I always get a NullPointerException when running the application.
Please, note that I need to use POST method. I already have it working using GET method
FIRST Make sure you have getters and setters in your PropForm class...
Then, you need to put into your model the Form entity:
model.put("NAME", propForm);
And declare method like this:
#RequestMapping(method = RequestMethod.POST)
public Prop getProp(
#ModelAttribute PropForm propForm
#Valid #ModelAttribute("NAME") PropForm propForm)
// ^ you're missing the name!
{
// do your stuff....
return (Prop) propForm;
}
I think you controller and mapping is ok.
But the problem is you are expecting a post request in the mapping, and you are calling
http://localhost:8080/prop?att1=blabla&att2=blob&att3=test
Now this will generate a GET request Not Post. You cannot send a post request using only url.
If you cant use a form for sending the request then you need to use any 3rd party to generate a POST request
like you can use jquery $.post()
And also att1 att2 will not help unless you bind the object with the model attribute.

On Java Controller how to get the value of annotation #RequestMapping("/getThisValueFromOtherClass")?

On Java MVC Controller how to get the value of annotation #RequestMapping("/getThisValueFromOtherClass")? I know we can extract this by using java reflections but is there any other way? Thank you.
#RequestMapping("/getThisString")
public class MyController{}
If the purpose is just to avoid changing the url at every place, I will suggest define a string constant in some class and instead of using hard coded string in request mapping use that constant every where.
In future if u want tp\o change the url, simple update the constant value at one place
final String constUrl = "/myurl";
#RequestMapping(value=constUrl)
you can make the constant static, if defining in another class
The value of the annotation can be read programmatically:
#RequestMapping("/endpoints")
public ResponseEntity<String> getPath() {
String path = getClass().getAnnotation(RequestMapping.class).value()[0];
return new ResponseEntity<String>(path, HttpStatus.OK);
}
To obtain the path, you should pass the Request i.e. HttpServletRequest as a parameter to your handler method.
#RequestMapping(value={"/getThisString"}, method=RequestMethod.GET)
public String handlerMethod (Model model, HttpServletRequest request) throws Exception {
String getThatString = request.getServletPath();
....
}
Reference:
HttpServletRequest
In your case if an URI pattern “/getThisString” is requested, it will map to this MyController, and handle the request with method where #RequestMapping(method = RequestMethod.GET) is declared.
You can refer this tutorial #RequestMapping example
Hope it helps.

Categories