How to add multiple RestController endpoints differed by query-params? - java

How can I simply redirect a url if a specific query parameter is missing?
#RestController
public class PersonController {
//only in case the "sort" query parameter is missing
#GetMapping("/persons")
public String unsorted() {
return "redirect:/persons?sort=name";
}
//only in case the "sort" query parameter exists
#GetMapping("/persons")
public String sorted() {
//...
}
}

Use #RequestParam to extract query parameters
Add parameter for #RequestParam: value, defaultValue, required
with java >= 8:
#RestController
public class PersonController {
#GetMapping("/persons")
public String personList(#RequestParam(value = "sort", defaultValue = "name") Optional<String> sort) {
//handling process here
}
}
with java < 8:
#RestController
public class PersonController {
#GetMapping("/persons")
public String personList(#RequestParam(value = "sort", defaultValue = "name", required=false) String sort) {
//handling process here
}
}

You could use #GetMapping.params
#GetMapping(value = "/persons", params = "sort")
public String sorted() {

You can use the params element. One mapping will supports params="sort" for when the sort parameter is present and the other params="!sort" for when it is missing.
However, you may want to consider using a default value instead of performing a redirect. What benefit does the redirect provide? It will require the server respond and then and have the client make a second HTTP request.
Using params
#RestController
public class PersonController {
//only in case the "sort" query parameter is missing
#GetMapping(value = "/persons", params = "!sort")
public String unsorted() {
return "redirect:/persons?sort=name";
}
//only in case the "sort" query parameter exists
#GetMapping(value = "/persons", params = "sort")
public String sorted() {
//...
}
}
Using default value
#RestController
public class PersonController {
//only in case the "sort" query parameter exists
#GetMapping("/persons")
public String sorted(
#RequestParam(name = "sort", defaultValue = "name") String sort)
{
//...
}
}

You can set a default value:
#RestController
public class PersonController {
//only in case the "sort" query parameter is missing
#GetMapping("/persons")
public String unsorted(#RequestParam(value = "sort", defaultValue = "name") String name) {
// do logic
}
}

You can also set the default value for the missing value and continue forward
#RequestParam(value = "sort", defaultValue = "name") String name

Related

#DefaultValue return empty string

I get request like
http://localhost:8080?sortBy=&sortDir=&page=10
javax.ws.rs.DefaultValue understand
My controller looks like
public class MyController {
public Response test(#BeanParam Pageable params){
...
}
}
#Data
public class Pageable {
#QueryParam("sortBy")
#DefaultValue("1")
private String sortBy;
#QueryParam("sortDir")
#DefaultValue("ASC")
private String sortDir;
private int page;
}
And under the hood you can find, that java check if parameter == null it put my default value, but in my request I have "" - empty string.
How to workaround situation with DefaultValue?
Can simply assign default value like below :
#Data
public class Pageable {
#QueryParam("sortBy")
private String sortBy = "1";
#QueryParam("sortDir")
private String sortDir = "ASC";
private int page;
}

Spring #RequestMapping with optional parameters when using custom request object

Hello I want to make an optional parameter on my controller, id that so far looks like this
#ApiOperation(value = "get runs by date in order to paginate")
#ApiResponses({ #ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 500, message = "Unexpected failure") })
#RequestMapping(value = "/time/{date}/to/{toPage}/from/{fromPage}",
params = {"pageSize", "id"},
method = RequestMethod.GET)
public RunCollection getPolicyByDate(GetPolicyByDateRequest request) {
return serviceImpl.getListOfRunsByDate(request);
}
But this means the parameter is required. I want it to be optional. I saw this answer where they use #RequestParam Spring #RequestMapping with optional parameters but I want to include it in my GetPolicyByDateRequest object. Is this possible? The spring docs don't allude to turning off the required attribute. Could I possibly use Optional
2 options :
If your GetPolicyByDateRequest has not primitive attribute, they should be inserted by Spring in your object. If your parameter is not set (i.e there is no pageSize in your url) then Spring injects NULL in your attribute.
public class GetPolicyByDateRequest {
private String date;
private int toPage;
private int fromPage;
private Integer pageSize;
private Integer id;
public GetPolicyByDateRequest() {
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public int getToPage() {
return toPage;
}
public void setToPage(int toPage) {
this.toPage = toPage;
}
public int getFromPage() {
return fromPage;
}
public void setFromPage(int fromPage) {
this.fromPage = fromPage;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String toString() {
return "MyRequest{" +
"date='" + date + '\'' +
", toPage=" + toPage +
", fromPage=" + fromPage +
", pageSize=" + pageSize +
", id=" + id +
'}';
}
}
Weirdly, I think it is some king of bug in Spring, even when using Optional you get a null object if the parameter in the requestParam is not provided.
If you want to get all parameters, you need to use #PathVariable with #RequestParam in your case. Here is how to get your parameters :
#RequestMapping(value = "/time/{date}/to/{toPage}/from/{fromPage}", method = RequestMethod.GET)
public RunCollection getPolicyByDate(#PathVariable String date, #PathVariable int toPage, #PathVariable int fromPage, #RequestParam(required = false) Optional<Integer> pageSize, #RequestParam(required = false) Optional<Integer> id) {
// then use your parameters the way you to use then.
}
Please note :
#PathVariable retrieves the parameter in the URL, where the value is between {}, like {date} -> #PathVariable String date
#RequestParam retrieves the parameter after the ? in the URL. ?pageSize=10 -> #RequestParam int pageSize.
Both of this annotation takes as parameters, among others, required=true|false. When do mark a parameter as not required, make sure you don't use a primitive type, like int, otherwise, you can set null to this object, and your code will fail at runtime. This is the reason why I have used Optional<Integer>, to allow the type. But Spring is clever, and if it detects the Optional in your parameter, then you are forced to use required=false in the #RequestParam.
If you wanted to populate your GetPolicyByDateRequest, then you should have done a POST mapping, and use the annotation #RequestBody GetPolicyByDateRequest request, and send a JSON body in your request to make Spring (actually Jackson) translate your JSON into a GetPolicyByDateRequest object.
Hope it helps

boolean is set to false if it's not present in #RequestBody

I've stumbled upon interesting case and I'm not sure how to resolve it. It's probably related to JSON Post request for boolean field sends false by default but advices from that article didn't help.
Let's say I have this class:
public class ReqBody {
#NotNull
#Pattern(regexp = "^[0-9]{10}$")
private String phone;
//other fields
#NotNull
#JsonProperty(value = "create_anonymous_account")
private Boolean createAnonymousAccount = null;
//constructors, getters and setters
public Boolean getCreateAnonymousAccount() {
return createAnonymousAccount;
}
public void setCreateAnonymousAccount(Boolean createAnonymousAccount) {
this.createAnonymousAccount = createAnonymousAccount;
}
}
I also have endpoint:
#PostMapping(value = "/test", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyOutput> test(
#ApiParam(value = "information", required = true) #RequestBody ReqBody input
) {
//do something
}
problem is when I send my request body as:
{
"phone": "0000000006",
"create_anonymous_account": null
}
or just like
{
"phone": "0000000006"
}
it sets createAnonymousAccount to false.
I have checked, and it correctly recognises "create_anonymous_account": true
Is there any way to "force" null value in boolean field?
I really need to know if it was sent or no, and not to have default value.
You can use Jackson annotation to ignore the null fields. If the Caller doesn't send createAnonymousAccount then it will be null.
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ReqBody {
#NotNull
#Pattern(regexp = "^[0-9]{10}$")
private String phone;
//other fields
#JsonProperty(value = "create_anonymous_account")
private Boolean createAnonymousAccount ;
}

Spring map GET request parameters to POJO automatically

I have method in my REST controller that contains a lot of parameters. For example:
#RequestMapping(value = "/getItem", method = RequestMethod.GET)
public ServiceRequest<List<SomeModel>> getClaimStatuses(
#RequestParam(value = "param1", required = true) List<String> param1,
#RequestParam(value = "param2", required = false) String param2,
#RequestParam(value = "param3", required = false) List<String> param3,
#RequestParam(value = "param4", required = false) List<String> param4,
#RequestParam(value = "param5", required = false) List<String> param5) {
// ......
}
and I would like to map all GET request parameters to a POJO object like:
public class RequestParamsModel {
public RequestParamsModel() {
}
public List<String> param1;
public String param2;
public List<String> param3;
public String param4;
public String param5;
}
I need something like we can do using #RequestBody in REST Controller.
Is it possible to do in Spring 3.x ?
Thanks!
Possible and easy, make sure that your bean has proper accessors for the fields. You can add proper validation per property, just make sure that you have the proper jars in place. In terms of code it would be something like
import javax.validation.constraints.NotNull;
public class RequestParamsModel {
public RequestParamsModel() {}
private List<String> param1;
private String param2;
private List<String> param3;
private String param4;
private String param5;
#NotNull
public List<String> getParam1() {
return param1;
}
// ...
}
The controller method would be:
import javax.validation.Valid;
#RequestMapping(value = "/getItem", method = RequestMethod.GET)
public ServiceRequest<List<SomeModel>> getClaimStatuses(#Valid RequestParamsModel model) {
// ...
}
And the request, something like:
/getItem?param1=list1,list2&param2=ok
Are you trying to do
#RequestMapping(value = "/getItem", method = RequestMethod.GET)
public ServiceRequest<List<SomeModel>> getClaimStatuses(#ModelAttribute RequestParamsModel requestParamModel) {
...
}

Spring REST-ful uri with optional querystring

The normal uri which triggers the default controller to get all cars is just "/cars"
I want to be able to search for cars aswell with an uri, for example: "/cars?model=xyz" which would return a list of matching cars. All the request parameters should be optional.
The problem is that even with the querystring the default controller triggers anyway and I always get "all cars: ..."
Is there a way to do this with Spring without a separate search uri (like "/cars/search?..")?
code:
#Controller
#RequestMapping("/cars")
public class CarController {
#Autowired
private CarDao carDao;
#RequestMapping(method = RequestMethod.GET, value = "?")
public final #ResponseBody String find(
#RequestParam(value = "reg", required = false) String reg,
#RequestParam(value = "model", required = false) String model
)
{
Car searchForCar = new Car();
searchForCar.setModel(model);
searchForCar.setReg(reg);
return "found: " + carDao.findCar(searchForCar).toString();
}
#RequestMapping(method = RequestMethod.GET)
public final #ResponseBody String getAll() {
return "all cars: " + carDao.getAllCars().toString();
}
}
You can use
#RequestMapping(method = RequestMethod.GET, params = {/* string array of params required */})
public final #ResponseBody String find(#RequestParam(value = "reg") String reg, #RequestParam(value = "model") String model)
// logic
}
ie, the #RequestMapping annotation has a property called params. If all of the parameters that you specify are contained in your request (and all other RequestMapping requirements match), then that method will be called.
Try a variation of this:
#Controller
#RequestMapping("/cars")
public clas CarController
{
#RequestMapping(method = RequestMethod.get)
public final #ResponseBody String carsHandler(
final WebRequest webRequest)
{
String parameter = webRequest.getParameter("blammy");
if (parameter == null)
{
return getAll();
}
else
{
return findCar(webRequest);
}
}
}

Categories

Resources