Limit to number of arguments in a controller method in NinjaFramework? - java

I'm using NinjaFramework 6.0, tried adding a 10th #Param to my controller method and now I get "No suitable method found for with(Controller[...]Index)" error compiling the Routes.java.
My method looks like this:
public Result personIndex(
#Param("ssn") Optional<String> ssn,
#Param("dodId") Optional<String> dodId,
#Param("firstName") Optional<String> firstName,
#Param("middleName") Optional<String> middleName,
#Param("lastName") Optional<String> lastName,
#Param("birthday") Optional<String> birthday,
#Param("branch") Optional<String> branch,
#Param("rateRank") Optional<String> rateRank,
#Param("status") Optional<String> status,
#Param("page") Optional<Integer> page) { ... }
If I remove one of the params then everything will compile and work. Is this a hard limit? Should I encapuslate these into some sort of form / dto object?
--
This appears to be a problem when using this type of Route configuration:
router.GET().route("/persons").with(PersonController::personIndex);
If I switch to the 'old' way it works fine:
router.GET().route("/persons").with(PersonController.class, "personIndex");

While browsing Ninja docs (http://www.ninjaframework.org/documentation/basic_concepts/routing.html), I noticed the following text
The class ninja.ControllerMethods defines the various interfaces that are acceptable method signatures for Java 8 lambda expressions. A controller method returns a ninja.Result and has anywhere from 0 to 12 arguments. If you need more than 12 arguments, you can fallback to Ninja’s legacy routing strategy of Class + “method”.
Not sure if something changed from 6.0 version, but 10 params should work.

Related

POST or GET what should I use in case request parameters exceeds 15? [duplicate]

This question already has answers here:
Design RESTful query API with a long list of query parameters [closed]
(4 answers)
Closed 3 years ago.
I am designing a rest API, to get a resource based on some parameters but in some cases these parameters are between 15-20 in number.
I am thinking of using a POST request to get the resource based on these 15 parameters. I know that POST request should not be used in case of getting the resource.
I want to know if there is a better option to handle this then sending POST request?
You can use Get service by using Map. It will accept all param.
/test?A=ABC&B=123
#RequestMapping(value="/test",method = RequestMethod.GET)
public String testUrl(#RequestParam Map<String, String> parameters)
{
println(parameters.get("A"));
println(parameters.get("B"));
return parameters.get("A");
}
Out Put Will Be
ABC
123
GET doesn't restrict the number of parameters
the only restriction is the length of the URL (which contains these parameters)
So if you're expecting that the parameters and values would cause a long URL, you can use POST instead
Standard says that URL length should be not more than 2,083 characters
even if some browsers/servers allow more, it's better to stick on this value for a wide-range support for all browsers/servers/gateways
In order to make your #Controller code more concise (e.g. get rid of 15x #RequestParam) you can use #ModelAttribute annotation.
#GetMapping(value="/things")
public List<Thing> findAllThings(#ModelAttribute ThingDTO thing) {
// logic
}
and your ThingDTO class like that:
public class ThingDTO {
private String name;
private ThingType type;
[...] //getters, setters etc.
}
This is going to map your class attributes to the #RequestParams. The code looks a bit cleaner imho.
When it comes to the URL length you should check the topic here: What is the maximum length of a URL in different browsers? and decide if there's possibility of exceeding the limit.
What should you use? For data retrieval I'd in 99% cases go with GET so if the above is not a blocker for you, go with GET. If there's a chance of exceeding the limit, go with POST.
The parameter length shouldn't be a problem to handle for the server.
You should read about how to design a rest api. I would recommend you to build it like this:
/api/{version}/{entity}/{id}
If you are using Spring Boot this is very simple to build.
Just write a Controller like this
#RestController
#RequestMapping("/api/users")
public class UsersService {
#Autowired
UsersRepository userRepo;
#RequestMapping(value="/find-all", method=RequestMethod.GET)
public Page<User> getAllUsers(#RequestParam(value="page", defaultValue = "0", required=false)Integer page,
#RequestParam(value="size", defaultValue= "20", required=false)Integer size){
...
}
#RequestMapping(value="/find/{id}")
public ResponseEntity<User> getUserById(#PathVariable(name="id")Long id){
...
}
#RequestMapping(value="/save", method=RequestMethod.POST)
public ResponseEntity<User> createUser(#RequestBody User user){
...
}
#RequestMapping(value="/delete/{id}", method = RequestMethod.GET)
public ResponseEntity<Void> deleteUser(#PathVariable(name="id")Long id){
...
}
}
Hope this sample code helps you to build your api. Just pass your ID as a PathVariable like shown in deleteUser() or findUser().
If you need more Parameters, you can extend the list or use RequestParam like used in getAllUsers().
The parameters are optional but need a default value.

Regarding REST Path Conflict

I am creating two methods(GET) in a REST service in which the URL is for the first method is of the form
/a/b/{parameter}?start=1 &
end=100 & name="some value"
and for the second method it is
/a/b/{parameter}
When i run the code it gives a conflict.
Can anyone please suggest me how these can be configured for the methods and also to make the query paramters OPTIONAL?
Thanks
This should work fine:
#GET
#Path("/a/b/{parameter}")
public Response get(#PathParam("parameter") String parameter, #QueryParam("start") Integer start, #QueryParam("end") Integer end, #QueryParam("name") String name) {
// switch here according to the values received. All are optional and non-sent are nulls here
}
If in the future you will have default values you can add them inline like this (for the name query param for example):
#DefaultValue("some-name") #QueryParam("name") String name

Optional path segments in Spring MVC

Reading this (Spring 3) article from 2010, it discusses using an extension to provide a convenient way to include optional path segments:
#RequestMapping("/houses/[preview/][small/]{id}")
public String handlePreview(#PathVariable long id, #PathVariable("preview/") boolean preview, #PathVariable("small/") boolean small) {
return "view";
}
I know I could just implement a number of request mappings to achieve the same effect:
#RequestMapping(value="/houses/preview/{id}")
...
#RequestMapping(value="/houses/{id}")
...
~~~ snip ~~~
But depending on the number of possible permutations, it seems like a very verbose option.
Does any later version of Spring (after 3) provide such a facility? Alternatively, is there any mechanism to chain portions of the request URL to feed a larger response method signature?
Update
This answer to a question relating to sharing path variables and request parameters suggests an approach like:
#RequestMapping(method=RequestMethod.GET, value={"/campaigns","/campaigns/{id}"})
#ResponseBody
public String getCampaignDetails(
#PathVariable("id") String id)
{
~~~ snip ~~~
But the path variable cannot be set to null. Just going to /campaigns will return a 400 response.
Why you don't use java.util.Optional if you are using Java 1.8. To take your later example, you can avert a 400 response with put a Optional<String> instead of String representing your eventualy path like this:
#RequestMapping(method=RequestMethod.GET, value={"/campaigns","/campaigns/{id}"})
#ResponseBody
public String getCampaignDetails(
#PathVariable("id") Optional<String> id)
{
if(id.isPresent()){
model.addAttribute("id", id.get());//id.get() return your String path
}
}

Spring, middle score in request parameter

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".

Can RestEasy choose method based on query params?

I've started working with RestEasy and I've come across a problem that I can't seem to find an answer to. If I have 2 methods that both resolve to the same path (in this case /path1/path2/path3), but they both have a different number of query parameters, will RestEasy be able to determine which method to use?
#GET
#NoCache
#Produces({
MediaType.APPLICATION_JSON
})
#Path("/path1/path2/{path3}")
public String getResults1(
#PathParam("path3") String path3,
#QueryParam("query1") #DefaultValue("") String query1,
#QueryParam("query2") String query2,
#QueryParam("query3") #DefaultValue("25") int query3) {
...
}
#GET
#NoCache
#Produces({
MediaType.APPLICATION_JSON
})
#Path("/path1/path2/{path3}")
public String getResults2(
#PathParam("path3") String path3,
#QueryParam("query1") #DefaultValue("") String query1,
#QueryParam("query2") #DefaultValue("5") Integer query2) {
...
}
I've done some testing and yesterday it seemed that everything was working perfectly and that it could choose the right path, but now today I'm starting to see it take the wrong path each time.
Is this something that should be handled, or should I just suck it up and put it in 1 method and do the check myself?
No, you should be handling this in the method. If conflicting resources are found it is implementation independent which method will be matched.
Take a look at your example again:
If you submitted query1 and query2 how would it know if you wanted the method with 2 query parameters or the method with 3 query parameters and you wanted it to default the 3rd to it's default value?

Categories