I am trying to pass multiple list from AngularJS to Spring controller through a POST call. If I send just one list as mentioned below it works fine on the Spring controller but would like to know the best way to send multiple list from Angular to Spring.
$scope.formData = [];
var AList = [];
AList.push({'a': 1, 'b' :2});
$scope.formData = AList;
$http.post('saveData', $scope.formData).success(function(resp){});
When I try to send multiple list through the same approach but by using push, it is received in the Spring controller as shown below which I think is valid
$scope.formData.push(Alist);
$scope.formData.push(Blist);
I get something like below in Spring Controller.
[[ {a=1, b=2}, {a=3, b=4} ]]
How do I iterate this in Spring Controller and store it to my domain object.
Is this the correct approach or Is there any better ways to do it
Your frontend approach is correct. All you need to do is create an empty array, push there as many objects as you need and post it to the server.
//Create an empty array
$scope.formData = [];
//Push as many objects as you need
$scope.formData.push({'a' : 1, 'b' : 2});
$scope.formData.push({'a' : 3, 'b' : 4});
//Post it to server
$http.post('saveData', $scope.formData).success(function(resp){
//handle response
});
But your Spring side can be improved. Arrays of Objects (Object[]) are generally deprecated because they are not type safe and thus are error prone. They should be replaced with parametrized collections (from Java Collections Framework) whenever possible.
In your case you could apply following steps:
Create class of your domain object or DTO, that corresponds to received json objects.
public class MyDomainObject {
private Integer a;
private Integer b;
public MyDomainObject(){ }
//getters and setters omitted
}
Then in your endpoint method switch #RequestBody type from Object[] to List<MyDomainObject>.
#RequestMapping(path = "/saveDate", method = RequestMethod.POST)
public void postDomainObjects(#RequestBody List<MyDomainObject> myDomainObjects){
//Do some business logic.
//i.e. pass further to service or save to database via repository
}
And you receive a list of objects that are exact java representation of json objects from angular. You could iterate the list in a standard java ways, for example using foreach operator:
for (MyDomainObject obj : myDomainObjects) {
//do something with each object.
//i.e. print value of "a" field
System.out.println("Value of a: " + obj.getA());
}
or using streams in case of java 8:
//Apply some filtering, mapping or sorting to your collection.
//i.e. count sum of "a" field values.
int sumOfAFields = myDomainObjects.stream()
.mapToInt(o -> o.getA())
.sum();
NOTE:
Above solution will work if you have configured object mapper. If you use Spring boot with any of web starters you'll get it for free. In standard spring project (with configuration on your own) you must include jackson library to your project's classpath and register ObjectMapper bean in configuration class or xml.
Related
I have a form that contains multiple radio inputs and one textarea input that I send using axios from a ReactJs client. The request looks like this:
axios.post("/wellbeing/" + params.wellbeingSurveyType, { formAnswersJson: formAnswers })
.then(res => {
// logic
})
.catch(err => {
// logic
})
The 'formAnswers' object looks like this:
I then receive the request from a Spring controller that looks like the following:
#PostMapping("{wellbeingSurveyType}")
public WellbeingSurveySubmission submitSurvey(
#PathVariable WellbeingSurveyType wellbeingSurveyType,
#RequestBody String formAnswersJson) throws JsonProcessingException {
var result = new ObjectMapper().readValue(formAnswersJson, HashMap.class);
return new WellbeingSurveySubmission(); //ignore this
}
When I call the 'toString()' method on the result object it seems to correctly print out the map values:
But when I try to actually operate on the object (which is parsed as a LinkedHashMap) I cannot access the keys or values:
When I try to open up the object using the debugging tool it seems to store a reference to itself as a value:
The result I want is simply a Map<String, String> that represents the JSON but I am unsure why this behavior is happening.
Any help or tips on how to do this in a better way would be greatly appreciated, thank you.
Alright the best way I found to make this work was to deconstruct the JSON object in the axios post request like so:
axios.post("/wellbeing/" + params.wellbeingSurveyType, { ...formAnswers })
.then(res => {
// logic
})
.catch(err => {
// logic
})
Works better as if I just pass the formAnswers object it unnecessarily wraps the object i.e. A hashmap that contains a single key-value pair 'formAnswers'.
Although as The Frozen One mentioned, it would be better to define a dedicated form object and take it as a param in the spring controller.
If you pass a JavaScript object as the 2nd parameter to the axios.post() function, Axios will automatically serialize the object to JSON for you.
So, with this line of code :
axios.post("/wellbeing/" + params.wellbeingSurveyType, { formAnswersJson: formAnswers })
You are basically sending object with key fromAnswersJson and value fromAnswers to your rest controller and Spring will deserilize it like a Map with key fromAnswersJson and value fromAnswers
To get what you want just send your request like this :
axios.post("/wellbeing/" + params.wellbeingSurveyType, formAnswers )
It Seems like the conversion from String to Map in java does not go smoothly from what I see in your printscreen.
Personally, I do not work like that when I handle requests. I create a dto object and give that in the controller as input. The fact that you have variables that the name is a number make that a bit more complicated since java cannot accept that as a valid variable name, but probably (did not test it) can be overcome by using #JsonProperty. So my solution would be the following
#Getter
#Setter
public class MyRequestDto {
#JsonProperty("user-comment")
private String userComment;
#JsonProperty("0")
private String zero;
#JsonProperty("1")
private String one;
#JsonProperty("2")
private String two;
...
}
I added lombok getters and setters ofcourse you can add your own if you don't use lombok.
Then replace the input in your controller
#PostMapping("{wellbeingSurveyType}")
public WellbeingSurveySubmission submitSurvey(
#PathVariable WellbeingSurveyType wellbeingSurveyType,
#RequestBody MyRequestDto request) throws JsonProcessingException {
request.getUserComment()
return new WellbeingSurveySubmission(); //ignore this
}
I'm trying to create a Solr collection on SolrCloud, and I want to pass in the hosts I want the collection to exist on in a certain order, and have Solr follow that order. Solr exposes this functionality in the API with the parameter createNodeSet.shuffle, but I can't explicitly set this parameter in a SolrAdminRequest.Create instance.
Does this functionality not exist within Solrj? Can I set the value with the setProperties() method even though it's a "param"?
I'm facing this problem too, and I notice that you had opened a PR on the GitHub. I've tried several ways to achieve this goal but finally I give up by shuffling the nodes myself, before passing them to the Create request.
In Kotlin:
val nodes = listOf("node1", "node2")
val createNodeSet = nodes.shuffled().joinToString(",")
In Java:
List<String> nodes = Arrays.asList("node1", "node2");
Collections.shuffle(nodes);
String createNodeSet = String.join(",", nodes);
As current Solr now has the constructor marked as protected, only accessible via a static builder, and as I didn't want to have a new class to worry about, I figured out the following way to set the needed parameter.
This method should be usable on many of the other builder created op objects.
Create req = CollectionAdminRequest //
.createCollection(newCollection, newConfigSet, NUM_SHARDS, NUM_REPLICAS);
final SolrParams reqParams = req.getParams();
if (reqParams instanceof ModifiableSolrParams) {
((ModifiableSolrParams) reqParams).set("createNodeSet.shuffle", "false");
}
Hiy!
I want all objects(rows in Test Type) with ModelService
So I could iterate through collection and update a Single row (object)'s attribute with new value
I see getModelService.create(TestModel.class) and getModelService.save()
but will they not create a new object/row rather than update a existing object?right
I don't want to create a new one rather selecting one of the existing matching my criteria and update one attribute of that
can somebody help with List<TestModel> testModels = getModelService.get(TestModel.class) will that return me all rows (collection) of Test Type/Table?
unfortunately I can't test it so need help
Actually I am in validateInterceptor ... and on the basis of this intercepted model changed attribute value I have to update another model attribute value...
thanks
ModelService.create(new TestModel.class) will create a single instance of the specified type and attach it to the modelservice's context.
But it will only be saved to the persistence store when you call modelService.save(newInstance)
ModelService.get() returns a model object but expects a Jalo object as input, (Jalo being the legacy persistence layer of hybris) so that won't work for you.
To retrieve objects you can either write your own queries using the FlexibleSearchService or you can have a look at the DefaultGenericDao which has a bunch of simple find() type of methods.
Typically you would inject the dao like e.g.:
private GenericDao<TestModel> dao;
[...]
public void myMethod()
{
List<TestModel> allTestModels = dao.find();
[...]
}
There are a lot more methods with which you can create WHERE type of statements to restrict your result.
Regarding ValidateInterceptor:
Have a look at the wiki page for the lifecycle of interceptors:
https://wiki.hybris.com/display/release5/Interceptors
It's not a good idea to modify 'all' objects of a type while being an interceptor of that type.
So if you're in an interceptor declared for the Test item type, then don't try to modify the items there.
If you happen to be in a different interceptor and want to modify items of a different type:
E.g. you have Type1 which has a list of Type2 objects in it and in the interceptor for Type1 you want to modify all Type2 objects.
For those scenarios you would have to add the instances of Type2 that you modify to the interceptor context so that those changes will be persisted.
That would be something like:
void onValidate(Test1 model, InterceptorContext ctx) throws InterceptorException
{
...
List<Type2> type2s = dao.find();
for (Type2 type2 : type2s)
{
// do something with it
// then make sure to persist that change
ctx.registerElementFor(type2, PersistenceOperation.SAVE);
[...]
}
}
First of all - i think it's not a good idea, to create/update models in any interceptor, especially in 'validation' one.
Regarding your question:
ModelService in most of the cases works with single model, and
designed for create/update/delete operations.
To retreive all models of certain type, you have to use FlexibleSearchService
Then to update each retrieved TestType model, you can use ModelService's save method.
A query to retreive all TestType models will look like:
SELECT PK FROM {TestType}
You could simply use the Flexible Search Service search by example method, and the model service to save them all. Here is an example using Groovy script, with all products :
import java.util.List
import de.hybris.platform.core.model.product.ProductModel
import de.hybris.platform.servicelayer.search.FlexibleSearchService
import de.hybris.platform.servicelayer.model.ModelService
FlexibleSearchService fsq = spring.getBean("flexibleSearchService")
ModelService ms = spring.getBean("modelService")
ProductModel prd = ms.create(ProductModel.class)
List<ProductModel> products = fsq.getModelsByExample(prd)
//Do Whatever you want with the objects in the List
ms.saveAll(products)
Context
I have the following data structure:
class Birthday(ndb.Model):
day = ndb.IntegerProperty()
month = ndb.IntegerProperty()
year = ndb.IntegerProperty()
class User(ndb.Model):
name = ndb.StringProperty()
birthday = ndb.StructuredProperty(Birthday)
# ... other properties
Problem
When I try to use the populate() method on an instance of User, it gives an error: expecting a Birthday instance instead of a dictionary of params.
If I remove the birthday property, everything works fine: the User instance is populated with the dictionary of params.
Shouldn't the populate() method recognize structured properties and automatically populate them as well?
Any clues?
Thanks
PS: The populate method could also use a forgiving mode on which it ignores unknown properties for which there are references on the params dictionary.
>>Added comments
I'm using a generic REST Handler which is extended for accessing and changing several data types. The extension has to define a method getModel() that returns the model class to access/manipulate. The model class has to implement a few methods, namely create(cls, params).
The POST handler parses params (sent by AngularJS using $resouce -- link below) the following way:
# inside the generic REST Handler
params = json.loads(self.request.body, object_hook=self.datetime_decoder) # parse json params
...
self.getModel().create(params) # invokes the create method of the
The model class implements the create method the following way:
#classmethod
def create(cls, params = None):
obj = cls()
if params:
obj.update(**params)
obj.put()
return True, obj
return False, None
The contents of the JSON dict are:
{"name":"Ana Matos","email":"ana.matos#nvd.com","phone":"+35196983465671","birthday":{"day":1,"month":0,"year":1980},"gender":"FEMALE","groups":["2012/2013"],"serviceProviderId":206133}
JSON contens -- firefox screenshot
AngularJS $resource
Are you reporting a bug or requesting a feature? The populate() method requires its parameter types to match the declared type of the property, which in this case is a Birthday instance.
It would help if you showed the contents of the JSON dict that you are passing to populate() (and exactly how you are passing it).
Possibly the solution is as simple as getting the 'birthday' value from the JSON dict and using it to create a Birthday instance. But I would have to see your code to know for sure.
I have a requirement to design a RESTful Service using RESTEasy. Clients can call this common service with any number of Query Parameters they would want to. My REST code should be able to read these Query Params in some way. For example if I have a book search service, clients can make the following calls.
http://domain.com/context/rest/books/searchBook?bookName=someBookName
http://domain.com/context/rest/books/searchBook?authorName=someAuthor& pubName=somePublisher
http://domain.com/context/rest/books/searchBook?isbn=213243
http://domain.com/context/rest/books/searchBook?authorName=someAuthor
I have to write a service class like below to handle this.
#Path("/books")
public class BookRestService{
// this is what I currently have, I want to change this method to in-take all the
// dynamic parameters that can come
#GET
#Path("/searchBook")
public Response searchBook(#QueryParam("bookName") String bookName,#QueryParam("isbn") String isbn) {
// fetch all such params
// create a search array and pass to backend
}
#POST
#Path("/addBook")
public Response addBook(......) {
//....
}
}
Sorry for the bad format (I couldn't get how code formatting works in this editor!). As you can see, I need to change the method searchBook() so that it will take any number of query parameters.
I saw a similar post here, but couldn't find the right solution.
How to design a RESTful URL for search with optional parameters?
Could any one throw some light on this please?
The best thing to do in this case would be using a DTO containing all the fields of your search criteria. For example, you mentioned 4 distinct parameters.
Book Name (bookName)
Author Name (authorName)
Publisher Name (pubName)
ISBN (isbn)
Create a DTO containing the fields having the following annotations for every property you want to map the parameters to:
public class CriteriaDTO{
#QueryParam("isbn")
private String isbn;
.
.
Other getter and setters of other properties
}
Here is a method doing that for your reference:
#GET
#Produces("application/json")
#Path("/searchBooks")
public ResultDTO search(#Form CriteriaDTO dto){
}
using following URL will populate the CriteriaDTO's property isbn automatically:
your.server.ip:port/URL/Mapping/searchBooks?isbn=123456789&pubName=testing
A similar question was asked here: How do you map multiple query parameters to the fields of a bean on Jersey GET request?
I went with kensen john's answer (UriInfo) instead. It allowed to just iterate through a set to check which parameters were passed.