I want to store some data from an HTML form (working with AngularJS) into my database, using Spring.
For this, I'm using the #RequestBody annotation with a POJO, but I can't make it work: my POJO is instancied, but it looks like the POJO attributes are not mapped with my form values (they are all null).
Controller :
#RequestMapping(value = "/createEntities", method = RequestMethod.POST)
#ResponseBody
public List<Entity> createEntities(#RequestBody final EntityList resource, #RequestParam final String kind) {
System.out.println("Creating entity for: " + kind);
Preconditions.checkNotNull(resource);
List<Entity> newEntities = new ArrayList<Entity>();
System.out.println("Entity test = " + resource.getTest()); // Prints "Entity test = null"
// Code below returns NullException
//System.out.println("Entity list nb = " + resource.getEntity().size());
if (resource.getEntities() != null && !resource.getEntities().isEmpty()) {
System.out.println("Entity list is OK");
for (EntityForm eForm : resource.getEntities()) {
if (eForm.getGrant() != null) {
Entity ent = new Entity();
if ("RTS".equals(kind)) {
ent.setDept(deptService.findByAbr(DeptEnum.RTS.name()));
} else {
ent.setDept(deptService.findByAbr(DeptEnum.RTB.name()));
}
ent.setGrant(eForm.getGrant());
ent.setCountry(eForm.getCountry());
ent.setName(eForm.getName());
ent = service.create(ent);
newEntities.add(ent);
}
}
}
return newEntities;
}
EntityList is the POJO for my form. This POJO contains a list of EntityForm (+ a string for test purpose), which is a DTO for my database entity Entity.
EntityList POJO :
public class EntityList implements Serializable {
private static final long serialVersionUID = 6335318686899794229L;
private List<EntityForm> entities;
private String test;
public EntityList() {
super();
}
public EntityList(List<EntityForm> entities, String test) {
super();
this.entities = entities;
this.test = test;
}
public List<EntityForm> getEntities() {
return entities;
}
public void setEntities(List<EntityForm> entities) {
this.entities = entities;
}
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
I thought the problem came from a bad mapping between my list of entities in my form and my List<EntityForm> in my POJO, that's why I added a simple String to my POJO.
AngularJS side
Service :
app.factory("Entities", function($resource) {
return $resource("api/auth/entities", null,
{
createEntities: {method:'POST', url: "api/auth/entities/createEntities", params: { kind: '#kind' }, isArray:true}
});
})
Controller :
$scope.EntForm = {};
$scope.EntForm.entities = [];
$scope.EntForm.test = "myTest";
/* ... */
$scope.saveEnt= function() {
console.log($scope.EntForm);
Entities.createEntities($scope.EntForm,{kind:"RTS"},function(res) {
var msg = 'Entities created...';
ngToast.create(msg);
$location.path("/entities");
});
}
In my firefox console, I see that $scope.EntForm is correctly set (I have all my entity objects with the fields set, as well as the test string defined in the controller).
Result
All this code will display :
Creating entity for: RTS
Entity test = null
What am I doing wrong ?
Have you checked out the POST payload with Firefox developer tools, is your custom createEntities method working correctly?
(Would have added this as a comment, but unfortunately I don't yet have enough reputation for that.)
I had to remove the #RequestParam final String kind part from my Spring controller, and the param in AngularJS code.
To get the kind, I just added $scope.EntForm.kind = "theValueIWant" in my AngularJS controller.
I don't know if it's a good way to make it work in terms of good practice, but I get the #RequestBody content now.
Related
The non-reactive version of this code works fine. But in the reactive version, something happens when mapping a null or empty collection from the database.
The POST of a new Template object returns a 201 with nothing unusual in the logs. But, when I do the GET on Template, the listAll() returns the error below.
I've tried initializing the "sections" member to an empty collection, but the result is the same.
What am I missing?
The Reactive Entity:
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
#Entity
public class Template extends PanacheEntity {
public String name;
#OneToMany(mappedBy = "template", cascade = CascadeType.ALL)
public List<Section> sections;
}
The Resource API:
#GET
#Path("template")
public Uni<List<Template>> listTemplates() {
return Template.<Template>listAll();
}
#POST
#Path("template")
#Consumes("application/json")
#Produces("application/json")
#ReactiveTransactional
public Uni<Response> addTemplate(Template template) {
return Panache.<Template>withTransaction(template::persist)
.onItem().transform(inserted -> {
return createdResponse("/template/%d", inserted.id);
});
}
The Dependencies:
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
<artifactId>quarkus-resteasy-reactive</artifactId>
<artifactId>quarkus-reactive-pg-client</artifactId>
The error:
JsonMappingException: HR000056: Collection cannot be initialized: score.Template.sections (through reference chain: java.util.ArrayList[0]->score.Template["sections"])
try this workaround fetch = FetchType.EAGER
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
#Entity
public class Template extends PanacheEntity {
public String name;
#OneToMany(mappedBy = "template", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
public List<Section> sections;
}
I have the following object:
{
"id" : "sampleId";
foos : [{
"prop1":"value1",
"prop2":"value2"
},
{
"prop1":"value3",
"prop2":"value4"
}
]}
How can I get foos, where prop2 is value4? I'm using Spring data mongodb.
If you use spring data mongodb repositories, it can make your life easy. You will need a domain class. e.g.
public class Foo {
String prop1;
String prop2;
}
public class MyClass {
String id;
List<Foo> foos;
}
public interface MyClassRepository extends MongoRepository<MyClass, String> {
List<MyClass> findByFoosProp2(String prop2ValueToLookFor);
// assuming you can find multiple matches
}
Then simply call this method from your code
public clsss SomeBusinessClass {
#Autowired
private MyClassRepository repository;
public void mySomeBusinessMethod() {
List<MyClass> myObjects = repository.findByFoosProp2("value4");
}
}
This code will return a SampleBean with id sampleId wich will have only matching items in collection foos.
// Match one document with sampleId
Query q1 = new Query(where("id").is("sampleId"));
// match only foos with prop2 value value2
Query q2 = query(where("foos").elemMatch(where("prop2").is("value2))
BasicQuery query = new BasicQuery(q1.getQueryObject(), q2.getQueryObject());
SampleBean result = mongoTemplate.findOne(query, SampleBean.class)
I'm trying to consume a RESTful JSON web service using WCF on the client side. The service is 3rd party, so I cannot make any changes to the server response.
The server is sending back a response that looks something like this when there's only one data point...
Single Data Point
{
"Data":
{
"MyPropertyA":"Value1",
"MyPropertyB":"Value2"
},
}
and something like this when there's more than one data point...
Multiple Data Points
{
"Data":
[
{
"MyPropertyA":"Value1",
"MyPropertyB":"Value2"
},
{
"MyPropertyA":"Value3",
"MyPropertyB":"Value4"
},
{
"MyPropertyA":"Value5",
"MyPropertyB":"Value6"
}
],
}
I have my service contract set up like this...
[ServiceContract]
public interface IRewardStreamService
{
[OperationContract]
[WebInvoke]
MyResponse GetMyStuff();
}
and a data point's data contract like this...
[DataContract]
public class MyData
{
[DataMember]
public string MyPropertyA { get; set; }
[DataMember]
public string MyPropertyB { get; set; }
}
and the only way I can get the single data point response to work is if I have a single instance property like this, but this does not parse the multiple data point response...
Response for Single Instance
[DataContract]
public class MyResponse
{
[DataMember]
public MyData Data { get; set; }
}
and the only way I can get the multiple data point response to work is if I have an array / list instance property like this, but this does not parse the single data point response...
Response for Multiple Instance
[DataContract]
public class MyResponse
{
[DataMember]
public IList<MyData> Data { get; set; }
}
I understand the issue is that the response is omitting the brackets when there's only one data point returned, but it seems that WCF doesn't play well with deserializing that syntax. Is there some way I can tell the DataContractJsonSerializer to allow single element arrays to not include brackets and then tell my service to use that serializer? Maybe a service behavior or something?
Any direction would be helpful.
You can use a custom message formatter to change the deserialization of the JSON into the data contract you want. In the code below, the data contract is defined to have a List<MyData>; if the response contains only one data point, it will "wrap" that into an array prior to passing to the deserializer, so it will work for all cases.
Notice that I used the JSON.NET library to do the JSON modification, but that's not a requirement (it just has a nice JSON DOM to work with the JSON document).
public class StackOverflow_12825062
{
[ServiceContract]
public class Service
{
[WebGet]
public Stream GetData(bool singleDataPoint)
{
string result;
if (singleDataPoint)
{
result = #"{
""Data"":
{
""MyPropertyA"":""Value1"",
""MyPropertyB"":""Value2""
},
}";
}
else
{
result = #"{
""Data"":
[
{
""MyPropertyA"":""Value1"",
""MyPropertyB"":""Value2""
},
{
""MyPropertyA"":""Value3"",
""MyPropertyB"":""Value4""
},
{
""MyPropertyA"":""Value5"",
""MyPropertyB"":""Value6""
}
],
} ";
}
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
return new MemoryStream(Encoding.UTF8.GetBytes(result));
}
}
[DataContract]
public class MyData
{
[DataMember]
public string MyPropertyA { get; set; }
[DataMember]
public string MyPropertyB { get; set; }
}
[DataContract]
public class MyResponse
{
[DataMember]
public List<MyData> Data { get; set; }
public override string ToString()
{
return string.Format("MyResponse, Data.Length={0}", Data.Count);
}
}
[ServiceContract]
public interface ITest
{
[WebGet]
MyResponse GetData(bool singleDataPoint);
}
public class MyResponseSingleOrMultipleClientReplyFormatter : IClientMessageFormatter
{
IClientMessageFormatter original;
public MyResponseSingleOrMultipleClientReplyFormatter(IClientMessageFormatter original)
{
this.original = original;
}
public object DeserializeReply(Message message, object[] parameters)
{
WebBodyFormatMessageProperty messageFormat = (WebBodyFormatMessageProperty)message.Properties[WebBodyFormatMessageProperty.Name];
if (messageFormat.Format == WebContentFormat.Json)
{
MemoryStream ms = new MemoryStream();
XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(ms);
message.WriteMessage(jsonWriter);
jsonWriter.Flush();
string json = Encoding.UTF8.GetString(ms.ToArray());
JObject root = JObject.Parse(json);
JToken data = root["Data"];
if (data != null)
{
if (data.Type == JTokenType.Object)
{
// single case, let's wrap it in an array
root["Data"] = new JArray(data);
}
}
// Now we need to recreate the message
ms = new MemoryStream(Encoding.UTF8.GetBytes(root.ToString(Newtonsoft.Json.Formatting.None)));
XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max);
Message newMessage = Message.CreateMessage(MessageVersion.None, null, jsonReader);
newMessage.Headers.CopyHeadersFrom(message);
newMessage.Properties.CopyProperties(message.Properties);
message = newMessage;
}
return this.original.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
throw new NotSupportedException("This formatter only supports deserializing reply messages");
}
}
public class MyWebHttpBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
IClientMessageFormatter result = base.GetReplyClientFormatter(operationDescription, endpoint);
if (operationDescription.Messages[1].Body.ReturnValue.Type == typeof(MyResponse))
{
return new MyResponseSingleOrMultipleClientReplyFormatter(result);
}
else
{
return result;
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new WebHttpBinding(), new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior());
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetData(false));
Console.WriteLine(proxy.GetData(true));
Console.Write("Press ENTER to close the host");
((IClientChannel)proxy).Close();
factory.Close();
Console.ReadLine();
host.Close();
}
}
I don't know about using WCF so I'll change to Asp.Net WCF. Here is an article that will get you one the way
http://www.west-wind.com/weblog/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing
I just can't figure out how to determine if it's an array or a single object. Here is a little code.
[TestMethod]
public void SingleObject()
{
using (var client = new HttpClient())
{
var result = client.GetStringAsync("http://localhost:8080/api/JSONTestOne");
string content = result.Result;
JObject jsonVal = JObject.Parse(content);
dynamic aFooObj = jsonVal;
Console.WriteLine(aFooObj.afoo.A);
}
}
[TestMethod]
public void ArrayWithObject()
{
using (var client = new HttpClient())
{
var result = client.GetStringAsync("http://localhost:8080/api/JSONTest");
string content = result.Result;
JObject jsonVal = JObject.Parse(content);
dynamic foos = jsonVal;
Console.WriteLine(foos[0].A);
}
}
I am brand new to both REST and RESTlet- I got everything up and communicating last night but what I found this morning is that everything I pass into the server is always becoming null.
just as a sample app i have the following - a User Objectify entity (id, emailAddress, and version), and a RESTUserProxy object (id, emailAddress) - I wasn't originally sure if i could pass Objectify Entities back and after not being able to see anything switched it to the Proxy object - if i can get it to work this way I will try switching it back
the front end is as follows:
public interface RESTUserResourceProxy extends ClientProxy {
#Get
public void find(String emailAddress, Result<RESTUserProxy> callback);
#Put
public void persist(RESTUserProxy user, Result<Void> callback);
#Delete
public void delete(RESTUserProxy user, Result<Void> callback);
}
the backend code is as follows (this is currently extremely ugly - i got a little frustrated just trying to see something and put in a ton of sysouts)
public class RESTUserServerResource extends ServerResource implements RESTUserResource {
private final UserDao userDao;
public RESTUserServerResource() {
System.out.println("CREATED USER RESOURCE IMPL");
userDao = new UserDao();
}
#Override
#Get
public RESTUserProxy find() {
System.out.println("reference = " + getReference());
Form queryParams = getReference().getQueryAsForm();
System.out.println("query params = " + queryParams);
System.out.println("query = " + getQuery());
System.out.println("query string = " + getQuery().getQueryString());
String searchQuery = (String) getRequest().getAttributes().get("searchQuery");
System.out.println("search query = " + searchQuery) ;
return null;
// if (emailAddress == null) {
// return null;
// }
// System.out.println("user resource impl find [" + emailAddress + "]");
// final User user = userDao.find(emailAddress.getText());
// if (user != null) {
// System.out.println("found user ");
// return new RESTUserProxy(user.getId(), user.getEmailAddress());
// } else {
// System.out.println("found absolutely nothing");
// return null;
// }
}
#Override
#Put
public void persist(RESTUserProxy userProxy) {
System.out.println("user proxy = " + userProxy);
if (userProxy == null) {
return;
}
final User user = userDao.find(userProxy.getId());
user.setEmailAddress(userProxy.getEmailAddress());
user.setId(userProxy.getId());
userDao.persist(user);
}
#Override
#Delete
public void delete(RESTUserProxy userProxy) {
final User user = userDao.find(userProxy.getId());
userDao.delete(user);
}
}
what im having problems with is that eerythings coming through as null - a lot of other answers on here said to get the query to get the params - but here the query is null
below is the output of calling find and persist
reference = http://127.0.0.1:8888/users/123
query params = []
query = []
query string =
search query = null
i'm sure i'm doing something stupid here i just have no idea how to proceed right now. Any help as to what i'm doing wrong would be greatly appreciated.
This is due to GAE not supporting chunked encoding. See workaround here:
http://wiki.restlet.org/docs_2.1/13-restlet/21-restlet/318-restlet/303-restlet.html#dsy303-restlet_gwt
I want to define a SuggestBox, which behaves like the search bar in Google Maps: When you begin to type, real addresses, starting with the typed letters, appear.
I think, that I need to use the Geocoder.getLocations(String address, LocationCallback callback) method, but I have no idea how to connect this with the oracle, which is needed by the suggest box to produce its suggestions.
Can you please give me ideas how do I connect the getLocations Method with the SuggestOracle?
I solved this by implementing a subclass of SuggestBox, which has it's own SuggestOracle. The AddressOracle deals as a Wrapper for the Google Maps Service, for which the class Geocoderin the Google Maps API for GWT offers abstractions.
So here is my solution:
First we implement the Widget for a SuggestBox with Google Maps suggestions
public class GoogleMapsSuggestBox extends SuggestBox {
public GoogleMapsSuggestBox() {
super(new AddressOracle());
}
}
Then we implement the SuggestOracle, which wraps the Geocoder async method abstractions:
class AddressOracle extends SuggestOracle {
// this instance is needed, to call the getLocations-Service
private final Geocoder geocoder;
public AddressOracle() {
geocoder = new Geocoder();
}
#Override
public void requestSuggestions(final Request request,
final Callback callback) {
// this is the string, the user has typed so far
String addressQuery = request.getQuery();
// look up for suggestions, only if at least 2 letters have been typed
if (addressQuery.length() > 2) {
geocoder.getLocations(addressQuery, new LocationCallback() {
#Override
public void onFailure(int statusCode) {
// do nothing
}
#Override
public void onSuccess(JsArray<Placemark> places) {
// create an oracle response from the places, found by the
// getLocations-Service
Collection<Suggestion> result = new LinkedList<Suggestion>();
for (int i = 0; i < places.length(); i++) {
String address = places.get(i).getAddress();
AddressSuggestion newSuggestion = new AddressSuggestion(
address);
result.add(newSuggestion);
}
Response response = new Response(result);
callback.onSuggestionsReady(request, response);
}
});
} else {
Response response = new Response(
Collections.<Suggestion> emptyList());
callback.onSuggestionsReady(request, response);
}
}
}
And this is a special class for the oracle suggestions, which just represent a String with the delivered address.
class AddressSuggestion implements SuggestOracle.Suggestion, Serializable {
private static final long serialVersionUID = 1L;
String address;
public AddressSuggestion(String address) {
this.address = address;
}
#Override
public String getDisplayString() {
return this.address;
}
#Override
public String getReplacementString() {
return this.address;
}
}
Now you can bind the new widget into your web page by writing the following line in the onModuleLoad()-method of your EntryPoint-class:
RootPanel.get("hm-map").add(new GoogleMapsSuggestBox());