how to achieve Dynamic URL in REST WebService - java

I am trying to develop new web service for my application.
For that I am using Spring REST-webservice.
In the controller end, I am trying to fetch the list of records based on the agent passed.Now the requirement , the agent can be passed or it can be null.In case of null agent all records should be selected.else only those records to be fetched.
Tried using below code for achieving dynamism., as per one of the search result however it is not working.
#RequestMapping(value = "/staging/{agentCode: [^/]*?}" , method =
RequestMethod.GET)
Here is my existing code:
#Controller
#RequestMapping(value="/batches")
public class BatchController {
#SuppressWarnings({ "rawtypes" })
#RequestMapping(value="/staging/{agentCode}", method =
RequestMethod.GET)
#ResponseBody
public ResponseEntity IntmBatch(#PathVariable("agentCode") String
agentCode)
{
//code here
}
CASE 1:when I use URL like .,
www.example.com/myapplication/batches/staging/1234
it works fine and desired result is fetched.
CASE 2:However in case I am not passing any parameter say.,
www.example.com/myapplication/batches/staging/
where in , I am not passing any parameter., it says me mapping not found.
Can you please let me know how to achieve this dynamic URL in REST GET Request Method Call.
Thanks in advance!!

Instead of using #Pathvariable you can use #RequestParam for optional values in URL.
So your URL will be like.
CASE 1 : www.example.com/myapplication/batches/staging?agentCode=1234 &
CASE 2 : www.example.com/myapplication/batches/staging
Hope it will work solve your issue.
#SuppressWarnings({ "rawtypes" })
#RequestMapping(value="/staging", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity IntmBatch(#RequestParam(name="agentCode",required=false) String agentCode)
{
//code here
}

create one more method in controller with #RequestMapping(value = "/staging", method = RequestMethod.GET) as follows.
#RequestMapping(value = "/staging", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity returnAll() {
System.out.println("returning all ");
// code here
return null;
}

Related

AWS lambda/api gateway does not redirect

I have a very simple spring route that im attempting to run on aws lambda. The route simply returns the text/string "redirect:/upload" instead of redirecting. I have the html file in the /resources/templates folder.
#RequestMapping(path = "/test", method = RequestMethod.POST)
public String UploadPage2() {
return "redirect:/upload";
}
I think the problem is from the return type of method: String.
You can do:
public RedirectView UploadPage2() {
return new RedirectView("/upload");
}
Second question
To return an view on path /test with GET request, you need another method with same path but different method
#RequestMapping(path = "/test", method = RequestMethod.GET)
public ModelAndView testGet(){
return new ModelAndView("uploadview");
}

MongoDB and Spring MVC: PATCH operation?

I have the following simple Java Controller with the Spring Web framework:
#RestController
#RequestMapping("/rounds")
#Slf4j
public class RoundController {
private RoundService roundService;
#Autowired
public RoundController(RoundService roundService) {
this.roundService = roundService;
}
#GetMapping(
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<Round> find() {
return roundService.find();
}
#GetMapping(
path = "/{userId}",
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<Round> get(#PathVariable String userId) {
return roundService.getRoundsByUserId(userId);
}
#PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.CREATED)
public Round create(#Valid #NotNull #RequestBody Round round) {
roundService.create(round);
return round;
}
#DeleteMapping(
path = "/{id}",
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public void delete(#PathVariable String id) {
ObjectId objectId = new ObjectId(id);
roundService.delete(objectId);
}
}
When using Mongo is there a best practice for doing an update / patch for an object?
Is it best to just use the POST method, and re-save the Round object in the DB with the changes the user has made?
According to me, the best practice should not be the use of the POST for doing an update/patch.
Keep you POST to do only Round creation.
If you use spring data mongodb just call the save method of your repository with your entity
see https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/MongoRepository.html
For an Update, better to add PUT /{roundId} in your controller and either :
call your save method if you have all the Round data
call a findById to have the full data and set the data you want to change, then save (but this is more like a PATCH)
Or you can also add a PATCH /{roundId} and update just the field you want in your document
see https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoTemplate.html

Unable to send data to POST Service in Spring 4

I'm trying to write a simple HTTP REST service using Spring 4.
I'm having troubles sending data to a POST endpoint
#RequestMapping(value = "/onlyPost", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestParam("value1")
String param1, #RequestParam("value2") String param2) {
....
}
While trying to send data with Postman, I receive a 400 message (obviously the values are setted in the request's body)
"message": "Required String parameter 'value1' is not present",
What I have noticed is that the issue is somewhat related to the headers, because when I remove the postman's header (Content-Type: application/json) everything works fine.
I tried for more than one hour fixing this by myself with no results. Any hints?
#RequestParam is used to read a URL query parameter.
http://localhost:8080/springmvc/onlyPost?value1=foo&value2=bar
For instance, in the URL above, value1 and value2 are query parameters that you can read using that annotation.
But if you want to read a JSON request instead, you need to change the method to:
#RequestMapping(value = "/onlyPost", method = RequestMethod.POST,
consumes = "application/json")
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
....
}
where User is a POJO holding the two fields:
public class User {
private String value1;
private String value2;
// getters and setters...
}
HTTP 400 is returned when your request is badly formatted, i.e. missing required request parameters
#RequestParam is for URL Params, if you want to pass them like that, you call
<api_url>/onlyPost?value1=<value1>&value2=<value2>
but... if you want to create user you should rather use #RequestBody and put your user data there. Something like that:
#RequestMapping(value = "/users", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
[...]
}
if you are creating REST api you should use concrete endpoints, here is a pretty cool reading with some tips: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

