In the web service I'm working on, I need to implement a URI with query parameters which look like /stats?store=A&store=B&item=C&item=D
To break it down, I need to be able to use query parameters to specify data from multiple/all stores and data for multiple/all items from those stores. So far I have been able to implement one query argument just fine in order to pull item data, but I'm lost as far as to how to implement more queries, and can't seem to find the resources I had seen before which deal with this implementation.
What I have so far in my method is along the lines of
#GET
#Path("stats")
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "item") final String item)
{
/**Run data using item as variable**/
return someStringOfData
}
which works well for one item, and will return all data if I don't type the parameter in the URI. However, I am unsure how to handle any more parameters than this.
Update:
I have figured out how to use 2 different parameters by simply adding a second argument to the method like so:
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "store") final String store,
#DefaultValue("All") #QueryParam(value = "item") final String item)
The question remains of how to implement multiple values of the same parameter.
If you change the type of your item method parameter from String to a collection such as List<String>, you should get a collection that holds all the values you are looking for.
#GET
#Path("/foo")
#Produces("text/plain")
public String methodImCalling(#DefaultValue("All")
#QueryParam(value = "item")
final List<String> item) {
return "values are " + item;
}
The JAX-RS specification (section 3.2) says the following regarding the #QueryParam annotation:
The following types are supported:
Primitive Types
Types that have a constructor that accepts a single String argument.
Types that have a static method named valueOf with a single String argument.
List<T>, Set<T>, or SortedSet<T> where T satisfies 2 or 3 above.
List<String> items=ui.getQueryParameters().get("item");
where ui is declared as a member in the rest resource like so :
#Context UriInfo ui;
the downside is that it doesn't appear in the methods arguments at all.
Some libs like axios js use the square brackets notation when sending a multi-value param request: /stats?store[]=A&store[]=B&item[]=C&item[]=D
To handle all cases (with or without square brackets) you can add another param like this:
public String methodImCalling(
#QueryParam(value = "store") final List<String> store,
#QueryParam(value = "store[]") final List<String> storeWithBrackets,
#QueryParam(value = "item") final List<String> item,
#QueryParam(value = "item[]") final List<String> itemWithBrackets) {
...
}
Inspecting each of the arguments checking for null.
Related
When I read some codes written with Spring MVC in an old project, I notice that the author using two similar method to pass value to controller as follows:
#RequestMapping(value="/print")
public String PrintInfo(#RequestParam("name") String name,#RequestParam("age") int age) {
.......
}
Or
#RequestMapping(value="/print")
public String PrintInfo(String name,int age) {
.......
}
Both them work well.
The difference is whether it use the #RequestParam.
So what's the main difference between them? Which one is better, and why?
This basically sounds to me like you're asking "what is a RequestParam and why should I use it?"
The RequestParam allows you to bind the method parameter argument to the web request parameter. Without any other attributes, your example tells Spring that a name and age parameter are required and Spring will know to associate those two parameters against the incoming request. You can optionally set required to false to, well, make the argument optional:
public String PrintInfo(#RequestParam("name", required = false) String name,
#RequestParam("age") int age) {
As an extremely useful feature, you can also provide a defaultValue in case you receive an empty value from the request. So you can do:
public String PrintInfo(#RequestParam("name", defaultValue="John Doe") String name,
#RequestParam("age") int age) {
...and you'll never deal with a null name.
Finally, using it also does some magic type conversions, like for example automatically using an Integer type. In your example, you could have used:
public String PrintInfo(#RequestParam("name") String name,
#RequestParam("age") Integer age) {
...and Spring would have boxed it automatically without you doing any extra work.
There's nothing inherently wrong with leaving off the RequestParam annotation, but you're essentially saying no to Spring enabling these features as you have in your second case.
Aside:
#RequestMapping(value="/print") can be more simply written as #RequestMapping("/print")
If the name of request parameter and the name of method arguments will be equal, then Spring will bind parameters automatically by names. For example, you have incoming GET request:
http://localhost:8080/print?name=somename&age=30
And controller method:
#RequestMapping(value="/print")
public String PrintInfo(String name,int age) {
...
}
In this case you don't need to specify #RequestParam annotation for parameter. Because names in request and names of methods args are equals.
But when names are not equals, then you need to specify the correspondence of names explicitly with #RequestParam. For example:
http://localhost:8080/print?user_name=somename&user_age=30
And controller method:
#RequestMapping(value="/print")
public String PrintInfo(#RequestParam("user_name") String userName, #RequestParam("user_age")int userAge) {
...
}
So #RequestParam needed to help the Spring make bindings properly, when request param names and method args names are different.
Acutally, many developers always use #RequestParam even when names are equal. For example empty #RequestParam:
#RequestMapping(value="/print")
public String PrintInfo(#RequestParam() String name, #RequestParam() int age) {
...
}
Because this annotation shows that argument comes from request and makes your code more clear and readable.
I have a response class - MyResponse as below to call the server using Retrofit2. resource is an array of objects.
public class MyResponse {
#SerializedName("success")
#Expose
private Boolean success;
#SerializedName("resource")
#Expose
private Array[] resource;
public ApiResponse(Boolean done, Array[] resource) {
this.done = done;
this.resource = resource;
}
//getters and setters
}
In the Activity, I created an Array as below:
MyResponse decodedResponse = response.body();
Array[] catsList = decodedResponse.getResource();
And I have a Model class Category as below:
public class Category {
#SerializedName("id")
#Expose
private Integer id;
#SerializedName("category")
#Expose
private String category;
//getters, setter and constructor
}
Each object of above catsList array follows above category model(with id and category keys). Now how I make an array of Category (Category[]) from above catsList array?
I can't do it as Category[] catsList = decodedResponse.getObject(); because I want to create more arrays for other models too. Therefore I am planning to create a common MyResponse class as above and cast it's generated array to specific model when needed.
Also following method(after changed Array[] =to=> Object[] in MyResponse) is not supported. It gives java.lang.ClassCastException: java.lang.Object[] cannot be cast to my.package.Category[] exception.
Object[] catsList = decodedResponse.getObject();
Category[] catsListCooked = (Category[]) catsList;
EDIT:
My JSON response from server has same style for every request types and uses one model for one request type. Because Retrofit wants to know which model class will use for the call in order to make a call as below.
#GET()
Call<MyResponse> getCategories(#Url String url);
Note: Please correct me if I can do this in another way. Because I call as mentioned above, I need to have a class in order to make a call.
So what I want to do is create a main response(MyResponse as above) which will receive the response and then use each model to fetch received data according to the request type. It is not possible to use the model directly when making a call because I have a custom JSON response from server.
i.e. If used model directly, then it gives errors such as expected BEGIN_ARRAY but provide BEGIN_OBJECT or expected BEGIN_OBJECT but provide BEGIN_ARRAY.
As commented below, from suggested duplicates, this suggestion by ytg looks like ok to achieve the solution. But using Arrays.copyOf() as suggested there, it seems it is not creating Category[] array to be useful since it gives "...cannot be stored in destination array of type..." error. Also other solutions from that question are not doable in my case.
I have a simple controller method with more than 7 parameters and would like to refactor it using a model object instead, i.e. to extract a parameter object:
#RequestMapping(value="/{one}")
public String controllerMethod(#PathVariable(value="one") String one, #RequestParam String two, ... #RequestBody body) {
...
}
I tried to extract an object with setters and getters and pathVariable and requestParameters are mapped by name. However I have troubles making the same for #RequestBody, it doesn't work for me even if I put #RequestBody into setter...
public class Parameters {
private String one; // pathVariable
private String two; // requestParameter
private String body;// requestBody - always NULL!
// other fields definition
public setBody(#RequestBody String body) {this.body = body}
//other setters/getters
}
How to keep #RequestBody parameter in extracted POJO?
Another question is how to control name of parameters, i.e. if
parameter name differs from the field name in POJO, is there any
annotation? This one doesn't work:
public void setOne(#RequestParameter(value="o") String one) {this.one = one}
How to mark the fields as required or give a default value like in the #RequestParameter annotation?
For the (1) I would simply keep #RequestBody as a separate parameter though I don't like it much.
Ok, looks like the only way of doing (2) and (3) is through customizing data binding: the similar question
Feel free to post another easy solution if you know about it.
I have a jersey endpoint
#Path(value = "/testfields")
#POST
#Consumes({"application/x-www-form-urlencoded"})
public Response acceptFields(#BeanParam MyWrapper initialWrapper,String entity) {
//.......
}
MyWrapper is:
class MyWrapper{
#FormParam("param1")
private String param1;
#FormParam("inners")
private List<MyInnerWrapper> inners;
//..getters setters
}
class MyInnerWrapper{
#FormParam("innerParam1")
private String innerParam1;
//.... getters setters
}
I have request POST entity string:
param1=aaa&inners[0]["innerParam"]=bbb&inners[1]["innerParam"]=nnn
The issue is I can obtain parent`s field param1 with #BeanParam annotation, but I can't obtain inner list of custom classes, why? How to process regular html form array? I need to get my class evaluated with its inner classes in my endpoint. I tried array, list of objects, nothing works. Thanks
AFAIK, that type of syntax is not supported in Jersey. If you want to use custom types for #FormParam, then you need to meet the requirements of at least one those listed in the Javadoc. One options is to just have a constructor with a String argument. For example
public class MyInnerWrapper {
private final String innerParam;
public MyInnerWrapper(String param) {
this.innerParam = param;
}
public String getInnerParam() {
return innerParam;
}
}
Then all you need to do is send this request
param1=blahparam&inners=first&inners=second&inners=third
Then you can have
#FormParam("inners")
private List<MyInnerWrapper> inners;
There's no need for an [] to specify indices. Duplicates are allowed. That's why we are allowed to have lists and arrays for the #FormParam types.
Now if you want to have more than one property and then try to map them like you currently are, I don't think that will work. At least not in Jersey. I would recommend maybe looking into using JSON if you need more complex types. Otherwise just stick to simple keys.
I've a Classified interface, annotated with #JsonAutoDetect with Visibility.NONE, so I can pick individual getters to be serialized with the #JsonSerialize annotation
#JsonAutoDetect(getterVisibility = Visibility.NONE)
public interface Classified {
#JsonSerialize
String getModel();
Until here there is no problem, and when I return Classified with #ResponseBody annotation, from my #Controller, it works returning the expected JSON:
#RequestMapping(value = "/classified/{idClassified}", method = RequestMethod.GET)
#ResponseBody
public final Classified getClassified(#PathVariable final int idClassified) {
However when I return a List of Classifieds, I would like to return a smaller set of getters, while with the following signature, obviously it returns all marked getters:
#RequestMapping(value = "/classified", method = RequestMethod.GET)
#ResponseBody
public final List<Classified> searchClassified(#RequestParam final int idBrand,
#RequestParam final String priceMax, #RequestParam final int page) {
I don't know how to return a smaller subset of Classified getters in each item of the list.
Check out "filtering properties", which lists multiple ways to change what gets serialized. I would guess that Json Views might be the easiest one; could use one smaller view, and then default "all" mode when no view is defined (default is then to serialize all properties).
"return a smaller set of getters"
If you mean reduce the number of items in the list, change the business logic in the controller's searchClassified method.
If you mean reduce the number of available public getter methods on each item, you could create a interface that only implements a subset of the original items getters, and return a list of them instead.