I've got a Spring #RequestMapping with a couple of #PathVariables, and the first one is necessary to narrow down to the second one - you can see in the example below that I need to get the Department in order to get the Module. Using plain String #PathVariables I can do it like this:
#RequestMapping("/admin/{dept}/{mod}/")
public String showModule(#PathVariable String dept, #PathVariable String mod) {
Department department = dao.findDepartment(dept);
Module module = department.findModule(mod);
return "view";
}
But I'm keen to use Spring's Converter API to be able to specify the Department directly as the #PathVariable. So this works after I've registered a custom Converter class:
#RequestMapping("/admin/{dept}/")
public String showDept(#PathVariable Department dept) {
return "view";
}
But the Converter API doesn't give access outside of the single argument being converted, so it's not possible to implement the Converter for Module. Is there another API I can use? I'm eyeing up HandlerMethodArgumentResolver - has anyone solved a problem like this, or are you sticking to String #PathVariables?
I'm using Spring 3.1.
I haven't done it like this but one way I thought of was to make a separate converter for the both of them:
#RequestMapping("/admin/{deptAndModule}/")
public String showDept(#PathVariable DepartmentAndModule deptAndModule) {
return "view";
}
And have the converter able to take an input of the form "deptid-modid" e.g. "ch-c104". It wouldn't be possible to separate them with a slash as the request wouldn't match the RequestMapping pattern of /admin/*/.
In my case, the requirements have changed slightly so that module codes are fully unique and don't need to be scoped to department. So I don't need to do this any more. If I did, I would probably eschew the automatic Module conversion and do it manually in the method.
Related
Old, one and only method in controller.
// Cancel all shipments for specific item (beacuse from today selling companyX product is forbidden)
#PostMapping("/items/shipments/stop")
public List<String> stopShipmentsForItem(#RequestBody List<UUID> itemsUuids) {
return itemShippingService.stopShipment(itemsUuids);
}
Propose endpoint which stops specific shipping for specific item
// Extending existing one with query string
#PostMapping("/items/shipments/stop")
public List<String> stopShipmentsForItem(#RequestBody List<UUID> itemsUuids, #RequestParam(required = false) UUID shipmentUuid) {
return itemShippingService.stopShipment(itemsUuids, shipmentUuid);
}
OR
// Creating new one with path variables
#PostMapping("/items/{itemUuid}/shipments/{shipmentUuid}/stop")
public String stopShipmentForItem(#PathVariable UUID itemsUuid, #PathVariable UUID shipmentUuid) {
return itemShippingService.stopShipment(itemsUuids, shipmentUuid);
}
One opinion - First option equals less code, it supports Open for extensions from SOLID, query string should be use for filtering and UUID shipmentUuid underneath in service is used for filtering purposes.
Second opinion - Second option is more reasonable as those are different actions and should be separated and be responsible for one type of action and as with i.e. getting resources from DB you probably will use different endpoints for one resource and all resources
Which approach do you think is better? - keep in mind that old endpoint should still works as previously after potential modification.
Do you have any sources to prove that your opinion is correct?
I try to build a rest api with spring and face some issues. My original api is built with express on node and saw that some stuff I am pretty used to seem more complicated in spring.
For example, I have the following case, I could borrow the "controller" for /tasks even from the UserController.
/users
/users/:id
/users/:id/tasks
/tasks
Or I can easily inherit routes, my delegating them down. Spring doesn't seem to have something like that, where I could reference an already existing controller. It even seems to me that the RequestMapping value becomes long.
Is there something similar in Spring like what express can? Because I couldn't find any large spring mvc rest projects to illustrate that
You could add multiple values for #RequestMapping and also use Path Variables that fits your needs. For example,
#RequestMapping(value = "/users/{id}/tasks", method = GET)
public String getUserTaksFromIdPathVariable(#PathVariable("id") long id) {
return "Get all tasks from user with id=" + id;
}
And as I said, you can have multiple values:
#RequestMapping({"/tasks", "/users/{id}/tasks"}, method = GET)
public String getTasks(#PathVariable("id") Optional<long> id) {
return "whatever";
}
Is there a way to map a query parameter with a middle score using requests in spring?
I have no problem binding single worded parameters doing this:
Uri example: http://localhost:8080/test/?product=hotels
public class CitiesRequest{
private ProductType product;
public ProductType getProduct() {
return this.product;
}
public void setProduct(String product) {
this.product = product;
}
}
But I'd like to be able to receive parameters like this:
http://localhost:8080/test/?product-type=hotels
As Misha stated it is syntactically incorrect to have a variable name with a hyphen in Java. But Spring is fine with that and allows you to specify a parameter name (in the request) different from the variable name (in java code). For exemple, when using RequestMapping driven controller, one can write :
#RequestMapping("/test")
public ModelAndView getProduct(
#RequestParam("product-type") String productType) {
...
}
That way, getProduct will be called for a url like http://localhost/test?product-type=hotels and the parameter productTypewill receive the value hotels. And all is still purely declarative.
By default, Spring maps the query parameter key to the name of the Java variable. However, it's syntactically incorrect to have a variable name with a hyphen in Java, which explains why you're finding it particularly difficult to get Spring to set the parameter's value for you.
One workaround that might work is to just have a Map<String, String[]> parameter to represent all of the parameters. Then Spring doesn't have to map any query parameters to variable names, so the hyphenated name might end up in that map of all parameters. It may not be as comfortable as pre-split parameter objects, but it might get the hyphenated keys.
Another solution might be to configure the WebDataBinder, which controls how data from HTTP requests are mapped onto your controller's request parameters. But that's a whole can of worms, especially if you're just starting out with Spring. You can read more about it in the documentation under "data binding".
Is there a way to tell Spring to map request to different method by the type of path variable, if they are in the same place of the uri?
For example,
#RequestMapping("/path/{foo}")
#RequestMapping("/path/{id}")
if foo is supposed to be string, id is int, is it possible to map correctly instead of looking into the request URI?
According to the spring docs it is possible to use regex for path variables, here's the example from the docs:
#RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
public void handle(#PathVariable String version, #PathVariable String extension) {
// ...
}
}
(example taken from http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-patterns )
Judging from that, it should be possible to write something like this for your situation:
#RequestMapping("/path/{foo:[a-z]+}")
#RequestMapping("/path/{id:[0-9]+}")
I'm working on an HttpServlet and trying to define a url-pattern with a wildcard, but not finding much documentation.
The path I want to capture is "resource/{id}/action"
I've tried my annotation as:
#WebServlet("/resource/*/action")
but this doesn't match, though the more basic "resource/*" works okay.
Also, is there any way I can automatically pull out my {id} wildcard, rather than having to parse the url manually?
I'm think you try to solve wrong task. It's really unusual decision to map servlet on wildcard like this. Take a look on Spring MVC framework there you can write methods like this
#RequestMapping("/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(#PathVariable String ownerId, #PathVariable String petId, Model model) {
Owner owner = ownerService.findOwner(ownderId);
Pet pet = owner.getPet(petId);
model.addAttribute("pet", pet);
return "displayPet";
}