Accept comma separated value in REST Webservice - java

I am trying to receive a list of String as comma separated value in the REST URI ( sample :
http://localhost:8080/com.vogella.jersey.first/rest/todo/test/1/abc,test
, where abc and test are the comma separated values passed in).
Currently I am getting this value as string and then splitting it to get the individual values.
Current code :
#Path("/todo")
public class TodoResource {
// This method is called if XMLis request
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Path("/test/{id: .*}/{name: .*}")
public Todo getXML(#PathParam("id") String id,
#PathParam("name") String name) {
Todo todo = new Todo();
todo.setSummary("This is my first todo, id received is : " + id
+ "name is : " + Arrays.asList(name.split("\\s*,\\s*")));
todo.setDescription("This is my first todo");
TodoTest todoTest = new TodoTest();
todoTest.setDescription("abc");
todoTest.setSummary("xyz");
todo.setTodoTest(todoTest);
return todo;
}
}
Is there any better method to achieve the same?

I am not sure what you are trying to achieve with your service, however, it may be better to use query parameters to get multiple values for a single parameter. Consider the below URL.
http://localhost:8080/rest/todos?name=name1&name=name2&name=name3
And here is the code snippet for the REST service.
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Path("/todos")
public Response get(#QueryParam("name") List<String> names) {
// do whatever you need to do with the names
return Response.ok().build();
}

If you don't know how many comma separated values you will get, then the split you do is as far as I've been able to find the best way to do it. If you know you will always have 3 values as comma separated, then you can get those 3 directly. (for instance if you have lat,long or x,y,z then you could get it with 3 pathvariables. (see one of the stackoverflow links posted below)
there are a number of things you can do with matrix variables but those require ; and key/value pairs which is not what you're using.
Things I found (apart from the matrix stuff)
How to pass comma separated parameters in a url for the get method of rest service
How can I map semicolon-separated PathParams in Jersey?

Related

semicolon in the restful service URL truncate the characters after it

I have the below code as my restful service operation.
#GET
#UnitOfWork
#Timed(name = "get-requests")
#Path("/{referenceId}")
public Response get(#Auth #ApiParam(access = "internal") UserPrincipal user,
#ApiParam(name = "id", value = "reference ID", required = true)
#PathParam("referenceId") String id) {
return Response.ok(id).build();
}
However, I noticed if I pass in m1234;5678, I get only m1234 returned. I tried #Path("/{referenceId:.*}"), but it doesn't work.
I also tried use #Encode at the top of the method to make sure the url is not decoded and then try to replace %3B with ";" in the code. But it seems not working also.
Please note that I cannot use Spring framework. Thanks.
The ; denotes a matrix parameter. Use #MatrixParam to get its value.
See also the answers to this question: URL matrix parameters vs. request parameters
Edit: The key of the matrix parameter would be 5678, the value would be null.
There is a way to get achieve what you want by using PathSegment as the type of the parameter instead of String:
#PathParam("referenceId) PathSegment id
In the body of the method, you can use
String idValue = id.getPath();
to get m1234;5678.

Passing json object to an endpoint developed with spring

I have an endpoint I created using spring.io. My GetMapping declaration can be seen below
#ApiOperation(
value = "Returns a pageable list of CustomerInvoiceProducts for an array of CustomerInvoices.",
notes = "Must be authenticated.")
#EmptyNotFound
#GetMapping({
"customers/{customerId}/getProductsForInvoices/{invoiceIds}"
})
public Page<CustomerInvoiceProduct> getProductsForInvoices(
#PathVariable(required = false) Long customerId,
#PathVariable String[] invoiceIds,
Pageable pageInfo) {
//Do something fun here
for (string i: invoiceIds){
//invoiceIds is always empty
}
}
Here is how I am calling the url from postman and passing the data.
http://localhost:8030/api/v1/customers/4499/getProductsForInvoices/invoiceIds/
{
"invoiceIds": [
"123456",
"234566",
"343939"
]
}
My string array for invoiceIds is always empty in the for loop Nothing gets passed to the array. What am I doing wrong?
The mapping you are using is this:
customers/{customerId}/getProductsForInvoices/{invoiceIds}
Both customerId and invoiceIds are Path variables here.
http://localhost:8030/api/v1/customers/4499/getProductsForInvoices/invoiceIds/
The call you are making contains customerId but no invoiceIds. Either you can pass the list in place of invoiceIds as String and read it as a String and then create a List by breaking up the List - which will be a bad practice.
Other way is to change your path variable - invoiceId to RequestBody.
Generally, Path Variables are used for single id or say navigating through some structured data. When you want to deal in a group of ids, the recommended practice would be to pass them as RequestBody in a Post method call rather than a Get method call.
Sample code snippet for REST API (post calls):
Here, say you are trying to pass Employee object to the POST call, the REST API will look like something below
#PostMapping("/employees")
Employee newEmployee(#RequestBody Employee newEmployee) {
//.. perform some operation on newEmployee
}
This link will give you a better understanding of using RequestBody and PathVariables -
https://javarevisited.blogspot.com/2017/10/differences-between-requestparam-and-pathvariable-annotations-spring-mvc.html
https://spring.io/guides/tutorials/rest/

