In Java, I am implementing a server where client can pass some data (Key-value pairs) using post query. I have decided to make a REST Service and I am planning to use JAX-RS along with Jetty.
I have no previous knowledge about the keys to be send here. Is there any way to browse over all the KV pairs POSTed by client? I know that if key is known we could retrieve data as in -
#Path("/testpath")
public class test {
#POST
#Path("/level1")
public Response getData(
#FormParam("key1") String val1,
#FormParam("key2") int val2) {
return Response.status(200)
.entity("getData is called, Key1 : " + val1 + ", Key2 : " + val2)
.build();
}
}
In the above example, I could have N of different keys!
I am planning to use vanilla JAX-RS without Jersey, or RESTeasy. However I am open to the options when not possible in JAX-RS!
Use MultiValuedMap :
#Path("/testpath")
public class test {
#POST
#Path("/level1")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces("text/plain")
public Response getData(MultiValuedMap<String, String> params) {
StringBuilder sb = new StringBuilder("getData is called, ");
for(String param : params.keySet()) {
sb.append(param + " : " + params.getFirst(param) + ", ");
}
return Response.status(200).entity(sb.toString()).build();
}
}
Related
We have a requirement to read data after '?' in service-url in Spring boot REST API.
For example, We exposed a service called sampleService and GET URL for this is
http://www.myservices.com/api/sampleServce
And clients will pass the data as http://www.myservices.com/api/sampleServce?dynamicdata
So we have to read that "dynamicdata" in my sample service and process.
Please let me know the possibilities.
GET: http://localhost:8080/api/foos?id=abc here the query string is id=abc . Now to extract the value of id, you can use the code something like this.
#GetMapping("/api/foos")
#ResponseBody
public String getFoos(#RequestParam String id) {
return "ID: " + id;
}
GET: http://www.myservices.com/api/sampleServce?dynamicdata is incorrect. Either it should be http://www.myservices.com/api/sampleServce/dynamicdata (PathVariable) or http://www.myservices.com/api/sampleServce?title=dynamicdata (RequestParam)
GET: http://www.myservices.com/api/sampleServce/dynamicdata to extract dynamicdata, you can use code something like
#GetMapping("/api/sampleServce/{id}")
#ResponseBody
public String getFooById(#PathVariable String id) {
return "ID: " + id; // here id = "dynamicdata"
}
GET: http://www.myservices.com/api/sampleServce?title=dynamicdata to extract title, you can use code something like
#GetMapping("/api/sampleServce")
#ResponseBody
public String getFoos(#RequestParam String title) {
return "title: " + title; // title="dynamicdata"
}
dynamicdata is path param, it cannot be placed after ?. It should be something like this:
http://www.myservices.com/api/dynamicdata/sampleServce
Check when and how to use query or path parameters
Accept dynamic data through request param:
#GetMapping("/api/sampleServce")
public void test(#RequestParam Map<String, String> dynamicdata) {
System.out.println(dynamicdata.keySet()); //http://localhost:9001/dag-backend/api/sampleServce?test&test11
Optional<String> data = dynamicdata.keySet().stream().findFirst();
String value = data.isPresent() ? data.get() : null;
System.out.println(value); //http://localhost:9001/dag-backend/api/sampleServce?test
}
URLs:
http://www.myservices.com/api/sampleServce?dynamicdata
http://www.myservices.com/api/sampleServce?dynamicdata&12344&1212
http://www.myservices.com/api/sampleServce?$999900&124434&234
You can add an HttpServletRequest object to your mapped method signature:
#RequestMapping("/api/sampleServce")
public Object sampleServce (HttpServletRequest request) {
//print everything after the question mark:
System.out.println("queryString: " + request.getQueryString());
//print key value pairs:
Enumeration<String> params = request.getParameterNames();
while(params.hasMoreElements()){
String paramName = params.nextElement();
String paramValue = request.getParameter(paramName);
System.out.println("name: " + paramName);
System.out.println("value: " + paramValue);
}
return request.getQueryString();
}
I want to pass a long series of request parameters (over 2000 characters in total) from one .jsp to another (via a URL), and make it seem to the receiving HTTPServletRequest as if it received the request parameters normally.
I cannot simply pass the URL normally as IE11 is truncating the URL at about 2000 characters (see What is the maximum length of a URL in different browsers?) so I need to have some kind of workaround.
It is trivial to save the url in the ClientSession with a key in one .jsp
public String addValue(String aString) {
String key=""+UUID.randomUUID();
mapValues.put(key, aString);
return key;
}
and then retrieve it in the other .jsp
public String getValue(String key) {
return mapValues.get(key);
}
However the other .jsp needs a HTTPServletRequest and not a string
I.e. I need to be able to do
public MyPosition(HttpServletRequest request) {
this.id= (String)request.getParameter("ID");
Is there anyway of doing this by converting the retrieved url to a HTTPServletRequest?
I know that I could rewrite MyPosition to take a string and extract the data from there directly, but I would much rather not touch the very lengthy, legacy code.
If I could do setParameter on the request, then this would be a solution. But such an option is not available (see HttpServletRequest - SetParameter)
The only way to modify an HttpServletRequest is to wrap it.
It sounds like you want to make a standard POST request instead of what sounds like a GET request.
I tried both #dimplex's and #Deadron's solutions, which I think should both work, but didn't manage to implement either in the short time frame I had available.
I ended up replacing the HTTPServletRequest request parameter in the MyPosition function with String urlKey and adding the following line inside the function
RequestStr request=new RequestStr(cSession,urlKey);
now my existing code did not need to be changed at all, and request.getParameter("paramName") would call my function below.
public class RequestStr {
String url = "";
public RequestStr(ClientSession cSession, String urlKey) {
super();
this.url = cSession.getValue(urlKey);
}
public String getParameter(String aParam) {
int i = url.indexOf("?" + aParam + "=");
if (i == -1) {
i = url.indexOf("&" + aParam + "=");
}
if (i == -1) {
return null;
} else {
int j = url.indexOf("&", i + 1);
if (j == -1) {
return url.substring(i + aParam.length() + 2);
} else {
return url.substring(i + aParam.length() + 2, j);
}
}
}
}
So all I needed to do was to save the very long URL in the session in a map with key urlKey and then in the request just passed this urlKey, and then I could retrieve the long URL via the urlKey and then decode it via my RequestStr class
My function looks like this:
#PUT
#Path("property/{uuid}/{key}/{value}")
#Produces("application/xml")
public Map<String,ValueEntity> updateProperty(#Context HttpServletRequest request,
#PathParam("key") String key,
#PathParam("value") String value,
#PathParam("uuid") String uuid) throws Exception {
...
}
I have to modify it, so it accepts indefinite(or many) list of key-value pairs from REST call, something like
#Path("property/{uuid}/{key1}/{value1}/{key2}/{value2}/{key3}/{value3}/...")
Is it possible to store them in an array or list, so I do not list dozens of #PathParams and parameters, to avoid this:
#PathParam("key1") String key1,
#PathParam("key2") String key2,
#PathParam("key3") String key3,
Might be a good opportunity to rethink this design. By using /s, we are in a way signifying, with each / that we are trying to locate a different resource. Key/Value pairs (in the context of the URL) are mainly for query parameters or matrix parameters.
If /property/{uuid} is the path to a main resource, and we just want to offer some parameters to the client for accessing this resource, then we could allow matrix parameters or query parameters
Matrix Parameters (in a request url) will look something like
/12345;key1=value1;key2=value2;key3=value3
A resource method to obtain the values might look something like
#GET
#Path("/property/{uuid}")
public Response getMatrix(#PathParam("uuid") PathSegment pathSegment) {
StringBuilder builder = new StringBuilder();
// Get the {uuid} value
System.out.println("Path: " + pathSegment.getPath());
MultivaluedMap matrix = pathSegment.getMatrixParameters();
for (Object key : matrix.keySet()) {
builder.append(key).append(":")
.append(matrix.getFirst(key)).append("\n");
}
return Response.ok(builder.toString()).build();
}
See PathSegment
Query Parameters (in a request url) might look something like
/12345?key1=value1&key2=value2&key3=value3
A resource method to obtain the values might look something like
#GET
#Path("/property/{uuid}")
public Response getQuery(#PathParam("uuid") String uuid,
#Context UriInfo uriInfo) {
MultivaluedMap params = uriInfo.getQueryParameters();
StringBuilder builder = new StringBuilder();
for (Object key : params.keySet()) {
builder.append(key).append(":")
.append(params.getFirst(key)).append("\n");
}
return Response.ok(builder.toString()).build();
}
See UriInfo
The difference is that Matrix parameters can be embedded into path segments, while query parameters must be placed at the end of the URL. You can also notice a little difference in syntax.
Some Resources
Query String (Wikipedia)
When to use query parameters versus matrix parameters?
URL matrix parameters vs. request parameters
UPDATE
Also looking at the PUT in you method signature, it appears you are trying update a resource using the path as the values for which you are trying to update, as I don't see any parameters in your method for an entity body. When PUTting, you should be sending the representation in the the entity body, not as as path segments or parameters.
A workaround:
#Path("/foo/bar/{other: .*}
public Response foo(#PathParam("other") VariableStrings vstrings) {
String[] splitPath = vstrings.getSplitPath();
}
VariableStrings class:
public class VariableStrings {
private String[] splitPath;
public VariableStrings(String unparsedPath) {
splitPath = unparsedPath.split("/");
}
}
Path segment sequence to vararg array in JAX-RS / Jersey?
Another example where you map the optional parameter to a Map:
#GET
# Produces({"application/xml", "application/json", "plain/text"})
# Path("/location/{locationId}{path:.*}")
public Response getLocation(#PathParam("locationId") int locationId, #PathParam("path") String path) {
Map < String, String > params = parsePath(path);
String format = params.get("format");
if ("xml".equals(format)) {
String xml = "<location<</location<<id<</id<" + locationId + "";
return Response.status(200).type("application/xml").entity(xml).build();
} else if ("json".equals(format)) {
String json = "{ 'location' : { 'id' : '" + locationId + "' } }";
return Response.status(200).type("application/json").entity(json).build();
} else {
String text = "Location: id=" + locationId;
return Response.status(200).type("text/plain").entity(text).build();
}
}
private Map < String, String > parsePath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
String[] pathParts = path.split("/");
Map < String, String > pathMap = new HashMap < String, String > ();
for (int i = 0; i < pathParts.length / 2; i++) {
String key = pathParts[2 * i];
String value = pathParts[2 * i + 1];
pathMap.put(key, value);
}
return pathMap;
}
I'm following this tutorial to write a workflow for 3 HTML forms (without using CQ5 form component and CQ5 workflow submit button). I use this code to write Process step handling for my workflow:
public class MyProcess implements WorkflowProcess {
public void execute(WorkItem item, WorkflowSession session,
MetaDataMap map) throws WorkflowException {
boolean advanced = false;
Boolean goBack = map.get("goBack", Boolean.class);
List<Route> routes = null;
if (goBack == null || goBack == false) {
routes = session.getRoutes(item);
} else {
routes = session.getBackRoutes(item);
}
for (Route route : routes) {
LOG.info("===============================");
LOG.info("Rout name: " + route.getName());
LOG.info("Destinations: ");
for (WorkflowTransition dest: route.getDestinations()) {
LOG.info("dest: " + dest.getTo().getTitle());
}
LOG.info("===============================");
if (route.hasDefault()) {
String fromTitle = item.getNode().getTitle();
String toTitle = route.getDestinations().get(0).getTo()
.getTitle();
session.complete(item, route);
LOG.info("===============================");
LOG.info(item.getId() + " advanced from " + fromTitle
+ " to " + toTitle);
LOG.info("===============================");
advanced = true;
}
}
// fallback if no route was marked as default
if (!advanced) {
session.complete(item, routes.get(0));
String fromTitle = item.getNode().getTitle();
String toTitle = routes.get(0).getDestinations().get(0).getTo()
.getTitle();
LOG.info("===============================");
LOG.info(item.getId() + " advanced from " + fromTitle + " to "
+ toTitle);
LOG.info("===============================");
}
}
}
My question is: after i use session.complete to advance to next step, how can i refresh the workflow session to reach the current step information.
I think #yashahuja is correct. I was poking around and on the aem 'working with workflow' page I found some information about persisting data throw workflow steps using MetaDataMap.
From this page: http://dev.day.com/docs/en/cq/current/workflows/wf-extending.html
"Use workflow metadata to persist information that is required during the lifetime of the workflow. A common requirement of workflow steps is to persist data for future use in the workflow, or to retrieve the persisted data.
Workflow metadata is stored in a MetaDataMap object. The Java API provides the Workflow.getMetaDataMap method that returns the MetaDataMap object. Also, the WorkItem.getWorkflowData method returns a WorkflowData object that provides the same getMetaDataMap object.
Therefore, the workflow MetaDataMap object is available to the OSGi service or ECMA script of a step component."
example:
public void execute(WorkItem item, WorkflowSession session, MetaDataMap args) throws WorkflowException {
MetaDataMap wfd = item.getWorkflow().getMetaDataMap();
wfd.put("mykey", "My Step Value");
Set<String> keyset = wfd.keySet();
Iterator<String> i = keyset.iterator();
while (i.hasNext()){
Object key = i.next();
log.info("The workflow medata includes key {} and value {}",key.toString(),wfd.get(key).toString());
}
In the client side, I am using jQuery to submit a number of pairs to the server.
But the number of entries is variable.
For example:
1: 1.55
2: 2.33
3: 5.66
In the server side, how should I design the Controller so that it could take the POST data and store it in a List / Map?
Something like:
#RequestMapping ("/submitdata")
public #ResponseBody String changeData (???) {
// changes to database
return "Success";
}
EDIT:
I got the answer:
#RequestMapping ("/submitdata")
public #ResponseBody String changeData (#RequestParam Map <String, String> params) {
for (Map.Entry<String, String> entry: params.entrySet()) {
System.out.println ("Key = " + entry.getKey() + " Value = " + entry.getValue());
}
// changes to database
return "Success";
}
You can use #RequestParam annotation to grab POST data
you can use
#ResponseBody Map<String,String> udpateIdentification(HttpServletResponse response, #ModelAttribute("jspObject") Test test,BindingResult result)
The values will be get bind to the ModelAttribute if you have used that in your JSP