resteasy: #QueryParam to parse nested array structure - java

I'm using a javascript library called tabulator to display data inside tables on the client side.
The Tabulator js library provides a feature to encode a representation of filters in the query parameters of an ajax request. For example, here's what the query params look like:
https://host/myEndpoint?size=10&page=1&filters%5B0%5D%5Bfield%5D=username&filters%5B0%5D%5Btype%5D=like&filters%5B0%5D%5Bvalue%5D=filteredBy
Here's the same url decoded:
https://host/myEndpoint?size=10&page=1&filters[0][field]=username&filters[0][type]=like&filters[0][value]=filteredBy
If possible, I'd like to have a Resteasy endpoint like this:
#GET
#Path("/myEndpoint")
#Consumes("application/json")
#Produces("application/json")
public Response myEndpoint(#QueryParam("page") Integer page,
#QueryParam("size") Integer size,
#QueryParam("filters") List<Filter> filters) {
resteasy interprets page and size no problem, but filters is always a list of size 0.
My Filter bean has 3 fields named field, type, and value with a constructor with single String argument as described here.
But it doesn't seem like resteasy is recognizing and parsing the filters query param? Is it possible to parse this type of nested array structure query parameters in resteasy?
filters[0][field]=username&filters[0][type]=like&filters[0][value]=filteredB

I'm still hoping that there's a better way, but here's a possible solution that works for me for now at least:
#GET
#Path("/myEndpoint")
#Consumes("application/json")
#Produces("application/json")
public Response myEndpoint(#QueryParam("page") Integer page,
#QueryParam("size") Integer size,
#Context UriInfo uriInfo) {
for(String key : uriInfo.getQueryParameters().keySet()) {
// check if key starts with something like `filters[0]`
// and then parse it however you need.
}
}

Related

Having alias param names to accept Url Encoded Form data values

In my Spring web application, I have an API that accepts requests with application/x-www-form-urlencoded content type.
#RequestMapping(value = "/do-it", method = {RequestMethod.POST})
public String test(#ModelAttribute("request")RequestDTO request,HttpServletRequest
httpServletRequest, Map<String, Object> model, RedirectAttributes redirectAttributes){
.....
}
My RequestDTO has following fields in it.
public class RequestDTO {
private String paramOne;
private String paramTwo;
// standard getters and setters
}
This implementation works fine, all the request params get mapped to the request dto as expected. However, now I have this requirement to accept the requests with the fields in following pattern.
param_one, param_two
I understand that, using #JsonProperty annotation on the fields in my request dto is not gonna work in my case since the request is not in the type of application/json.
The only way I have found to solve the issue is creating new setters like following (which looks ugly in my opinion when it comes to naming convention).
public void setParam_one(String param_one) {
this.paramOne = param_one;
}
Can some one help me to find a better way to get this done? I cannot change the param names in original request dto.
Thank you..!
I was able to get this done. Thanks to #neetash for guiding me.
Everything I needed to have was a Custom HandlerMethodArgumentResolver to map the post request body data to the object that I wanted to get.
I followed following linked tutorial to implement it. It contains every single thing someone needs to know to create a HandlerMethodArgumentResolver.
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/

chinese characters are getting corrupted as passed as query parameter values

I am using Apache cxf library and implemented rest apis.
Below is the code sample code snippet. When I pass the term value as query parameter, it is corrupting the value, but when I pass the term value as request body, everything is working fine.
#POST
#Path("/search")
#Produces({ APPLICATION_JSON })
public UserSearch searchUser(#FormParam("term") String term, #Context HttpServletRequest request) {
}
When I hit the url like "https://{SERVER_URL}/search?term=求人". It is corrupting and the term value is not passed to 'searchUer' function properly.
But when I pass the 'term' value as request body, everything is working fine.
I would like to know the reason of corruption?

Multiple resources under one request: RESTful API design pattern

I'm trying to create a GET request, where I have two different requests. Since both resources are related with each other, I'm trying to put them under one 'sub-resource'.
The first one is:
#QueryParam("{customerId}")
public List<customerModel> getCustomer(#QueryParam("customerId") Long customerId) {....}
this fetches the customer's name depending on the customerId
#QueryParam("{customerId}/details")
public List<customerDetailModel> getCustomerDetail(#QueryParam("customerId") Long customerId) {....}
this fetches the detailed information of the customer (phone number, address, etc.)
I'm running the first one with the following (works fine) :
......?customerId=7453112
but I can't reach the second request when I'm hitting the following URL:
......?customerId=7453112/details
Any suggestions?
Resource methods must be annotated with #Path and with a request method designator such as #GET, #POST, #PUT,
#DELETE, #HEAD or #OPTIONS. Your #QueryParam annotation is misplaced.
By the way, for this situation, you should consider using a path parameter instead of a query parameter. Hence you should use #PathParam instead of #QueryParam in the method parameter. For more details on when to use query or path parameters, have a look at this answer
Also, not sure why you are returning a List if you are requesting a resource by its unique identifier. In this situation, you are supposed to return a representation of a single resource (and not a represention of multiple resources).
So your resource class would be as following:
#Path("customers")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class CustomerResource {
#GET
#Path("{customerId}")
public CustomerModel getCustomer(#PathParam("customerId") Long id) {
...
}
#GET
#Path("{customerId}/details")
public CustomerDetailModel getCustomerDetail(#PathParam("customerId") Long id) {
...
}
}
The first endpoint can be requested as following:
GET http://example.com/api/customers/1
And the second endpoint can be requested as following:
GET http://example.com/api/customers/1/details
You need to go with #RequestMapping.
Something like:
#RequestMapping("/{customerId}") and #RequestMapping("/{customerId}/details").
Your URL becomes 7453112/details instead of query params.
You can specify the /details in an #Path annotation, and then use the same query parameter, like this:
#Path("/details/{customerId}")
public List<customerDetailModel> getCustomerDetail(#PathParam("customerId") Long customerId) {....}
Then your URL would look like this:
.../details/7453112
Or if you want to keep using it as a query parameter you can do something like this:
#Path("/details")
public List<customerDetailModel> getCustomerDetail(#QueryParam("customerId") Long customerId) {....}
using this url:
.../details?customerId=xxx

dojo grid sorting and RESTEasy JAX-RS

Dojo grids implement sorting by by issuing requests to REST webservices like so:
GET http://server/request?sort(+id)
Where sort(+id) is the direction (+/-) and the column to sort on.
Currently I'm doing this, and it works, but its ugly:
#GET
#Path("request/")
#Produces(MediaType.APPLICATION_JSON)
public Collection<RequestDataWithCurrentStatus> getAllRequests() {
Collection<RequestDataWithCurrentStatus> col = new ArrayList<RequestDataWithCurrentStatus>();
....
//handle the various types of sorting that can be requested by the dojo widget
String queryString = this.request.getQueryString();
//parse the dojo sort string 'sort(+id)' and sort
...
return col;
}
This method uses a RESTEasy injected #Context private HttpServletRequest request to get access to the raw query string.
I feel like I should be able to map this query string to one of RESTEasy's #*Param annotations in my getAllRequests() invocation. But according to the documentation for RESTEasy, there doesn't seem to be a good mapping to the screwy dojo query string from the doc. I want to do something like this:
#GET
#Path("request/")
#Produces(MediaType.APPLICATION_JSON)
public Collection<RequestDataWithCurrentStatus> getAllRequests( #DojoQueryParam String sort) {
...
}
How do I marshal dojo grid query strings to RESTEasy webservice methods the right way?
I have scarce experience with the old stores / dojox grids, but in case you are using dojo/store/JsonRest: You can change the way it sends the sort param with sortParam. Shamelessly snagged from the reference guide:
var store = new JsonRest({
target: "/FooObject/",
sortParam: "sortBy"
});
This should make requests on the form /foo/bar?sortBy=+id.
http://dojotoolkit.org/reference-guide/1.8/dojo/store/JsonRest.html#sorting

how to mime type of the url

I am using cxf as a webservice.It supports xml and json format output of the requested data.I want to know that if some exception has occured in my code then i want to return him back the error code either in xml or json format.But i dont know when to give json and xml ,it depends on the requested url that user has asked.
example
#Path("/reports/ad-view/loginId/{loginId}/publisher/")
PublisherReports getPublisherReportsAdView(
#PathParam("loginId") String loginId,
#QueryParam("fromDate") String fromDate,
#QueryParam("toDate") String toDate,
#QueryParam("filterValue") String filterValue);
If you mean you want to detect the mime type used to make the request, then you can use the #Consumes annotation to dictate which method handles which type of request. So you could write:
// Called when an XML request is made
#Path("/reports/ad-view/loginId/{loginId}/publisher/")
#Consumes("application/xml")
PublisherReports getPublisherReportsAdViewXml(...
and:
// Called when a JSON request is made
#Path("/reports/ad-view/loginId/{loginId}/publisher/")
#Consumes("application/json")
PublisherReports getPublisherReportsAdViewJson(...
Then have each variant of the getPublisherReportsAdView() method call a common method to perform the actual processing logic but still handle exceptions differently depending on the method that gets called.
An alternative approach, which doesn't require additional methods, would be to add a parameter which is annotated with the #HeaderParam annotation and use that to hold the value of the 'Content-Type' request header.
e.g.:
PublisherReports getPublisherReportsAdView(
#PathParam("loginId") String loginId,
#QueryParam("fromDate") String fromDate,
#QueryParam("toDate") String toDate,
#QueryParam("filterValue") String filterValue,
#HeaderParam("Content-Type") String contentType)
{
...
The value of contentType will likely also include charset information, for example: application/json; charset=UTF-8 so you'll need to ignore that when working out if the request contained JSON or XML.

Categories