In MVC3, is it possible to automatically bind javascript objects to models if the model has nested objects? My model looks like this:
public class Tweet
{
public Tweet()
{
Coordinates = new Geo();
}
public string Id { get; set; }
public string User { get; set; }
public DateTime Created { get; set; }
public string Text { get; set; }
public Geo Coordinates { get; set; }
}
public class Geo {
public Geo(){}
public Geo(double? lat, double? lng)
{
this.Latitude = lat;
this.Longitude = lng;
}
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public bool HasValue
{
get
{
return (Latitude != null || Longitude != null);
}
}
}
When I post the following JSON to my controller everything except "Coordinates" binds successfully:
{"Text":"test","Id":"testid","User":"testuser","Created":"","Coordinates":{"Latitude":57.69679752892457,"Longitude":11.982091465576104}}
This is what my controller action looks like:
[HttpPost]
public JsonResult ReTweet(Tweet tweet)
{
//do some stuff
}
Am I missing something here or does the new auto-binding feature only support primitive objects?
Yes, you can bind complex json objects with ASP.NET MVC3.
Phil Haack wrote about it recently.
You've got a problem with your Geo class here.
Don't use nullable properties:
public class Geo
{
public Geo() { }
public Geo(double lat, double lng)
{
this.Latitude = lat;
this.Longitude = lng;
}
public double Latitude { get; set; }
public double Longitude { get; set; }
public bool HasValue
{
get
{
return (Latitude != null || Longitude != null);
}
}
}
This is the javascript code I've use to test it:
var jsonData = { "Text": "test", "Id": "testid", "User": "testuser", "Created": "", "Coordinates": { "Latitude": 57.69679752892457, "Longitude": 11.982091465576104} };
var tweet = JSON.stringify(jsonData);
$.ajax({
type: 'POST',
url: 'Home/Index',
data: tweet,
success: function () {
alert("Ok");
},
dataType: 'json',
contentType: 'application/json; charset=utf-8'
});
UPDATE
I've tried to do some experiments with model binders and I came out with this solutions which seems to work properly with nullable types.
I've created a custom model binder:
using System;
using System.Web.Mvc;
using System.IO;
using System.Web.Script.Serialization;
public class TweetModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var contentType = controllerContext.HttpContext.Request.ContentType;
if (!contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
return (null);
string bodyText;
using (var stream = controllerContext.HttpContext.Request.InputStream)
{
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(stream))
bodyText = reader.ReadToEnd();
}
if (string.IsNullOrEmpty(bodyText)) return (null);
var tweet = new JavaScriptSerializer().Deserialize<Models.Tweet>(bodyText);
return (tweet);
}
}
and I've registered it for all types tweet:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add(typeof(Models.Tweet), new TweetModelBinder());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
I experienced the same issue, however in my case this was related to how the data was being transmitted from client side. Make sure the AJAX request is using the proper headers and formatting. For example:
dataType: 'json',
contentType: 'application/json; charset=UTF-8',
data: JSON.stringify({
MemberId : '123',
UserName: '456',
Parameters: [
{ Value : 'testing' },
{ Value : 'test2' }
]
}),
Related
I want to create a windows console application to read Json api data, retrieve all the type="folder" items and then save the data to database. How to access to the properties of the Json data?
Json data format:
{
asset:{
path:{
Children:[
0:{
id:xxxx,
type: folder
},
1:{
id:xxxxx,
type: folder
},
2:{
id:xxxx,
type: page
}
...
]
}
},
success:ok
}
FolderModel:
public class FolderModel
{
public string id { get; set; }
public string type { get; set; }
}
//main
static void Main(string[] args)
{
Task T = new Task(ApiCall);
T.Start();
Console.ReadLine();
}
//apicall
static async void ApiCall()
{
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync("https://apiurl/");
response.EnsureSuccessStatusCode();
using (HttpContent content = response.Content)
{
string responseBody = await response.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<FolderModel>(responseBody);
//items id and type are null here. how to retrieve those information ??
}
}
}
Your JSON and c# model are not identical first you need to create models to deserialize json
public class FolderModel
{
public string id { get; set; }
public string type { get; set; }
}
public class Path
{
public List<FolderModel> Children { get; set; }
}
public class Asset
{
public Path path { get; set; }
}
public class ResponseModel
{
public Asset asset { get; set; }
public string Success { get; set; }
}
Now you can access deserialize using
var response = JsonConvert.DeserializeObject<ResponseModel>(responseBody);
Now you can acess properties using linq or foreach loop
Using Linq
var folderResponse = response.asset.path.Children.Select(x => new
FolderModel { id = x.id, type = x.type });
//or you can access like this if you need to get all fields
var folderResponse = response.asset.path.Children.Select(x => x);
ForEach
foreach (var folder in response.asset.path.Children)
{
//folder.id
//folder.type
}
JSON to parse
{
"asset": {
"path": {
"Children": [
{
"id": "xxxx",
"type": "folder"
},
{
"id": "xxxxx",
"type": "folder"
},
{
"id": "xxxx",
"type": "page"
}
]
}
},
"success": "ok"
}
how to call post method in angular 5 and ionic 3.
My angular code is:
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
let body= {
records:
this.records
};
this.http.post('http://localhost:13799/HealthPotliWebService.asmx/getsingleproductdetails',
"{body2:" + JSON.stringify(body) + "}",options)
.map(res=>res.json())
.subscribe(data=>{
console.log(data);
});
}
this is my webservice class
my class is like this :
public class body2
{
public List<records> records { get; set; }
}
public class records
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
}
webservice code :
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string getsingleproductdetails(body2 body2)
{
// some code here
return new JavaScriptSerializer().Serialize(resources);
}
Return type of my webservice is:
<string xmlns="http://tempuri.org/">
{"error_msg":"Success","status":"0","medicinename":"CROCIN","description":"\u003cdiv style=\u0027text-align:justify;\u0027\u003e\u003cu\u003e\u003cb\u003eParacetamol\u003c/b\u003e\u003c/u\u003e-\u003cu\u003e125mg\u003c/u\u003e\u003cbr/\u003e\u003cbr/\u003e\u003c/div\u003e","lstComments":[],"rateValue":0,"potliMoney":0,"offer":null}
</string>
In my web api controller there is an action method like below
[HttpPost]
[EnableCors(origins: "*", headers: "*", methods: "*", exposedHeaders: "X-Custom-Header")]
public IReportOutput InsuranceHandlingFiles([FromBody]CaseCountsInputData caseCountsInputData)
{
}
Parameter class of this action is
[Serializable]
public class CaseCountsInputData
{
public string MainTitle { get; set; }
public string YearTitle { get; set; }
public string InsurerFileCountTitle { get; set; }
public List<FileCount> FileCounts { get; set; }
public int InsurerTotalCount { get; set; }
public int GrandTotal { get; set; }
public string TotalTitle { get; set; }
public string GrandTotalTitle { get; set; }
}
[Serializable]
public class FileCount
{
public string year { get; set; }
public int InsurerFilesCount { get; set; }
}
I call this API method for testing purposes as follows. My action is called this way and my json object is binded to CaseCountsInputData class but all parameters are null. Can you help me exactly where I made the mistake?
$("#btnExport")
.click(function() {
var reportData = GetReportData();
console.log(JSON.stringify(reportData));
$.ajax({
type: "POST",
dataType: "json",
data: JSON.stringify(reportData),
contentType: "application/json",
url: "http://localhost:50773/api/export/insurancehandlingfiles",
success: function(data) {
var DocumentBody = data.Data;
var FileName = data.FileName;
dataURItoBlob(DocumentBody, FileName);
},
error: function(error,as,asd) {
jsonValue = jQuery.parseJSON(error.responseText);
alert("error" + error.responseText);
}
});
});
});
function GetReportData() {
var reportModel = {
CaseCountsInputData: {
MainTitle: "Dosya Sayısı",
YearTitle: "Yıl",
InsurerFileCountTitle: "Sigortacı Dosya Sayısı",
TotalTitle: "Toplam",
GrandTotalTitle: "Genel Toplam",
InsurerTotalCount: 1,
GrandTotal: 3,
FileCounts: []
}
}
var caseCounts =[];
caseCounts.push({
"year": 2014,
"InsurerFilesCount": 1
});
caseCounts.push({
"year": 2015,
"InsurerFilesCount": 4
});
for (var i = 0; i < caseCounts.length; i++) {
reportModel.CaseCountsInputData.FileCounts.push(caseCounts[i]);
}
return reportModel;
}
I have a list of class that made in client with typescript, Now i want to send it to webmethod.
My typescript code is below :
class MakeReportData {
LocalName: string;
FldSi: number;
ViewSi:number;
TypeName:string ;
CheckBoxshow :boolean ;
CheckBoxFilter:boolean;
}
My Ajax code is below :
var temp: MakeReportData[] = [];
for (var i = 0; i < $scope.myData.ReportDetail.length; i++) {
var rep: MakeReportData=new MakeReportData();
rep.LocalName = $scope.myData.ReportDetail[i].LocalName;
rep.FldSi = $scope.myData.ReportDetail[i].FldSi;
rep.ViewSi = $scope.myData.ReportDetail[i].ViewSi;
rep.TypeName = $scope.myData.ReportDetail[i].TypeName;
rep.CheckBoxshow = $scope.myData.ReportDetail[i].CheckBoxshow;
rep.CheckBoxFilter = $scope.myData.ReportDetail[i].CheckBoxFilter;
temp.push(rep);
}
var tedata = JSON.stringify({ itm: temp });
alert(tedata);
$.ajax({
type: "POST",
url: "MakeReport.aspx/GetList",
contentType: "application/json; charset=utf-8",
data: tedata ,
dataType: "json",
success: function (data) {
alert(data.d);
},
error: function (data, status, jqXHR) {
alert(status);
alert(jqXHR);
}
});
my webmethod is below :
[WebMethod]
public static string GetList(MakeReportData[] itm)
{
return "";
}
my class in C# is like this :
public class MakeReportData
{
public string LocalName { get; set; }
public int FldSi { get; set; }
public int ViewSi { get; set; }
public string TypeName { get; set; }
public bool CheckBoxshow { get; set; }
public bool CheckBoxFilter { get; set; }
}
I want to send the list of MakeReportData to the server webmethod.
My problem is that the webmethod does not call.
I made it myself, It just need to make webmethod's input as list like below:
public static string GetList(List<MakeReportData> itm)
{
return "";
}
I am new to ASP.net (and programming in general) and I'm having trouble building a Web API. More specifically I need help in these two areas:
How to configure my DOCcontroller to post a new document (DOC table).
How to make the actual ajax post -- I am having trouble passing the EXT_GUID parameter. As it stands I get an error when I try to post. "Can't bind multiple parameters (doc and parentOwner) to the request's content."
Essentially this is for a simple document management system. I want Get/Post documents (DOC) by having the user supply an GUID from an external database (the EXT_GUID field) as a filter/parameter. Each document can have multiple EXT_GUIDs and each EXT_GUID can have multiple Documents (DOC). You can assume that the EXT_GUID fields we be populated prior to the http post.
This is the DOCcontroller code
//POST api/DOC
public HttpResponseMessage PostDOC(DOC doc, List<string> parentOwners)
{
if (ModelState.IsValid)
{
var parents = db.BIMs.Where(bx => parentOwners.Contains(bx.EXT_GUID));
foreach (var p in parents)
doc.Owners.Add(p);
db.DOCs.Add(doc);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, doc);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = doc.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
This is my model setup -- EntityFramework codefirst stuff
public class EXT
{
public int Id { get; set; }
public string EXT_GUID { get; set; }
public int ProjectID { get; set; }
public virtual ICollection<DOC> DOCs { get; set; }
}
public class DOC
{
public int Id { get; set; }
public int ProjectID { get; set; }
public string Subject { get; set; }
public string Link { get; set; }
public virtual ICollection<EXT> EXTs { get; set; }
}
This is more Storage Model...
public StoreDBContext() : base("name=StoreDBContext")
{
}
public DbSet<EXT> EXTs { get; set; }
public DbSet<DOC> DOCs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Set FLUENT API config for many to many here
modelBuilder.Entity<EXT>()
.HasMany(a => a.DOCs)
.WithMany()
.Map(x =>
{
x.MapLeftKey("EXT_Id");
x.MapRightKey("DOC_Id");
x.ToTable("EXTsDOCs");
});
}
AJAX Code
function AddDOC() {
var parentOwner = "{\"" + $('#txtaddEXT').val() + "\"}";
jQuery.support.cors = true;
var DOC = {
ProjectId: ProjectID,
Subject: $('#txtaddDOCSubject').val(),
Link: $('#txtaddDOCLink').val(),
parentOwner: parentOwner
};
$.ajax({
url: "http://localhost:54171/api/DOC/",
type: 'POST',
data: JSON.stringify(DOC),
contentType: "application/json;charset=utf-8",
success: function (data) {
WriteResponse(data);
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
}
What you receive from the client and what you will save in the database is two different things.
Your doc object is ok:
var DOC = {
ProjectId: ProjectID,
Subject: $('#txtaddDOCSubject').val(),
Link: $('#txtaddDOCLink').val(),
parentOwner: parentOwner
};
Now you need to change the server logic. Make a model like this:
public class DocReceivedModel
{
public int ProjectID { get; set; }
public string Subject { get; set; }
public string Link { get; set; }
public List<string> parentOwner { get; set; }
}
Then your PostDOC method will be:
public HttpResponseMessage PostDOC(DocReceivedModel docReceived)
{
if (ModelState.IsValid)
{
Doc newDoc = new Doc();
newDoc.ProjectID = docReceived.ProjectID
newDoc.Subject = docReceived.Subject
newDoc.Link = docReceived.Link
var parents = db.BIMs.Where(bx => docReceived.parentOwners.Contains(bx.EXT_GUID));
foreach (var p in parents)
newDoc.Owners.Add(p);
// I not see in your model Owners, maybe this is EXTs but I suppose you catch the idea
db.DOCs.Add(newDoc);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, newDoc);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new {id = newDoc.Id}));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}