Is it possible to store pathparams as a list?

I have a Rest Service that I want to respond to requests with the following paths
1) /v1/config/type/service
2) /v1/config/type/service, service2
What I'd like is to be able to store the path param serviceName as a List where each element is delimited by a comma. For example, if someone types v1/config/foo/bar1,bar2,bar3 I'd like serviceName to be a List with 3 elements (bar1, bar2, bar3). Right now it just returns a list with 1 element that contains all three service strings. Is that even possible? Or is that something I'll simply have to parse. The code I have is shown below, it's pretty rough as I'm in the beginning stages of the project:
#ApplicationPath("/")
#Path("/v1/config")
public class ServiceRetriever extends Application {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getHelloWorld() {
return "Hello World";
}
#GET
#Path("{type}/{serviceName}")
#Produces("application/zip")
public Response getServices(#PathParam("type") String type, #PathParam("serviceName")List<String> serviceNames,
#QueryParam("with_config") boolean withConfig, #QueryParam("with_drive") boolean withDriver) throws IOException
{
//some random file i made to test that we can return a zip
File file = new File(System.getProperty("user.home")+"/dummy.zip");
System.out.println(serviceNames.size()); //returns 1
//we can change the zip file name to be whatever
return Response.ok(file).header("Content-Type","application/zip").
header("Content-Disposition", "attachment; filename="+file.getName()).build();
}
The problems is that you have to alter the deserialization process of that variable. Typically only query parameters are lists so this might not be compatible with some libraries.
You could:
Capture the parameter as a string and parse it internally via helper method (obvious)
Create your own annotation like #PathParamMutli and return Arrays.asList(parameter.split(","));. Ideally you should have access to the framework source code and branching privileges.
Use a query parameter instead

Spring REST Controller understanding arrays of strings when having special characters like blank spaces or commas

I am trying to write a Spring REST Controller getting an array of strings as input parameter of a HTTP GET request.
The problem arises when in the GET request, in some of the strings of the array, I use special characters like commas ,, blank spaces or forward slash /, no matter if I URL encode the query part of the URL HTTP GET request.
That means that the string "1/4 cup ricotta, yogurt" (edit which needs to be considered as a unique ingredient contained as a string element of the input array) in either this format:
http://127.0.0.1:8080/[...]/parseThis?[...]&ingredients=1/4 cup ricotta, yogurt
This format (please note the blank spaces encoded as + plus, rather than the hex code):
http://127.0.0.1:8080/[...]/parseThis?[...]&ingredients=1%2F4+cup+ricotta%2C+yogurt
Or this format (please note the blank space encoded as hex code %20):
http://127.0.0.1:8080/[...]/parseThis?[...]&ingredients=1%2F4%20cup%20ricotta%2C%20yogurt
is not rendered properly.
The system does not recognize the input string as one single element of the array.
In the 2nd and 3rd case the system splits the input string on the comma and returns an array of 2 elements rather than 1 element. I am expecting 1 element here.
The relevant code for the controller is:
#RequestMapping(
value = "/parseThis",
params = {
"language",
"ingredients"
}, method = RequestMethod.GET, headers = HttpHeaders.ACCEPT + "=" + MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public HttpEntity<CustomOutputObject> parseThis(
#RequestParam String language,
#RequestParam String[] ingredients){
try {
CustomOutputObject responseFullData = parsingService.parseThis(ingredients, language);
return new ResponseEntity<>(responseFullData, HttpStatus.OK);
} catch (Exception e) {
// TODO
}
}
I need to perform HTTP GET request against this Spring controller, that's a requirement (so no HTTP POST can be used here).
Edit 1:
If I add HttpServletRequest request to the signature of the method in the controller, then I add a log statement like log.debug("The query string is: '" + request.getQueryString() + "'"); then I am seeing in the log a line like The query string is: '&language=en&ingredients=1%2F4+cup+ricotta%2C+yogurt' (So still URL encoded).
Edit 2:
On the other hand if I add WebRequest request to the signature of the method, the the log as log.debug("The query string is: '" + request.getParameter("ingredients") + "'"); then I am getting a string in the log as The query string is: '1/4 cup ricotta, yogurt' (So URL decoded).
I am using Apache Tomcat as a server.
Is there any filter or something I need to add/review to the Spring/webapp configuration files?
Edit 3:
The main problem is in the interpretation of a comma:
#ResponseBody
#RequestMapping(value="test", method=RequestMethod.GET)
public String renderTest(#RequestParam("test") String[] test) {
return test.length + ": " + Arrays.toString(test);
// /app/test?test=foo,bar => 2: [foo, bar]
// /app/test?test=foo,bar&test=baz => 2: [foo,bar, baz]
}
Can this behavior be prevented?
The path of a request parameter to your method argument goes through parameter value extraction and then parameter value conversion. Now what happens is:
Extraction:
The parameter is extracted as a single String value. This is probably to allow simple attributes to be passed as simple string values for later value conversion.
Conversion:
Spring uses ConversionService for the value conversion. In its default setup StringToArrayConverter is used, which unfortunately handles the string as comma delimited list.
What to do:
You are pretty much screwed with the way Spring handles single valued request parameters. So I would do the binding manually:
// Method annotations
public HttpEntity<CustomOutputObject> handlerMethod(WebRequest request) {
String[] ingredients = request.getParameterValues("ingredients");
// Do other stuff
}
You can also check what Spring guys have to say about this.. and the related SO question.
Well, you could register a custom conversion service (from this SO answer), but that seems like a lot of work. :) If it were me, I would ignore the declaration the #RequestParam in the method signature and parse the value using the incoming request object.
May I suggest you try the following format:
ingredients=egg&ingredients=milk&ingredients=butter
Appending &ingredients to the end will handle the case where the array only has a single value.
ingredients=egg&ingredients=milk&ingredients=butter&ingredients
ingredients=milk,skimmed&ingredients
The extra entry would need to be removed from the array, using a List<String> would make this easier.
Alternatively if you are trying to implement a REST controller to pipe straight into a database with spring-data-jpa, you should take a look at spring-data-rest. Here is an example.
You basically annotate your repository with #RepositoryRestResource and spring does the rest :)
A solution from here
public String get(WebRequest req) {
String[] ingredients = req.getParameterValues("ingredients");
for(String ingredient:ingredients ) {
System.out.println(ingredient);
}
...
}
This works for the case when you have a single ingredient containing commas

Pass List to RESTful webservice

Is there a way to pass a list to RESTFul web service method in Jersey? Something like #PathParam("list") List list?
Hope that this will help you
Java code
import java.util.List;
#Path("/customers")
public class CustomerResource {
#GET
#Produces("application/xml")
public String getCustomers(
#QueryParam("start") int start,
#QueryParam("size") int size,
#QueryParam("orderBy") List<String> orderBy) {
// ...
}
}
Passing value from javascript using AJAX
Ajax call url : /customers?orderBy=name&orderBy=address&orderBy=...
I found out that the best way to send a list via POST from the client to a REST service is by using the #FormParam.
If you add a parameter twice or more times to the form, it will result into a list on the server's side.
Using the #FormParammeans on client side you generate a com.sun.jersey.api.representation.Form and add some form parameters like shown below. Then you add the filled form to the post like that: service.path(..) ... .post(X.class, form) (see example code).
Example-code for the client side:
public String testMethodForList() {
Form form = new Form();
form.add("list", "first String");
form.add("list", "second String");
form.add("list", "third String");
return service
.path("bestellung")
.path("add")
.type(MediaType.APPLICATION_FORM_URLENCODED)
.accept(MediaType.TEXT_XML)
.post(String.class, form);
}
Example-Code for server side:
#POST
#Path("/test")
#Produces(MediaType.TEXT_XML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String testMethodForList(
#FormParam("list") List<String> list {
return "The list has " + list.size() + " entries: "
+ list.get(0) + ", " + list.get(1) + ", " + list.get(2) +".";
}
The return String will be:
The list has 3 entries: first String, second String, third String.
Note:
The MediaTypes of #Consumes on server side and .type() on client
side have to be identical as well as #Produces and .accept().
You can NOT send objects other than String, Integer etc. via
#FormParam. In case of an object you'll have to convert it to XML
or JSON String and re-convert it on the server side. For how to convert see here.
You can also pass a List to the form like form.add(someList), but this will result in a String containing the list's entries on server side. It will look like: [first String, second String, third String]. You'd have to split the String on server side at "," and cut off the square brackets for extracting the single entiries from it.
If I'm understanding what you're trying to do, you could serialize the List object and pass it as a string.

Categories