Is this the right way to have multiple parameters for a REST API ?
#GET
#Path("/id/{userId,type,date}")
#Nullable
#Produces(MediaType.APPLICATION_JSON)
List<Exercise> findExercises(
#ApiParam( value = "User ID", required=true) #PathParam("userId") Long userId,
#ApiParam( value = "Type") #PathParam("type") String type,
#ApiParam( value = "Date") #PathParam("date") String date);
If not, how can i accomplish that?
I guess this is the right way :
#GET
#Path("/id/{userId}/{type}/{date}")
#Nullable
#Produces(MediaType.APPLICATION_JSON)
List<Exercise> findExercises(
#PathParam("userId") Long userId,
#PathParam("type") String type,
#PathParam("date") String date);
You should separate the path params as follows:
#Path("/id/{userId}/{type}/{date}")
Related
I'm using validators in my spring controllers. If #RequestParam is required there is no problem, i can check String with #NotBlank. But if #RequestParam is optional, i can't use it with #NotBlank, because this parameter is optional and sometimes can be null.
I want to validate #NotBlank if String is not null. Is there any constraint help me?
#RequestParam #NotBlank String name
working perfectly. I have problem with required=false
if client don't send optional description parameter, validation fails.
#PatchMapping("/role/{id}")
public ResponseEntity<?> updateRole(HttpServletRequest request, #PathVariable #Positive Integer id,
#RequestParam #NotBlank String name,
#RequestParam(required = false) #NotBlank String description)
I want to validate #NotBlank if description is not null.
`#RequestParam(required = false) #NotBlank String description`
If i use like that, i got "Input validation failed!".
You need to add a custom validator for this.
Interface
#Documented
#Constraint(validatedBy = YourValidator.class)
#Target({ ElementType.METHOD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface NotBlankIfPresent{
String message() default "Error MEssage";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Your Validator Class
public class YourValidator implements ConstraintValidator<NotBlankIfPresent, String> {
#Override
public boolean isValid(String s, ConstraintValidatorContext context) {
if (s == null) {
return true;
}
return !s.isBlank();
}
}
This is not right approach to validate #RequestParam. You have to validate in your code
if it is blank then throw new IllegalArgumentException("{\"error\":\"The parameter is invalid\"}"
There is no point in using #RequestParam(required = false) and #NotBlank together.
Here is how the #NotBlank annotation works.
a String field constrained with #NotBlank must be not null and the trimmed length must be greater than zero.
Maybe a workaround could be using a default value in your request param variable whenever you have required = false
Example:
#PatchMapping("/role/{id}")
public ResponseEntity<?> updateRole(HttpServletRequest request, #PathVariable
#Positive Integer id, #RequestParam #NotBlank String name,
#RequestParam(required = false, defaultValue = "adefaultvalue") #NotBlank String description) {
if(description.equals("adefaultvalue") {
// that means that the user did not send any value for this variable so you can
// add your validation logic here
}
}
Please have in mind that the above code has not been tested
I have a requirement in which I need to map the request body to a particular child class based on the path variable. The request body does not contain by itself any information as to which child class to pick.
#ApiOperation(value = "Update Developer (dev)",
response = ResponseEntity.class)
#RequestMapping(method = RequestMethod.PATCH,
value = "/{type}")
public ResponseEntity<Response> updateDeveloper(
#PathVariable String type,
#RequestParam("year") String year,
#RequestBody Developer employeeUpdate,
) { .....}
#ApiOperation(value = "Update Manager (manager)",
response = ResponseEntity.class)
#RequestMapping(method = RequestMethod.PATCH,
value = "/{type}")
public ResponseEntity<Response> updateManager(
#PathVariable String type,
#RequestParam("year") String year,
#RequestBody Manager employeeUpdate,
) { .....}
Where Developer and Manager extends abstract class Employee.
I tried by having only one method like below:
#ApiOperation(value = "Update Employee (manager, dev)",
response = ResponseEntity.class)
#RequestMapping(method = RequestMethod.PATCH,
value = "/{type}")
public ResponseEntity<Response> updateEmployee(
#PathVariable String type,
#RequestParam("year") String year,
#RequestBody Employee employeeUpdate,
) { .....}
But spring is unable to instantiate the Employee instance as its abstract.
Is my design incorrect? I do prefer to have a solution which doesn't require to modify the Employee/Developer/Manager classes.
Thanks in advance!!
By REST compliance, you should identify your resource:
#RequestMapping(method = RequestMethod.PATCH, value = "/developer")
#RequestMapping(method = RequestMethod.PATCH, value = "/manager")
If you still want to post a body randomly (which either a developer or a manager), you a combined DTO of Manager and Developer:
public class EmployeeDTO {
private int type; // Developer or manager
// All of properties of Manager and Developer
}
By inspecting type you are able to delegate to correct service method.
Thank you all for looking into this. I have found a way that suits my situation:
#ApiOperation(value = "Update Developer (dev)",
response = ResponseEntity.class)
#RequestMapping(method = RequestMethod.PATCH,
value = "/{type:developer}")
public ResponseEntity<Response> updateDeveloper(
#PathVariable String type,
#RequestParam("year") String year,
#RequestBody Developer employeeUpdate,
) { .....}
#ApiOperation(value = "Update Manager (manager)",
response = ResponseEntity.class)
#RequestMapping(method = RequestMethod.PATCH,
value = "/{type:manager}")
public ResponseEntity<Response> updateManager(
#PathVariable String type,
#RequestParam("year") String year,
#RequestBody Manager employeeUpdate,
) { .....}
With this I will not get any instantiation error as well, when trying to have single method and data type of request body is set as the parent.
I have to write an API call
#GET
#Path("/{settingName1, settingName2}")
public Response getNetworkSettingValue(#ApiParam(value = "Name") #QueryParam("name") String name,
#ApiParam(value = "City") #QueryParam("city") String city,
#ApiParam(value = "State") #QueryParam("state") String state) {}
here my doubt is how to get settingName1 & settingName2 values,
can write like
#ApiParam(value = "SettingName1") #PathParam("settingName1") String settingName1
or
#ApiParam(value = "SettingName1") #PathVariable("settingName1") String settingName1
in method declaration.
or
any other way to get those two values
By the #Path annotation, I assume you are using JAX-RS (Jersey, RESTEasy, etc). So it should be:
#ApiParam(value = "SettingName1") #PathParam("settingName1") String settingName1
If you were using Spring, it should be:
#ApiParam(value = "SettingName1") #PathVariable("settingName1") String settingName1
your annotations are mixed up with spring and swagger.
If u want to accesss pathvariables with spring than it have to be like
#RequestMapping(value = "/{settingName1}/{settingName2}", method = equestMethod.GET)
public Response getNetworkSettingValue(#ApiParam(value = "settingName1") #PathVariable final String settingName1,
#ApiParam(value = "settingName2") #PathVariable final String settingName2) {
...
return new Response();
}
I cannot seem to get this to work for me ,I have seen this in other posts and was hoping someone may be able to spot what im doing wrong.I am trying to get the body of a request to this rest api but cannot seem to pull back what i need and just get null in the string below.
#POST
#Path("/SetFeeds")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public String setFeed(#PathParam("name")String name2, #QueryParam("name") String name,#Context UriInfo uriInfo){
MultivaluedMap<String,String> queryParams = uriInfo.getQueryParameters();
String query = uriInfo.getRequestUri().getQuery();
String response = queryParams.getFirst("name");
return response;
}
A method parameter to accept the body of the request should not be annotated with anything (except in few cases like individual form params and multipart). So to get the raw JSON, you could simply add a String parameter
public String setFeed(#PathParam("name")String name2,
#QueryParam("name") String name,
#Context UriInfo uriInfo,
String jsonBody){
Or if you want to do the more common thing and use POJOs (so you don't need to parse the JSON yourself), you should look at this answer
Great answer, but I would like to add that you can use an object instead of a String and the Jackson of REST will take care the transformation without any further definition.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public String setFeed(#PathParam("name")String name2,
#QueryParam("name") String name,
MyJson json,
#Context UriInfo uriInfo){
MultivaluedMap<String,String> queryParams = uriInfo.getQueryParameters();
String query = uriInfo.getRequestUri().getQuery();
String response = queryParams.getFirst("name");
return response;
and a pojo as a json object:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyJson{
private String name;
public MyJson(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Lets say I have a Category domain model object that follows a file tree structure. I want to be able to construct a RequestMapping annotation for the controller method so that
/category/art/macros
/category/people/weddings/2014/5-19
/category/sports/college/baseball/2014/5-19
can be handled by the minimum number of controller methods.
I already have one controller method defined:
#RequestMapping(value ={"/category/{category}"}, method = RequestMethod.GET)
public String adminCategory(ModelMap model, #PathVariable(value = "category") String category){
model.addAttribute("message", category);
return "gallery";
}
This works for a single URL like
/category/sports
How can I adapt this to be more flexible?
The challenge is here that you can't make #PathVariable optional but you can have two or more controller methods which can call the same service code. So, for you three URL patterns you have to define three different controllers:
GET: /category/art/macros
#RequestMapping(value ={"/category/{category}"}, method = RequestMethod.GET)
public String adminCategory(ModelMap model, #PathVariable(value = "category") String category){
model.addAttribute("message", category);
return "gallery";
}
GET: /category/people/weddings/2014/5-19
#RequestMapping(value ={"/category/{category}/{subcategory}/{year}/{date}"}, method = RequestMethod.GET)
public String adminCategory(ModelMap model, #PathVariable(value = "category") String category,
#PathVariable(value = "subcategory") String subcategory,
#PathVariable(value = "year") String year,
#PathVariable(value = "date") String date
){
model.addAttribute("message", category, subcategory, year, date);
return "gallery";
}
GET: /category/sports/college/baseball/2014/5-19
#RequestMapping(value ={"/category/{category}/{subcategory}/{year}/{date}"}, method = RequestMethod.GET)
public String adminCategory(ModelMap model, #PathVariable(value = "category") String category,
#PathVariable(value = "subcategory") String subcategory,
#PathVariable(value = "sub_sub_category") String sub_sub_category,
#PathVariable(value = "year") String year,
#PathVariable(value = "date") String date
){
model.addAttribute("message", category, subcategory, sub_sub_category, year, date);
return "gallery";
}
PS You can user #RequestParam which can be optional and reduce the number of controllers.