I want to call a web service method in javascript. (asp.net 3.5)
I have traced the result with firebug .
here is the result:
{"d":"[{\"TI\":\"www\"},{\"TI\":\"www1\"}]"}
I think the correct result should like this
{"d":[{\"TI\":\"www\"},{\"TI\":\"www1\"}]}
what is the quotation before and after the Bracket ?
// edited :
in webserivce:
public class Test
{
public Test(string t){T1 = t;}
public string T1 { set; get; }
}
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true, XmlSerializeString = false)]
public string Load(string e)
{
List<Test> post = new List<Test> { new Test("www"), new Test("www1") };
return JsonConvert.SerializeObject(post);
}
and in js file :
var store = new Ext.data.JsonStore({
proxy: new Ext.data.HttpProxy({
url: '/core/webservice/service.asmx/Load',
method: 'GET',
headers: { 'Content-type': 'application/json' }
}),
root: 'd',
id: 'Id',
fields: ['TI']
});
store.load({ params: { e: ''} });
return;
thank you .
mir
You shouldn't need to serialize manually in the web service; consider using something like this instead:
public List<Test> Load(string e)
{
List<Test> post = new List<Test> { new Test("www"), new Test("www1") };
return post;
}
Since you're using string as your return object, it will convert it as that for you when serializing it (once again).
The quotation indicates that this is a string, so :
var b = {"d":"[{\"TI\":\"www\"},{\"TI\":\"www1\"}]"};
b["d"] will return a string instead of array of objects.
You can either go around this with the following in javascript:
var c = eval(b["d"]);
which will turn the string into an array of objects.
Or the better way, post the code that is returning this and we can try to figure out why it is returned as a string.
Related
The "normal" way of accepting file uploads in Web API is to examine the the Request.Content. However, I'd like to have the file data be an actual parameter of the method, so that tools like NSwag are able to generate proper TypeScript clients.
So instead of stuff like this:
[HttpPost]
public async Task UploadFile()
{
var provider = new MultipartFormDataStreamProvider(Path.GetTempPath());
var result = await Request.Content.ReadAsMultipartAsync(provider);
var formData = result.FormData.GetValues("Path");
...
}
I just want something like this:
public async Task UploadFileInfo(Models.FileInfo fileInfo)
{
//FileInfo contains a string of the binary data, decode it here
}
I have this partly working except that I can't reliably encode the binary file data as a string in JavaScript and then decode it for saving in C#/.NET.
In JavaScript, I've tried FileReader.readAsArrayBuffer, FileReader.readAsBinaryString, FileReader.readAsDataURL, FileReader.readAsText, but none of these have produced a string of binary data that I can reliably decode. In C#, I've tried System.Text.Encoding.UTF8.GetBytes and/or Convert.FromBase64String.
For reference, the relevant code NSwag is generating to communicate with the Web API is as follows:
const content_ = JSON.stringify(fileInfo);
let options_ : any = {
body: content_,
observe: "response",
responseType: "blob",
headers: new HttpHeaders({
"Content-Type": "application/json",
})
};
return this.http.request("post", url_, options_)
where this.http is an Angular HttpClient.
FileDto
[DataContract]
public class RestFileDataDto
{
[DataMember(Name = "data", IsRequired = true)]
[Required]
public string Data { get; set; }
[DataMember(Name = "filename")]
public string Filename { get; set; }
}
Base64-Persistence
public void SaveFile(string data, string filePath)
{
var dir = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
using (FileStream outputStream = File.Create(filePath))
{
using (Stream inputStream = new MemoryStream(new Base64Converter().ConvertToByteArray(data)))
{
inputStream.Seek(0, SeekOrigin.Current);
inputStream.CopyTo(outputStream);
}
}
}
Controller
[HttpPut]
[Route("route")]
public HttpResponseMessage Upload([FromBody] RestFileDataDto fileImage)
{
//// call persistence
}
Base64Converter().ConvertToByteArray
public byte[] ConvertToByteArray(string base64String)
{
var splittedString = base64String.Split(',');
return Convert.FromBase64String(splittedString[splittedString.Length - 1]);
}
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 = ''){}
}
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 am trying to send an object array to my controller but having some difficulties.
It is sending the array and when delivered to the controller, the object count of the array also seems OK.
But if you will look inside the objects all the attributes of the objects are null
How it can be possible?
JavaScript:
function callme(results) {
for (var i = 0; i < results.length; i++) {
var endRes = {
Id: results[i].id,
Icon: results[i].icon
};
jsonObj.push(endRes);
}
sendPackage(jsonObj);
}
function sendPackage(jsonObj) {
$.ajax({
type: "POST",
url: '../../Home/RegisterList',
data: { List: jsonObj },
cache: false,
dataType: "json",
error: function (x, e, data) {
alert(data);
}
});
}
Controller:
[HttpPost]
public JsonResult RegisterList(ICollection<DetailsModel> List)
{
foreach (var i in List) ....... // other process will be here
............................... // other process will be here
return Json(new { message = "OK" });
}
Model:
public class DetailsModel
{
public string Id { get; set; }
public string Icon { get; set; }
}
Ok I've solved the problem last night by using Newton's JSON.NET (you can get it from NuGet). I've stringified the array and recieved it as a string with the controller. Finally I used json.net to convert(deserialize) this string into a collection.
To stringify: use same code but change the data section of the json request with:
data: { List : JSON.stringify(jsonObj) }
Finally recieve it by:
using Newtonsoft.Json;
public JsonResult RegisterList(string List)
{
ICollection<DetailsModel> jsonModel = JsonConvert.DeserializeObject<ICollection<DetailsModel>>(List);
}
And voila; you have a collection named jsonModel!
Unfortunately model binding of lists is not that nice and obvious in MVC. See:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Passing the list this way works:
data: {"List[0].Id":"1", "List[0].Icon":"test"}
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.