spring rest web service how to solve ambiguous get - spring

I have a spring web service rest.
I have two get :
// VIEW PRODUCT DETAILS
#RequestMapping(value="/products/{prodId}", method=RequestMethod.GET)
public #ResponseBody Product productView(#PathVariable("prodId") int prodId) {
logger.info("======= in productView (REST)");
Product product = inventoryService.findProductById(prodId);
return product;
}
// VIEW PRODUCT DETAILS NAME
#RequestMapping(value="/products/{prodName}", method=RequestMethod.GET)
public #ResponseBody Product productNameView(#PathVariable("prodName") String prodName) {
logger.info("======= in productNameView (REST)");
Product product = inventoryService.findProductByName(prodName);
return product;
}
I get a ambiguous message when I do a get with
http://localhost:8080/rest/products/627790"
How can I have two different get?

You can user query param approach:
#RequestMapping(value="/products/{prodId}",
method=RequestMethod.GET, params = "type=prodId" )
#RequestMapping(value="/products/{prodName}",
method=RequestMethod.GET, params = "type=prodName")
Hit call using following URLs:
http://localhost:8080/rest/products/123?type=prodId"
http://localhost:8080/rest/products/456?type=prodName"
let me know it helps you.

That is because when the url */products/627790 is hit, 627790 can be treated as int as well as string. How will the rest know which controller to invoke?
To avoid ambiguity in your case you should use #RequestParam instead of #PathVariable. URL on client side will change but code will be more readable and resolvable.
So,
#RequestMapping(value="/products", method=RequestMethod.GET)
public #ResponseBody Product productView(#RequestParam("prodId") int prodId) {}
#RequestMapping(value="/products", method=RequestMethod.GET)
public #ResponseBody Product productNameView(#RequestParam("prodName") String prodName) {}
Your urls will be:
http://localhost:8080/rest/products/prodId=627790"
http://localhost:8080/rest/products/prodName=myProduct"

Related

Error 400 when receiving data from URL parameters en Spring MVC

