My requirement is to pass multiple class object(which is going to map with request param or request body in rest controller) in angular $http service.
I can't pass class object in request body since one http call has only one request body.
When i'm trying to pass class object in $http param I'm getting class cast exception.
is there any way to pass multiple object in single $http call ??
#RequestMapping(value = "CommitObject", method = RequestMethod.POST)
public void Commit(#RequestParam(value = "clazz") final String clazz,
#RequestParam(value = "Id") final String modelId,#RequestBody LinkedHashMap<String, Object> obj) {
mService.Commit(Id,clazz workingmodelObj);
}
I tried it many times but always failed.So to overcome this I use to create a Java Bean and encapsulate both objects into it at then accepting it in #RequestBody.
Your Wrapper should look like this:
public class SampleWrapper {
private Object1 object1;
private Object2 object2;
//getters setters and constructors go here
}
And your constructor should look like this:
#RequestMapping(value = "/url",method= RequestMethod.POST)
public String getRequest(#RequestBody SampleWrapper wrapper) {
Object1 o1 = wrapper.getObject1();
Object2 o2 = wrapper.getObject2();
}
JSON request should look like this:
{
"object1" : {//object1 body },
"object2" : {//object2 body }
}
If you're trying to make a $http.post request you can make an array of the objects you are trying to send and then attach it in the request.
$http.put('ROUTE HERE', Array Of Objects);
You can pass Objects by appending it in formData
Angular side
var formData=new FormData();
formData.append("obj1",new Blob([JSON.stringify(obj1)],{type: "application/json"}));
formData.append("obj2",new Blob([JSON.stringify(obj2)],{type: "application/json"}));
http({
method: 'POST',
url: '/MyProject/requestUrl',
headers: { 'Content-Type': undefined},//to set boundary value by default
data: formData,
})
Spring side
#RequestMapping(value = { "/requestUrl" }, consumes = { "multipart/form-data" }, method = RequestMethod.POST)
#ResponseBody
public String getRequest(#RequestPart("obj1") Object1 object1,
#RequestPart("obj2") Object2 Object2,HttpSession session)
throws IOException {
//implementation
}
Related
I'm new to Typescript and started using it to play with Ionic. I come from a Java background and I'm experiencing some trouble getting accustomed to it's syntax and way of doing things. Specifically creating new object instances and getting them initialized.
Recently I came across this issue which I cannot explain. I'm sending a HTTP GET request and getting it's response in a Javascript Object.
To work with this response, I'm trying to map this Javascript Object I received to a typescriptclass of mine which I called HttpResponse, creating a new instance of it when I receive the response. To make things easier I created a simple fiddle which ilustrates my problem.
You can find the quick written fiddle on this link
As you can see on the output, object data is there but when I invoke the HttpResponse it just creates an empty instance, it contains nothing, and I don't understand why it does so.
Any help appreciated! Here's the code:
class HttpResponse {
constructor(status: number = 0,
data: string = '',
headers: Object = '',
error: string = ''){}
}
class Page {
response: HttpResponse;
retrieveData(): void {
document.body.innerHTML += 'inside retrieveData()<br>';
let data = {
status: 200, data: "this is some fake data",
headers: { foo: 'foo', bar: 'bar', baz: 'baz' }
}
document.body.innerHTML +='Data: ' + JSON.stringify(data) + '<br>';
this.response = new HttpResponse(data.status, data.data, data.headers);
document.body.innerHTML += JSON.stringify(this.response);
}
}
new Page().retrieveData();
In the constructor of your HttpResponse class, you are not setting the properties of the class with the passed arguments. You can do so by using the public qualifier in the parameters.
class HttpResponse {
constructor(public status: number = 0,
public data: string = '',
public headers: Object = '',
public error: string = ''){ }
}
See the updated Fiddle.
Alternatively, you can declare the properties in your class and set them in the constructor like this.data = data; assignments.
Because your HttpResponse does not actually have any properties. You only have constructor parameters and nothing else. If you want to automatically convert your constructor parameters into properties, you can do so by using parameter properties:
class HttpResponse {
constructor(
public status: number = 0,
public data: string = '',
public headers: Object = '',
public error: string = ''){}
}
You must have to declare constructor parameters using qualifiers i.e. private public protected
class HttpResponse {
constructor(
private status: number = 0,
private data: string = '',
private headers: Object = '',
private error: string = ''){}
}
I'm trying to post a object that includes an id (int) and a array, but I get a http 400 Bad request response from the server side. This is what I have so far...
Java Bean Object for Request:
public class GuardarVentaRequest {
private Integer idCliente;
private List<Venta> venta; ... (Getters and Setters code)
Java Object:
public class Venta {
private Integer id;
private String nombre;
private Integer precio;
private Integer cantidad;
private Integer total; ... (Getters and Setters code)
Java Controller:
#RequestMapping(value = "/guardarVenta", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody void venta(#RequestBody GuardarVentaRequest factura){
System.out.println(factura);
}
AngularJS Service:
function guardarVenta(array){
let factura = {
idCliente : parseInt($('#cliente').val()),
venta : array,
};
console.log(factura);
$http({
method: 'POST',
url: '/blue/guardarVenta',
contentType: 'application/json',
data: factura
}).then(
function successCallback(response){
console.log(response.statusText);
},
function errorCallback(response){
console.log(response.statusText);
}
);
}
Array:
$scope.productos = new Array();
let productoInfo = {
id: $scope.producto.id,
nombre: $scope.producto.nombre,
precio: $scope.producto.precio,
cantidad: $scope.cantidadProducto,
total: $scope.producto.precio * $scope.cantidadProducto
}
$scope.productos.push(productoInfo);
Output:
ADVERTENCIA: Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException: Could
not read document: Can not construct instance of com.blue.beans.Venta: no
suitable constructor found, can not deserialize from Object value (missing
default constructor or creator, or perhaps need to add/enable type
information?)
at [Source: java.io.PushbackInputStream#663359f3; line: 1, column: 28]
(through reference chain: com.blue.beans.GuardarVentaRequest["venta"]-
>java.util.ArrayList[0]); nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Can not construct
instance of com.blue.beans.Venta: no suitable constructor found, can not
deserialize from Object value (missing default constructor or creator, or
perhaps need to add/enable type information?)
at [Source: java.io.PushbackInputStream#663359f3; line: 1, column: 28]
(through reference chain: com.blue.beans.GuardarVentaRequest["venta"]-
>java.util.ArrayList[0])
Chrome's Network Tab Output
Any ideas?
Try these 3 things.
Add the default constructor to GuardarVentaRequest and Venta.
GuardarVentaRequest(){} &
Venta(){}
Check if a HTTPMessageConverter has been added to your spring config. Eg: MappingJackson2MessageConverter (Check for compatibility with your spring version).
Try serializing the request payload by using angular.toJson
Hope that helps!
In my web Api project I am using parameterized methods and not calling by method name. I am using $resource in my angular code and this call works perfect.
For example this gets me a list of contacts:
public class LeadsController : ApiController
{
public IEnumerable<Contact> Get()
{
var contacts = new ContactRepository();
return contacts.BuildContacts();
}
}
The only problem I have is the casing so then I use newtonsoft and have to change the return type to string for it to work
public class LeadsController : ApiController
{
public string Get()
{
var contacts = new ContactRepository();
var settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
return JsonConvert.SerializeObject(contacts, Formatting.Indented, settings);
}
}
Now the issue I have is the angular is expecting an array but it is returning an object (string) so I am having errors. Here is my angular
return $resource('http://localhost:33651/api/Leads/', {
get: { method: 'GET', isArray: false },
query: {
method: 'GET',
isArray: true
}
});
Is there a way on my web api method I can return IEnumerable and have the Json formatted in camel case correctly? What is the best way I should handle my situation?
Thanks for the help
Web API already uses Newtonsoft serializer, so you need only to configure it. Add these lines to WebApiConfig.Register method:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
And return IEnumerable<Contact> instead of the string, please take a look at this sample project https://github.com/maxbrodin/camel-case-serialization-webapi
Edited
I was able to serialize my form and pass the object to my MVC controller. Here is a cut down version of the code.
public void Test(ComplexType model)
{
// do stuff with model
}
with the JavaScript:
$.ajax({
type: 'POST',
url: '/CmaTestRun/Test/',
data: $('#theForm').serializeArray()
}).......
However, I then realized that I needed to pass additional data along with the serialized form so I made the following changes.
I created a class to that would hold the original ComplexType and an integer value and would be passed to the controller:
public class TestObject
{
public int TestId { get; set; }
public ComplexType TestModel { get; set;}
}
And my controller action:
public void Test(TestObject model)
{
}
Finally, in my JavaScript, I made the following changes:
var TestObject = {
TestId:99,
ComplexType: $('#theForm').serializeArray()
}
$.ajax({
type: 'POST',
url: '/CmaTestRun/Test/',
data: TestObject
})
When I run and step into the controller, the TestObject is passed and TestId is 99. However, although my ComplexType has the correct structure, its properties are all null.
How should I change the code so that everything is populated correctly?
EDIT - Serialized Form
The properties are a bit different from the original post. The collapsed objects follow the same structure as the expanded ones.
Try this, first serialize the form and then push extra data.
var params = $('#theForm').serializeArray();
params.push({name: 'testId', value: 99});
$.ajax({
type: 'POST',
url: '/CmaTestRun/Test/',
data: params
})
and you can also use jquery $.param() param
Demo
$(document).ready(function(){
var TestObject = {
TestId:99,
ComplexType: $('#theForm').serializeArray()
}
var shallowEncoded = $.param( TestObject, false );
var shallowDecoded = decodeURIComponent( shallowEncoded );
console.log(shallowEncoded);
console.log(shallowDecoded);
})
and here is controller: Nothing to change. Modelbinder will take care of it.
[HttpPost]
public ActionResult Test(TestObject)
{
}
and if you want to keep them seperate in controller:
[HttpPost]
public ActionResult Test(ComplexType model, int testId)
{
// do stuff with model
}
and keep the model like it was before. modelbinder populates the model from http form collection from posted data.
I'm having a class like the following:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
[DataContract()]
public class TestCol : List<Test> { }
[DataContract()]
public class MainTest
{
public TestCol Components { get; set; }
}
[DataContract()]
public class Test
{
public Test() { }
public String Name { get; set; }
}
And a webservice with the following webmethod like this:
[WebMethod]
public String Test(MainTest input)
{
String rtrn = String.Empty;
foreach (Test test in input.Components)
rtrn += test.Name;
return rtrn;
}
Which is called by AJAX with the following method:
var Test = {};
Test.Name = "Test";
var MainTest = {};
MainTest.Components = [];
MainTest.Components.push(Test);
$.ajax({
type: "POST",
url: "WebService/WSTest.asmx/Test",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
"input": MainTest
}),
success: function(data, textStatus) {
console.log("success");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
window.console && console.log && console.log(XMLHttpRequest.responseText + " || " + textStatus + " || " + errorThrown);
}
});
When executing the AJAX call, it will return errors. I found out that the error is with the typed class TestCol, which has no properties.
Now do I have found 2 solutions that require changes in the C# classes:
Remove the TestCol class and change the Components property to List<Test> datatype:
[DataContract()]
public class MainTest
{
public List<Test> Components { get; set; }
}
[DataContract()]
public class Test
{
public Test() { }
public String Name { get; set; }
}
Or add an extra property to the TestCol class and change the webmethod:
[DataContract()]
public class TestCol : List<Test>
{
public List<Test> Components { get; set; }
}
[DataContract()]
public class MainTest
{
public TestCol Components { get; set; }
}
[DataContract()]
public class Test
{
public Test() { }
public String Name { get; set; }
}
&
[WebMethod]
public String Test(MainTest input)
{
String rtrn = String.Empty;
foreach (Test test in input.Components.Components)
rtrn += test.Name;
return rtrn;
}
Both solutions require changes in the C# classes, which I prefer not to, as other code is depended on it. Does anyone know a solution for this problem?
Edit: I've uploaded a test solution, containing above code: http://jeroenvanwarmerdam.nl/content/temp/JSONtoClassWebservice.zip
So this solution changes the List to Object instead of Test. I hoped to change as little code as possible (i dislike having to do casts in foreach loops). The below code does so with two function additions and the previously mentioned inheritance change.
public class TestCol : List<object>
{
public new IEnumerator<Test> GetEnumerator()
{
return this.ConvertAll<Test>(
dict => ConvertDictionaryTo<Test>(
(Dictionary<string, object>) dict
)
).GetEnumerator();
}
private T ConvertDictionaryTo<T>(IDictionary<string, object> dictionary) where T : new()
{
Type type = typeof(T);
T ret = new T();
foreach (var keyValue in dictionary)
{
type.GetProperty(keyValue.Key).SetValue(ret, keyValue.Value, null);
}
return ret;
}
}
Convert function courtesy TurBas
Mapping object to dictionary and vice versa
JavaScriptSerializer serialization: IEnumerable -> JavaScript Array
When the JavaScriptSerializer is used, it automatically converts an IEnumerable (without IDictionary) type -- that covers List<> or anything derived from it -- into an array.
Deserialization: JavaScript Array -> IEnumerable -> Collection Object
Now, upon deserialization from JSON, the JavaScriptSerializer must take the array, create an IEnumerable, then create an object for the field by passing that IEnumerable into its constructor.
Constructing Collection object via Constructor
Now, for List<> you have a constructor overload that takes an IEnumerable. So if you put List<Test> as the type of your component it creates it fine.
Constructors not inherited
However, TestCol does NOT have such a constructor! The reason why it worked with List<Test> and not with TestCol (which derives from List<Test>) is that the only thing that is not inherited between classes are constructors!
Therefore, the JavaScriptSerializer does not have any way to construct a TestCol from an IEnumerable. So it fails silently.
Deserialize Array by Creating List, then Casting to Type
Now the JavaScriptSerializer may then attempt to create a List<Test> from this IEnumerable<Test>, and then try to cast it into a TestCol.
Possible Solution
Solution: Try putting in:
public TestCol () {} // Need this when you have another constructor
public TestCol (IEnumerable<Test> list) : base(list) {} // Constructor that takes an IEnumerable
public TestCol (IList<Test> list) : base(list) {} // Constructor that takes an IList
as your TestCol's constructors.
And if it still doesn't work, implement an explicit type cast from List<Test> to TestCol.
public static explicit operator TestCol(IList<Test> list) { return new TestCol(list); }
hmmm this didn't work in the web method?
foreach (Test test in input.Components.TestCol)
Re comment below, does this work then?
foreach (Test test in (List<Test>)input.Components.TestCol)
It should work because a class can be enumerated...
If you're expecting JSON, you'll need to return JSON.
Check with the System.Web.Script.Serialization.JavaScriptSerializer
http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx
If you use ASMX services the JavaScriptSerializer will be responsible for the data conversion and not the DataContractJsonSerializer. So all DataContract attributes which you use will not work.
You are write that classes like public class TestCol : List<Test> { } are bad for the JavaScriptSerializer, but classes having List<Test> as the property (public class MainTest { public List<Test> Components { get; set; }}) have no problem.
So I suggest to simplify you code to the following. The classes used as the parameters can be defines as
public class Test {
public String Name { get; set; }
}
public class MainTest {
public List<Test> Components { get; set; }
}
The WebMethod Test will be
[WebMethod]
public String Test(MainTest input)
{
StringBuilder rtrn = new StringBuilder();
foreach (Test test in input.Components) {
rtrn.AppendLine (test.Name);
}
return rtrn.ToString ();
}
and the ajax call can be
var tests = {
Components: [
{Name:"Test1"},
{Name:"Test2"},
{Name:"Test3"}
]
};
$.ajax({
type: "POST",
url: "WebService1.asmx/Test",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
"input": tests
}),
success: function (data, textStatus) {
alert("success:\n" + data.d);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText+" || "+textStatus+" || "+errorThrown);
}
});
How you can see all will be very simple and it's work. For more details how you can send complex data I recommend you to read another answer and this.
You seem to be using ASMX (not WCF) because you have omitted [DataMember] attributes on all your public properties and still get serialized. WCF is "opt-in", so you shouldn't be seeing any serialization of any properly.
As a result, all [DataContract] attributes are useless.
ASMX defaults to the JavaScriptSerializer if you are using ScriptManger and outputing JSON. The JavaScriptSerializer is "opt-out" (which means that all public properties are automatically serialized unless marked with [ScriptIgnoreAttribute]).
The JavaScriptSerializer supports serializing List<>'s. You should not be having problems serializing your TestCol property because JavaScriptSerializer automatically supports serializing all types that implement IEnumerable (but not IDictionary) -- which includes List<> -- into JSON arrays.
Your error seems to be that the JavaScriptSerializer does not properly handle classes that inherit from List<> (or from a class implementing IEnumerable). In your first work-around, you eliminated the class that inherited from List<>. In your second work-around, you skipped all functionalities of the base class, but re-implemented the List<> in a property.
Your JSON post data currently looks like:
{ Components: [
{ Name:"foo" },
{ Name:"bar" },
:
] }
However, you have one extra level or redirection in the serializer (inheriting from List<Test> -> TestCol). It is possible that the serializer is looking for:
{ Components: {
Items: [
{ Name:"foo" },
{ Name:"bar" },
:
] }
}
because you are essentially serializing the "Items" property of List<>. So, your JSON post data is just feeding Test objects to the wrong place, and your TestCol Components property ends up empty.
I'd suggest that you add a web service method to output a test MainTest object to see what it serializes into. You'll probably find that it puts in an additional level.