I'm sending some parameters from a form in this way:
myparam[0] : 'myValue1'
myparam[1] : 'myValue2'
myparam[2] : 'myValue3'
otherParam : 'otherValue'
anotherParam : 'anotherValue'
...
I know I can get all the params in the controller method by adding a parameter like
public String controllerMethod(#RequestParam Map<String, String> params){
....
}
I want to bind the parameters myParam[] (not the other ones) to a list or array (anything that keeps the index order), so I've tried with a syntax like:
public String controllerMethod(#RequestParam(value="myParam") List<String> myParams){
....
}
and
public String controllerMethod(#RequestParam(value="myParam") String[] myParams){
....
}
but none of them are binding the myParams. Even when I add a value to the map it is not able to bind the params:
public String controllerMethod(#RequestParam(value="myParam") Map<String, String> params){
....
}
Is there any syntax to bind some params to a list or array without having to create an object as #ModelAttribute with a list attribute in it?
Thanks
Or you could just do it that way:
public String controllerMethod(#RequestParam(value="myParam[]") String[] myParams){
....
}
That works for example for forms like this:
<input type="checkbox" name="myParam[]" value="myVal1" />
<input type="checkbox" name="myParam[]" value="myVal2" />
This is the simplest solution :)
Arrays in #RequestParam are used for binding several parameters of the same name:
myparam=myValue1&myparam=myValue2&myparam=myValue3
If you need to bind #ModelAttribute-style indexed parameters, I guess you need #ModelAttribute anyway.
Subscribing what basil said in a comment to the question itself, if method = RequestMethod.GET you can use #RequestParam List<String> groupVal.
Then calling the service with the list of params is as simple as:
API_URL?groupVal=kkk,ccc,mmm
Just complementing what Donal Fellows said, you can use List with #RequestParam
public String controllerMethod(#RequestParam(value="myParam") List<ObjectToParse> myParam){
....
}
Hope it helps!
One way you could accomplish this (in a hackish way) is to create a wrapper class for the List. Like this:
class ListWrapper {
List<String> myList;
// getters and setters
}
Then your controller method signature would look like this:
public String controllerMethod(ListWrapper wrapper) {
....
}
No need to use the #RequestParam or #ModelAttribute annotation if the collection name you pass in the request matches the collection field name of the wrapper class, in my example your request parameters should look like this:
myList[0] : 'myValue1'
myList[1] : 'myValue2'
myList[2] : 'myValue3'
otherParam : 'otherValue'
anotherParam : 'anotherValue'
It wasn't obvious to me that although you can accept a Collection as a request param, but on the consumer side you still have to pass in the collection items as comma separated values.
For example if the server side api looks like this:
#PostMapping("/post-topics")
public void handleSubscriptions(#RequestParam("topics") Collection<String> topicStrings) {
topicStrings.forEach(topic -> System.out.println(topic));
}
Directly passing in a collection to the RestTemplate as a RequestParam like below will result in data corruption
public void subscribeToTopics() {
List<String> topics = Arrays.asList("first-topic", "second-topic", "third-topic");
RestTemplate restTemplate = new RestTemplate();
restTemplate.postForEntity(
"http://localhost:8088/post-topics?topics={topics}",
null,
ResponseEntity.class,
topics);
}
Instead you can use
public void subscribeToTopics() {
List<String> topicStrings = Arrays.asList("first-topic", "second-topic", "third-topic");
String topics = String.join(",",topicStrings);
RestTemplate restTemplate = new RestTemplate();
restTemplate.postForEntity(
"http://localhost:8088/post-topics?topics={topics}",
null,
ResponseEntity.class,
topics);
}
The complete example can be found here, hope it saves someone the headache :)
Change hidden field value with checkbox toggle like below...
HTML:
<input type='hidden' value='Unchecked' id="deleteAll" name='anyName'>
<input type="checkbox" onclick="toggle(this)"/> Delete All
Script:
function toggle(obj) {`var $input = $(obj);
if ($input.prop('checked')) {
$('#deleteAll').attr( 'value','Checked');
} else {
$('#deleteAll').attr( 'value','Unchecked');
}
}
Related
I have two services (A and B). I want to send a GET request from service A to service B. Here how my request looks like (Service A):
public Set<StudentDTO> getStudentsByIds(Set<Long> ids) { //here may be a set of ids [123213L, 435564L]
return restTemplate.exchange("localhost:8090/students/all?ids={ids}",
HttpMethod.GET, new HttpEntity<>(ids), new ParameterizedTypeReference<Set<StudentDTO>>() {}, ids).getBody();
}
Here how my Service B controller looks like:
#RequestMapping("/students")
public class StudentController {
#GetMapping("/all")
public Set<StudentDTO> getStudentsByIds(#RequestParam Set<Long> ids) {
return studentService.getStudentsByIds(ids);
}
}
I am having trouble with sending set as parameter. I guess we can't put Set as parameter. I tried already turning set to String and removing the brackets from it like following and it worked:
String ids = ids.toString().substring(1, ids.toString().length() - 1);
But maybe there better solution or is there any solution to send Set?
My url looks like this: localhost:8090/students/all?ids=id1&ids=id2&ids=id3
Your url is being formed incorrectly. By using all?ids={ids} the resulting url sent to the service layer is http://localhost:8090/students/all?ids=%5B23677,%2012345,%201645543%5D. This is because the brackets from the set are being added to the url, but aren't interpreted properly. You can fix this by sending it as a comma delimited String appended to the URL, like this.
public Set<Long> getStudentsByIds(Set<Long> ids){
String studentIdsUrl = "http://localhost:8080/api/all?ids=" + ids.stream().map(Object::toString).collect(Collectors.joining(","));
return restTemplate.exchange(studentIdsUrl,
HttpMethod.GET, new HttpEntity<>(ids), new ParameterizedTypeReference<Set<Long>>() {}, ids).getBody();
}
Heyy,
I want to take a list of data in my request param,here "personIdCollection" is a set of list but when i am hitting through postman i am getting a bad request.
Here is my code.
controller
#PostMapping("/face-tag-data")
public String getFaceTaggedData(#RequestParam String projectId,#RequestParam List<String> personIdCollection) {
return null;
}
and here is my ajax
var data = {};
data.personIdCollection = personIdCollection;
data.projectId = $("#projectId").val();
$.ajax({
type:'POST',
url:contextPath+'/face-tag-data',
data:data,
success:function(resp){
console.log(resp);
},
failure:function(resp){
console.log(resp);
}
});
This is working for me. I do not use an ajax-request but instead submit my form directly but it should work either way.
My controller looks like:
#RequestMapping(value="addSingleArticles", method=RequestMethod.POST)
public #ResponseBody String addSingleArticles(
ArticleSubmitData pupilPackageData,
HttpServletRequest request) {
... // do something with the data
}
As you can see I have defined my own composite type which consists of three lists. So you obviously can use it with only one list directly.
public class ArticleSubmitData {
private List<Article> singleArticles;
private List<Article> packageArticle;
private List<Article> popupArticles;
... // getter & setter, inner classes etc.
}
In my server page oder faclet I use the following html-code to make this work
...
<input id="" class="" type="text" name="singleArticles[${line.index}].engraving" />
...
So the trick is to define the variables on your webpage as an array and use this in your controller as a list. As you can see in my example I also use an inner class in my composite class which has extra attributes.
in order to write a clean and smart code, I'm wondering what can I do to improve my actual piece of code:
public JSONObject getCustomer(final String customerId) {
if (customerId == null || customerId.equals("")) {
return null;
} else {
final RestTemplate restTemplate = new RestTemplate();
final String result = restTemplate.getForObject("http://localhost:6061/customers/" + customerId,
String.class);
return new JSONObject(result);
}
}
Especially, I didn't like the way I composed the url, neither the check on customerId's value.
I'd like to have something like JPA, where I ask some information passing a parameter, just to be clear (in pseudocode):
public JSONObject getCustomer(final String customerId) {
final RestTemplate restTemplate = new RestTemplate();
final Query query = restTemplate.query("http://localhost:6061/customers/:customerId");
query.addParameter("customerId", customerId);
JSONObject result = query.getForObject();
return result;
}
Then, if customerId would be null or some white spaces or not existing, I'd like that result would be null.
Is there a way to do this with a standard library?
Thanks
First off, I would remove the else branch and refactor the condition to:
public JSONObject getCustomer(final String customerId) {
if (isNull(customerId) || customerId.trim().isEmpty()) {
return null;
}
...
}
Second, if you have a bunch of URI variables, Spring guys recommend using a Map<String, String>:
final String templateURL = "http://localhost:6061/customers/{customerId}";
final Map<String, String> variables = new HashMap<>();
variables.put("customerId", customerId);
...
template.getForObject(templateURL, String.class, variables);
Third, the method shouldn't create a RestTemplate instance on its own. I would prefer injecting the already-tuned object into an instance field:
getTemplate().getForObject(templateURL, String.class, variables);
Finally, I would name the result more meaningful:
final String customerRepresentation = ...;
Some notes:
getCustomer actually returns a JSONObject, not a Customer.
templateURL hardcoded the base URL as well as the URL to customers.
The method does a lot of work (takes too much responsibility) - argument validation, URL construction, making a request. Try to split these responsibilities between corresponding methods.
Firstly I would rather use DTO objects to hold the response data and manipulate them rather than using a String representation of the payload. So you may change it like this. Here Jackson takes care of all the serialization and deserialization of your data.
CustomerDTO customerDTO = restTemplate
.getForEntity("http://localhost:6061/customers/{customerId}", CustomerDTO.class, customerId).getBody();
You can use javax.validators such as #Min, #NotEmpty etc at your controller to check for the empty values. A sample is given below.
#RequestMapping(value = someURL, params = {"id"})
public SomeResponse doSomething(#PathVariable(value = "id") #Size(min=1) String id)
This throws a ValidationException with a relevant error message which can be customized by you. You then need to have an error handling aspect that sets the error message in ErrorDTO object and set the status code appropriately.
I tried binding N numbers of parameters in a URL like this
/web/appl/<applname>/rule/<rulename>/<attrname1>/<attrval1>/<attrname2>/<attrval2>/.../<attrnameN>/<attrvalN>
with
#RequestMapping(value = "/web/appl/{applname}/rule/{rulename}/{attributes}", method = RequestMethod.GET)
public Object GetService(HttpServletRequest request, #PathVariable("attributes") Map<String, Object> map,
#PathVariable("applname") String applname, #PathVariable("rulename") String rulename)
throws Exception {
...
}
but could not get values of <attrval1>/<attrname2>/<attrval2>/.../<attrnameN>/<attrvalN>
Unfortunately Spring MVC does not provide such a solution. You can consider the Matrix Variables as an alternative.
If you prefer sticking to your current URI scheme then you have to implement a solution yourself. One approach is to use a path pattern. Example:
#RequestMapping(value = "/web/appl/{applname}/rule/{rule name}/**")
public Object getService(HttpServletRequest request,
#PathVariable("applname") String applname ...) {
String attributesPart = new AntPathMatcher()
.extractPathWithinPattern("/web/appl/{applname}/rule/{rule name}/**",
request.getServletPath());
...
You could implement your argument resolver that does that. Something like
#RequestMapping(value = "/web/appl/{applname}/rule/{rule name}/**")
public Object getService(#MyAttributes Map<String, String> attributes,
#PathVariable("applname") String applname ...) {
Use
String urlAttributes = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
and get whole URL. After that parse this URL according to your needs like if you did not get
<attrval1>/<attrname2>/<attrval2>/.../<attrnameN>/<attrvalN>
in proper sequence then you can throw exception.
I've inherited a REST API that is using JBoss, Spring, Hibernate, etc. and I'm new to all these so please forgive what seems like a dumb question (to me).
The basic flow when performing a GET is to build an entity list add it to a ModelAndView object then return it.
QUESTION:
How can I add a calculated value to a response?
I don't want to store calculated values in the database, nor do I want to add a member to each entity that is being returned (ie an ArrayList<Athletes> and add a "points behind the leader" member to the Athletes class). I simply want to perform the calculation on for each request and append a member to the json response with that dynamic value like so:
{
"Athlete1" : {"name" : "bob", "someDynamicValue" : "124548412" }
}
Seems like this would be relatively common but I can't seem to find a simple solution.....maybe i'm just not sure how to ask the question correctly.
Any help would be appreciated.
PS - I thought this would help me Returning entities in Rest API with Spring but the answer never addressed my questions, how to add dynamic values to the response.
EDIT: Per request here's some simplified code
public ModelAndView getAthletes(HttpServletRequest req, HttpServletResponse res)
throws WebServiceException {
res.setContentType("text/xml");
List<AthleteStat> athleteStats = athleteManager.getAthleteStats();
Map<String, Object> model = new HashMap<String, Object>();
model.put("athleteStats", athleteStats);
ModelAndView modelAndView = new ModelAndView("athletes.ftl", "model", model);
modelAndView.setCacheable(true);
return modelAndView;
}
public class AthleteStat{
private Long id;
private String name;
private String hometown;
private String resides;
private Date birthdate;
//getters and setters
}
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<#escape x as x?xml>
<response status="ok">
<Data>
<Athletes>
<#list model.athleteStats as athleteStat>
<Athlete Name="${athleteStat.athlete.name}" InternalID="${athleteStat.athlete.id?c}">
<AbbrevName><#if athleteStat.athlete.abbrevName?exists>${athleteStat.athlete.abbrevName}</#if></AbbrevName>
<Birthdate><#if athleteStat.athlete.birthdate?exists>${athleteStat.athlete.birthdate?string("yyyy-MM-dd'T'HH:mm:ss")}</#if></Birthdate>
<Hometown><#if athleteStat.athlete.hometown?exists>${athleteStat.athlete.hometown}</#if></Hometown>
<Resides><#if athleteStat.athlete.resides?exists>${athleteStat.athlete.resides}</#if></Resides>
<Age><#if athleteStat.athlete.age?exists>${athleteStat.athlete.age}</#if></Age>
</Athlete>
</#list>
</Athletes>
</Data>
</response>
</#escape>
You can put in the model an additional map which will map those dynamic values to each athlete.
Like this: Where you have
model.put("athleteStats", athleteStats);
Add the generation of the dynamic value map and add it to the model:
model.put("athleteStats", athleteStats);
Map<Integer, String> dynamicValues = new HashMap<Integer, String>();
for(AthleteStat athleteStat : athleteStats) {
String dynValue = athleteStat.toString(); /* calculate the dynamic value */
dynamicValues.put(athleteStat.getId(), dynValue);
}
model.put("dynamicValues", dynamicValues);
Now add this line to map it:
<Dynamic>
<#if model.dynamicValues.get(athleteStat.athlete.id)?exists>
${model.dynamicValues.get(athleteStat.athlete.id)}
</#if>
</Dynamic>
Like:
...
<Age><#if athleteStat.athlete.age?exists>${athleteStat.athlete.age}</#if></Age>
<Dynamic><#if model.dynamicValues.get(athleteStat.athlete.id)?exists>${model.dynamicValues.get(athleteStat.athlete.id)}</#if></Dynamic>
</Athlete>
</#list>