I want to create a Spring controller where I retrieve 4 params: Id, Name, Description and Qty but I need only take Id or Name, if user send both is wrong, How Can I do this?
Im trying followgin:
#GetMapping(value="/purchase")
public model purchase(#Valid #PathVariable String Id,
#Valid #PathVariable String Name){
...
}
You can ensure in code, that only one parameter (id or name, but not both) is sent to your method. Instead of path variables you should use query parameters in your GET method. So a solution could look like this:
#GetMapping("/purchase")
public ResponseEntity<Object> getItemByName(#RequestParam(required = false) String name,
#RequestParam(required = false) String id) {
if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(id))
throw new IllegalArgumentException("Only on parameter allowed.");
...
Then you can test you method with calls like these:
http://localhost:8080/purchase?name=ball&id=123
http://localhost:8080/purchase?id=123
In my German blog I wrote an article about how to create RestController in Spring and how you can handle parameters, this might give you more background information:
https://agile-coding.blogspot.com/2020/10/rest-json-apis-in-java-leicht-gemacht.html
Related
I want to add a list of items to a Spring Model. It sometimes throws ConversionFailedException. The method in question:
private void addAuthoritiesToModel(Model model){
List<Authority> allAuthorities = Arrays.asList(
Authority.of("ROLE_ADMIN","Admin"),
Authority.of("ROLE_USER","User")
);
model.addAttribute("allAuthorities",allAuthorities);
}
The method throws on the last line. The curious thing it only does so, when called from a particular method and not others. For example, it works fine here:
#GetMapping("/users/new")
public String newUserForm(Model model){
model.addAttribute("user",User.blank());
model.addAttribute("newUser",true);
addAuthoritiesToModel(model);
return "user_details";
}
But it blows here:
#PostMapping(value = {"/users","/profile","/users/{any}"})
public String postUser(#Valid #ModelAttribute("user") User user,
BindingResult bindingResult,
#RequestParam("newPassword") Optional<String> newPassword,
#RequestParam("confirmPassword") Optional<String> confirmPassword,
RedirectAttributes redirectAttributes,
#PathVariable("any") String pathVariable
){
if(bindingResult.hasErrors()) {
if(user.getId()==null)
redirectAttributes.addAttribute("newUser",true);
addAuthoritiesToModel(redirectAttributes);
return "user_details";
}
...
}
I have tried exchanging Arrays.asList to another List implementation, but that doesn't solve the problem. And it wouldn't explain, why it doesn't work in the first case.
There is difference between Model and RedirectAttributes.
The values in RedirectAttributes are getting formatted as String.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/support/RedirectAttributes.html
A specialization of the Model interface that controllers can use to select attributes for a redirect scenario. Since the intent of adding redirect attributes is very explicit -- i.e. to be used for a redirect URL, attribute values may be formatted as Strings and stored that way to make them eligible to be appended to the query string or expanded as URI variables in org.springframework.web.servlet.view.RedirectView.
You should not use unless required for redirecting and in such case should be string values.
I have a view that is rendered with this method:
#RequestMapping("/employee/{id}")
public String showSpecificEmployee(#PathVariable String id, Model model){
model.addAttribute("employee", employeeService.findEmployeeById(new Long(id)));
DateCommand dateCommand = new DateCommand();
dateCommand.setEmployeeId(new Long(id));
model.addAttribute("date", dateCommand);
return "specificEmployee";
}
The view displayes some basic information about the Employee. On the same view, I do have a form to choose a month and filter the information by Date.
After the Date is chosen, I would like to have the view 'refreshed' with updated information. That means I do have a POST & GET methods bound to the same view.
#RequestMapping("/passdate")
public String updateWorkmonth(#ModelAttribute DateCommand dateCommand, Model model){
model.addAttribute("employee", employeeService.findEmployeeWithFilteredWorkdaysAndPayments(dateCommand.getEmployeeId(), dateCommand.getActualDate()));
model.addAttribute("date", dateCommand);
return "specificEmployee";
}
After the second method is invoked looks like
http://localhost:8080/passdate?employeeId=1&actualDate=2018-02, but I want it to be /employee/{id}. How do I combine those 2 methods, so they point to the same URL?
If I set #RequestMapping("/employee/{id}") on both methods, I keep getting an error.
You actually need only one GET method
#RequestMapping("/employee/{id}")
and optionally passed
#RequestParam("actualDate")
You can redirect user to that url. Just replace in method updateWorkmonth one line
return "specificEmployee";
with
return "redirect:/employee/" + dateCommand.getEmployeeId();
You can specify the type of HTTP request you want in the #RequestMapping parametters
When you don't specify it, it uses GET by default
#RequestMapping(value = "/employee/{id}",method = RequestMethod.POST)
For example I have a bean
public class Order
{
int orderID;
String name;
}
And I have a POST operation
#ApiOperation(value = "Insert a new order", response = Order.class)
#RequestMapping(value = "/addOrder", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public Order addOrder(#Valid #RequestBody Order order)
{
//Set random id here
order.id = 'xxxxx';
Order o = orderService.insertOrder(order);
return o;
}
And in Swagger I have the following:
So my question is, how do I hide id on POST but show ID on GET?
Or should I add a description saying that even if you choose to add an ID it wont do anything and just return my random id? Just like in Kubernetes (uid)
And properties like read-only in #ApiModelProperty will solve anything?
A simple approach is to split your bean in two - one for creating a new object, and another one which extends that for data about an existing object.
e.g.
public class IncompleteOrder {
String name;
}
public class ExistingOrder extends IncompleteOrder {
int id;
}
Then have your POST method take an object of IncompleteOrder and return one of ExistingOrder. I'd also delegrate responsibility for assigning a random order id to the underlying service...
public ExistingOrder addOrder(#Valid #RequestBody IncompleteOrder order) {
ExistingOrder o = orderService.insertOrder(order);
return o;
}
The same thing could be achieved by having two completely separate classes with no inheritance relationship, which would probably be appropriate if there was a significant divergence between the information needed to create a new order from the information which is on an existing order.
An alternative is to ask what the id is actually for - why are your clients getting integer id's for anything? Ideally, if they want any information about the order they should be querying the API for the resource, and to do that they need the URI of the order rather than the integer id. So external services communicating about an order should be passing the URIs back and forth rather than ids. Perhaps you could encourage your clients to communicate with each via the URI you return in the Location header from your POST request? Then you could do away with exposing the id on your response and have a purely symmetric request / response body.
I am trying to reload the same page with different content that varies depending on which link is clicked on the page. The url pattern for the page is "/owners", which triggers the running of this method in OwnerController.java:
#RequestMapping(value = "/owners", method = RequestMethod.GET)
public String processFindForm(Owner owner, BindingResult result, Map<String, Object> model) {
Collection<Owner> results = this.clinicService.findOwnerByLastName("");
model.put("selections", results);
model.put("sel_owner",this.clinicService.findOwnerById(ownerId));//ownerId is not defined yet
return "owners/ownersList";
}
The jsp includes a dynamically generated list of ownerId integer values, each of which can be used to send a unique ownerId back to the server. What do I need to add to my jsp in order to get ownerId to have a user-specified value when processFindForm() is run? In other words, what does the hyperlink need to look like?
You are using GET request type.If you want to send any parameter to controller then you have to use either #RequestParam or #PathParam annotations based on requirement as an arguments to controller method. In your case it will be something like...
public String processFindForm(#RequestParam("ownerID") String ownerId, BindingResult result, Map<String, Object> model) {... }
Take a look on this link as well: http://www.byteslounge.com/tutorials/spring-mvc-requestmapping-example
Hi I need to modify an existent Rest interface. The old Rest interface only take a phone number then look for a record. The phone number used to be unique with the table. Now is the combination of the phone and a number (batchid). So i need to modify the service impl and the client that calls it. Here is the old controller:
#RequestMapping(value="/{phone}", method = RequestMethod.GET)
#ResponseBody
public BatchDetail findByPhone(#PathVariable String phone) {
return batchDetailService.findByPhone(phone);
}
and here is the how the old client is accesing it:
private static final String URL_GET_BATCHDETAIL_PHONE = "http://localhost:8080/Web2Ivr/restful/batchdetail/{phone}";
batchdetail = restTemplate.getForObject(URL_GET_BATCHDETAIL_PHONE, BatchDetail.class, phone);
batchdetail.setStatus("OK");
restTemplate.put(URL_TO_UPDATE_BATCHDETAIL, batchdetail, batchdetail.getId())
;
So my question will be how to modify the controller and the restemplate's client call to support two variables, phone and the number(batchid) something like:
http://localhost:8080/Web2Ivr/restful/batchdetail/{batchid}/{phone}
#RequestMapping(value="/{batchid}/{phone}", method = RequestMethod.GET)
#ResponseBody
public BatchDetail findByPhone(#PathVariable String phone, #PathVariable String batchid) {
return batchDetailService.findByPhone(phone);
}