How to map REST parameters to complex object?

I want to create a REST service with spring that takes a bunch of parameters. I'd like these parameters to be mapped automatically into a complex transfer object, like:
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public String content(#RequestParam RestDTO restDTO) {
Sysout(restDTO); //always null
}
public class RestDTO {
private boolean param;
//getter+setter
}
But: when I execute a query like localhost:8080/myapp?param=true the restDTO param remains null.
What am I missing?
Try with localhost:8080/myapp?param=true.
Probably a case where another pair of eyes sees the obvious :)
EDIT
Remove #RequestParam from method signature, works for me.
It turned out I have to omit the #RequestParam for complex objects:
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public String content(RestDTO restDTO) {
Sysout(restDTO);
}
So, I see few problems (if it's not mistyping of course):
localhost:8080/myapp&param=true "&" isn't correct, you have to use "?" to split params from URL like localhost:8080/myapp?param=true.
I don't see mapping value in #RequestMapping(method = RequestMethod.GET) (But if you caught the request you've made correct configuration).

Can #PathVariable return null if it's not found?

Is it possible to make the #PathVariable to return null if the path variable is not in the url? Otherwise I need to make two handlers. One for /simple and another for /simple/{game}, but both do the same just if there is no game defined i pick first one from a list however if there is a game param defined then i use it.
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable("example") String example,
HttpServletRequest request) {
And this is what I get when trying to open page /simple:
Caused by: java.lang.IllegalStateException: Could not find #PathVariable [example] in #RequestMapping
They cannot be optional, no. If you need that, you need two methods to handle them.
This reflects the nature of path variables - it doesn't really make sense for them to be null. REST-style URLs always need the full URL path. If you have an optional component, consider making it a request parameter instead (i.e. using #RequestParam). This is much better suited to optional arguments.
As others have already mentioned No you cannot expect them to be null when you have explicitly mentioned the path parameters. However you can do something like below as a workaround -
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable Map<String, String> pathVariablesMap,
HttpServletRequest request) {
if (pathVariablesMap.containsKey("game")) {
//corresponds to path "/simple/{game}"
} else {
//corresponds to path "/simple"
}
}
If you are using Spring 4.1 and Java 8 you can use java.util.Optional which is supported in #RequestParam, #PathVariable, #RequestHeader and #MatrixVariable in Spring MVC
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable Optional<String> game,
HttpServletRequest request) {
if (game.isPresent()) {
//game.get()
//corresponds to path "/simple/{game}"
} else {
//corresponds to path "/simple"
}
}
You could always just do this:
#RequestMapping(value = "/simple", method = RequestMethod.GET)
public ModelAndView gameHandler(HttpServletRequest request) {
gameHandler2(null, request)
}
#RequestMapping(value = "/simple/{game}", method = RequestMethod.GET)
public ModelAndView gameHandler2(#PathVariable("game") String game,
HttpServletRequest request) {
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable(value="example",required = false) final String example)
Try this approach, it worked for me.
I just tested this just now, but by combining the above solution i got this:
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable(value = "game", required = false) String example,
HttpServletRequest request) {
if (example != null) {
//...
} else {
//pick first, ...
}
}
Now when you use "/simple", String example will be null instead of throwing Exception.
Short solution, no fancy Optional<> or Map<>
We can write multiple methods in controllers with explicit mapping with the path variable combination to exclude the optional variables (if using old version of Spring)
In my scenario wanted to develop an API to get recycle value for old device where parameters could be brand, model and network however network is an option one.
One option to handle this was use network as a request parameter instead of pathVariable.
for e.g. /value/LG/g3?network=vodafone however I didn't like this approach.
for me the more cleaner one was to use below
/refurbValue/LG/g3
/refurbValue/LG/g3/vodafone
#RequestMapping(value = "/refurbValue/{make}/{model}/{network}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
def getRefurbValueByMakeAndModelAndNetwork(#PathVariable String make, #PathVariable String model, #PathVariable String network ) throws Exception {
//logic here
}
#RequestMapping(value = "/refurbValue/{make}/{model}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
def getRefurbValueByMakeAndModel(#PathVariable String make, #PathVariable String model) throws Exception {
//logic here
}
In the above example, both controller can use the same service method and handling of the parameter can be done. In my case I was using Groovy so it was easy to use with optional parameter like
Map getRefurbValue(String brand, String model, String network="")

Categories