I am trying to receive data from an URL with two parameters like this one:
http://localhost:80000/xxx/xxx/tickets/search?codprovincia=28&municipio=110000
No matter the approach, I am always getting a 400 error, but if I access the URL without the two parameters, the controller returns the view correctly (without the parameters, naturally)
This is the code of my controller:
#Controller
#RequestMapping(value = "/xxx" )
public class BuscadorIncidenciasController extends BaseControllerWeb {
#RequestMapping("tickets")
public String tickets(Model model, #RequestParam ("codprovincia") String codprovincia, #RequestParam ("municipio") String municipio, HttpServletRequest request) throws NoAjaxException {
//...
return CONST.JSP_VIEW;
}
...}
Extra info: if I use this URL:
http://localhost:9081/xxx/xxx/tickets/search/28/790000
And this code:
#Controller
#RequestMapping(value = "/xxx" )
public class BuscadorIncidenciasController extends BaseControllerWeb {
#RequestMapping(value = "buscar/{codprovincia}/{municipio}", method = RequestMethod.GET)
public String buscar(#PathVariable Integer codprovincia, #PathVariable Integer municipio ,Model model, HttpServletRequest request) throws NoAjaxException {
//...
return CONST.JSP_VIEW;
}
...}
It gets the parameters correctly. The problem is that I have to use the first URL. I have reviewed similar questions about similar issues, and I have implemented the solutions to those issues, but I get the 400 error regardless what I try (add value="xxx=, required=false, and other suggestions.)
For RequestParam, you need to explicitly add 'name' attribute
#RequestParam(name = "codprovincia"), #RequestParam (name = "municipio")
No need to for HttpServletRequest, unless you have reason
Also, in your 'tickets' method, RequestMapping is not conforming to your URL path.
I think it should be
#RequestMapping("/xxx/tickets/search")
Cheers!

Problems implementing an easy REST service - Spring MVC

I have a Springboot application (a videoclub application). I do not think it is necessary to show you all the code as it would be very verbose. But it works fine.
A class Film, and FilmRepository with some methods, a controller, database JPA, and the HTML files. Everything works fine. I am not looking for a code solution, but more for a "conceptual" solution, just to know if I am implementing properly the REST service.
I want to add now a really easy REST service (adding a class "MyRestController") that will search for a film just by adding the name of it in the URL.
So apart from my Controller, I want to add this RestController just to do this simple thing: If I add the name of a film in the URL, it will search for it with the normal MVC methods.
But adding what I think is the solution gives me this error:
There was an unexpected error (type=Not Found, status=404).
No message available
So when going through the HTML content to the page to search for the film, and adding to that /buscar the film name (which I have in the db) /buscar/Interstellar, it shows the before error.
#CrossOrigin
#RestController
public class MyRestController {
#Autowired
private FilmRepository filmRepo;
#RequestMapping(value = "/buscar", method = RequestMethod.GET)
public ResponseEntity<List<Film>> getFilms(#RequestParam String Title) {
List<Film> pelis = (List<Film>) filmRepo.findByTitle(Title);
HttpStatus status = HttpStatus.OK;
ResponseEntity<List<Film>> response = new ResponseEntity<>(pelis, status);
return response;
}
}
Getting this error makes me think the page knows it has to do something, but might be having trouble getting it (due to strings, iterable things, lists, or that sort of problem, JSON maybe). But I do not know if the "theory" behind the rest service is alright in MyRestController.
Film repository:
public interface FilmRepository extends CrudRepository<Film, Long>{
Iterable<Film> findByTitle(String Title);
}
(The MVC method in the normal controller)
#RequestMapping("/buscar")
public ModelAndView processSearch(#RequestParam(value = "title", required = false) String title) {
if (title == null || title == "") {
Iterable<Film> films = filmRepo.findAll();
return new ModelAndView("buscar").addObject("films", films);
}
Iterable<Film> films = filmRepo.findByTitle(title);
return new ModelAndView("buscar").addObject("films", films);
}
What you're talking about is a #PathVariable
#RequestMapping(path={"/buscar","/buscar/{title}"})
public ModelAndView processSearch(#PathVariable(value = "title", required=false) String title) {
In the end, the problem was with the #RequestParam, which makes you search in the URL with a query like: /buscar?Title=Interstellar
#RequestMapping(value = "/buscar/{title}", method = RequestMethod.GET)
public ResponseEntity<List<Film>> getFilms(#PathVariable String title) {
List<Film> pelis = (List<Film>) filmRepo.findByTitle(title);
HttpStatus status = HttpStatus.OK;
ResponseEntity<List<Film>> response = new ResponseEntity<>(pelis, status);
return response;
}
With this REST service, you can search by URL like "/buscar/Interstellar".
The result is going to give you JSON content with all the information of the Object Film.

Spring common method between #RequestMappings

In a Servlet, you can include an #Override service method which gets called before the doGet or doPost, is there a way to achieve the same in a Spring #Controller?
Or more precisely, in each method in the Controller, I need to make sure an Entity (in this case, a Product) exists and redirect otherwise, like so, so how would one achieve that in Spring? Note that I also need the Product available in each Method.
#Controller
#RequestMapping("/product/{prod_id}/attribute")
public class AttributeController {
#Autowired
private AttributeService attributeService;
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String add(Model model, #PathVariable Long prod_id) {
Product product = attributeService.getProduct(prod_id);
if (product == null) {
return "products/products";
}
model.addAttribute("product", product);
model.addAttribute("attribute", new Attribute());
return "products/attribute_add";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String save(Model model, #PathVariable Long prod_id, #Valid Attribute attribute, BindingResult result) {
Product product = attributeService.getProduct(prod_id);
if (product == null) {
return "products/products";
}
// ...
}
// ...
}
This can be done with HandlerInterceptor. All you need to do is to extend HandlerInterceptorAdapter#preHandle and then register your interceptor through WebMvcConfigurer#addInterceptors. You can choose to use interceptor with all your mappings or with some specific mappers through InterceptorRegistration object with is returned by InterceptorRegistry#addInterceptor method.
By the way, HandlerInterceptors are useful to do some utility operations with requests and responses in general, like logging, adding headers, authentication, etc. For business-related operations I would recommend to use ControllerAdvice with custom business-oriented exceptions. In this case it would be a method which retrieves Product from database and throws custom exception if not found.

how to configure spring-data-rest search method path with #PathVariable

I want to customize my spring-data-rest search method path by passing parameter as a path variable like follows
http://localhost:8080/orders/search/customers/{customerId}
findByCustomer(#PathVariable("customerId") Integer customer);
The search resource listh the links as follows
http://localhost:8080/orders/search/customers/%7BcustomerId%7D
How to expose search url with path params?
You can use custom handler similar to this:
#RepositoryRestController
public class OrderController {
#Autowired
OrderRepository orderRepository;
#GetMapping("/orders/search/customers/{id}")
public #ResponseBody ResponseEntity<?> getByCustomers(#PathVariable Integer customer) {
Order order = orderRepository.findOne(id);
if(order == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
Resource<Order> resource = new Resource<Order>(order);
return ResponseEntity.ok(resource);
}
}
More about this can be found here.
Use HttpServletRequest to get the request url:
findByCustomer(#PathVariable("customerId") Integer customer, HttpServletRequest request){
String request = request.getRequestURL().toString(); // StringBuffer, so use append if you want to...
[...]
}
also you can use request.getQueryString() to get the query part after ?.

How to specify multiple parameters in POST method

I have Model called Loan:
public class Loan {
private int loan_id;
private String clientName;
private String clientSurname;
private Double amount;
private int days;
//getters and setters
}
And Controller
#RestController
public class MyController {
#Autowired
MyService myService;
#RequestMapping(value = "/makeAction",method = RequestMethod.POST)
public String makeLoan(){
return myService.makeAction(...);
}
}
The question is: how to bypass multiple variables via adressbar like:
localhost:8080/makeAction?loanId=1#clientName=Stive#clientSurname=Wassabi
and so on.
UPD: Another attempt failed:
#RequestMapping(value="/makeLoan",method = RequestMethod.GET)
public String makeLoan(#PathVariable("loan_id")int loan_id,
#PathVariable("name") String clientName,
#PathVariable("surname") String clientSurname,
#PathVariable("amount") double amount,
#PathVariable("days") int days ) throws Exception {
return myService.makeLoan(loan_id,clientName,clientSurname,amount,days);
P.S tried #PathVariables - failed to use
Thanks you all for helping me with this
The final code looks like that:
#RequestMapping(value = "/makeAction")
public String makeLoan(#RequestParam("loan_id")int loan_id,
#RequestParam("clientName")String clientName,
#RequestParam("clientSurname")String clientSurname,
#RequestParam("amount")double amount,
#RequestParam("days")int days ) throws Exception {
return loanService.makeAction(loan_id,clientName,clientSurname,amount,days);
}
I had to remove GET/POST method and switch #PathVariable to #RequestParam
Well, first of all, you shouldn't put parameters for POST in the URL.
URL parameters are used for GET, and they are separated with & so in your case:
localhost:8080/makeAction?loanId=1&clientName=Stive&clientSurname=Wassabi
For POST you should submit parameters as request body parameters. Parameters are bound with #RequestParam annotation like #SMA suggested.
In your method define them with RequestParam annotation like:
public String makeLoan(#RequestParam(value="clientName", required=false) String clientName) {//and others, and hope you meant & to seperate request parameters.
}
Well, assuming you're using spring MVC, this could be helpful:
How to explictely obtain post data in Spring MVC?
Be aware that if you're using a POST method, your parameters should be read in the request body...

Resources