How to post DbGeography param to MVC? - javascript

I am trying to expose an API that will allow users to post polygons to persist on the server. I am using ASP.NET MVC 5. How do I format the AJAX parameters correctly to post the request for DbGeography? This is what I am trying:
$.ajax({
url: '/api/map',
type: 'POST',
data: {
Title: 'My Title',
MyGEOG: {
WellKnownText: 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))'
}
}
});
This is how my MVC action signature looks like:
[HttpPost]
[Route("map")]
public JsonResult Post(MyShape newShape) {...}
Also my MyShape class:
public class MapShape
{
public string Title { get; set; }
public System.Data.Entity.Spatial.DbGeography MyGEOG { get; set; }
}
When putting a breakpoint in the action, newShape.Title does show as My Title, but MyGEOG is null when the AJAX post happens. What is the correct format of the parameter to post correctly as a DbGeography type?

The reason the DBGeography object is immutable, meaning that you can't write to it once it has been created. When you instantiate your MapShape class in the model binder, the MyGEO property is null. In other words, you are trying to set a property on a null object.
The only way to "create" a DBGeography object is using one of the factory methods like:
FromText - Creates a new DbGeometry value based on the specified well known text value.
http://msdn.microsoft.com/en-us/library/hh673669(v=vs.110).aspx
So, to pass in the Title and WellKnownText values to you controller, I suggest creating a Data Transfer Object (DTO) to act as proxy for the information.
public class MapShapeDTO
{
public string Title { get; set; }
public string WellKnownText { get; set; }
}
Your Ajax gets simplified like this
$.ajax({
url: '/api/map',
type: 'POST',
data: {
Title: 'My Title',
WellKnownText: 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))'
}
});
And your controller, you can use the DTO to create the MapShape object.
[HttpPost]
[Route("map")]
public JsonResult Post(MapShapeDTO dto)
{
MapShape m = new MapShape()
{
Title = dto.Title,
MyGEOG = System.Data.Entity.Spatial.DbGeography.FromText(dto.WellKnownText)
};
...
}

Related

Posting JSON object via AJAX to ASP.NET Core Web API

I have tried different methods that have been posted on this site, but nothing seems to work.
I want to create a clothing site (a personal project). The products on the site have their own class, that is built like this:
public class Product
{
public string ProductName { get; set; }
public string ProductPrice { get; set; }
public int Quantity { get; set; }
}
The shopping cart is another class that will contain a list of Product objects and this one is built like this:
public class ShoppingCart
{
[Key]
public int Id { get; set; }
List<Product> ProductList { get; set; }
public string ClientName { get; set; }
public string ClientAddress { get; set; }
public string ClientMail { get; set; }
}
I created an API Controller class and thought that would solve the problem. It looks like this:
[Route("api/Shopping")]
[ApiController]
public class ShoppingCartController : ControllerBase
{
[HttpPost]
public ShoppingCart Save([FromBody] ShoppingCart s)
{
return s;
}
}
In my JavaScript code I create my JSON object and try to post it like this:
var orderB = document.getElementById("orderB");
orderB.addEventListener("click", function () {
var inputName = document.getElementById("inputName").value;
var inputAddress = document.getElementById("inputAddress").value;
var inputMail = document.getElementById("inputMail").value;
var auxArray = [];
for (var i = 0; i < productsAux.length; i++) {
auxArray[i] = { "productName": productsAux[i].titlu, "productPrice": productsAux[i].pret, "quantity": localStorage.getItem(productsAux[i].titlu)};
}
var shoppingCart = {
productList: auxArray,
clientName: inputName,
clientAddress: inputAddress,
clientMail: inputMail
};
$.ajax({
type: "POST",
data: JSON.stringify(shoppingCart),
url: "api/shopping/save",
contentType: "application/json charset=utf-8",
}).done(function (res) {
alert(res);
});
After I push the order button on my page I expect to see the alert pop-up with the callback result which I suppose is the ShoppingCart object that is created using the JSON that I send.
For those coming on later, I would suggest checking that your types are correct on both ends. For instance, if your JS is posting a byte array and C# tries to convert it to an int, the whole object (not just that prop) will be null.
This has caught me many a time.
I opened the Network tab and I got this: I got a 404 (kind of
expected that) , the name of the method 'save' , a type of 'xhr' and a
size of 45B.
The 404 error obviously means the url/routing is wrong. Here to solve it ,you have two ways to achieve.
First way:
You can change url to "api/shopping" in ajax as follow:
$.ajax({
type: "POST",
data: JSON.stringify(shoppingCart),
url: "api/shopping",
contentType: "application/json charset=utf-8",
}).done(function (res) {
alert(res);
})
Second way:
You can change the path name
of Save action by Attribute routing with Http verb attributes as follow:
[Route("api/Shopping")]
[ApiController]
public class ShoppingCartController : ControllerBase
{
[HttpPost("Save")]
public ShoppingCart Save([FromBody] ShoppingCart s)
{
return s;
}
}
Update
According to your comment, in addition to the above updates, you also need to modify the routing settings as follows:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Debug result:

how to post objects from angular to a webapi controller

I am creating a pdf using pdfsharp. I need to pass the chart legend data(name,color) to the pdfsharp controller. I am using a angular $http post, a ajax post would be fine as well. the error I am getting is
Request URL:http://localhost:46691/api/exportChartPdf/[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object],[object%20Object]
do i need to some how pass it as a string? if so how would I parse it in the api controller back to the way I need it?
the objects i am trying to pass back
0: Object
color: "rgb(67,134,215)"
name: "Fleming Place - Miscellaneous Spec Builders"
__proto__: Object
1: Object
2: Object
3: Object
4: Object
javascript
var legendModel = $scope.seriesData.map(function (a) {
return {
color: a.color,
name: a.name
};
});
$http.post('/api/exportChartPdf/' + legendModel);
api controller
[HttpPost]
public PDF Post() // allow nullable parameter
{
try
{
You should be posting the object on the form body, not on the querystring. In addition, your Web API controller should receive a strongly typed object that mirrors the data that you're passing.
public class Data
{
public string Color { get; set; }
public string Name { get; set; }
}
[HttpPost]
public PDF Post([FromBody]List<Data> data)
{
...
}
And then simply post the object.
var legendModel = $scope.seriesData.map(function (a) {
return {
color: a.color,
name: a.name
};
});
$http.post('/api/exportChartPdf/', legendModel);
Looks like your controller action is missing parameter on it,
[HttpPost]
public PDF Post(SomeClass legendModel) // allow nullable parameter
{
try
{
SomeClass would contain color & name properties.
Then you should correct your $http.post call, pass 2nd param as data in JSON format.
$http.post('/api/exportChartPdf', { legendModel: legendModel);

How to post an object to WebAPI

I'm trying to figure out how to post an object from my form to a web api service. Within my controller I defined a model that I wanted to add input values to.
$scope.Label;
within my input fields I have them bound using ng-model such as:
<input type="checkbox" ng-model="label.isPublic" />
<input type="text" ng-model="label.labelName" required focus-me />
On the submission of the form these two fields a passed to my service and submitted to my WebApi
I have tried this submission in two ways:
function addLabel(label) {
var mylabel = encodeURIComponent(angular.toJson(label));
return $http.post('reportLibrary/createlabel/', { params: label }, {
}).then(function (response) {
return response.data;
});
};
and also as the following without declaring parameters
function addLabel(label) {
var mylabel = encodeURIComponent(angular.toJson(label));
return $http.post('reportLibrary/createlabel/', label , {
}).then(function (response) {
return response.data;
});
};
In the webAPI I have a method setup for the post
[Route ("reportLibrary/createlabel/")]
[HttpPost]
public DTOs.ReportLabel CreateLabel(DTOs.ReportLabel json)
{
DTOs.ReportLabel result = new DTOs.ReportLabel();
//.... do stuff
return result;
}
The ReportLabel (dto) is defined as follows:
public class ReportLabel
{
public Int64 LabelId { get; set; }
public string LabelName { get; set; }
public bool IsPublic { get; set; }
public IEnumerable<Report> Reports { get; set; }//placeholder?
}
The issue I have is when I post an object from my angular service it shows up as null within the API. If I change the type in the method to something like a JToken or JObject the values appear.
Can anyone help me understand why when I define the type that it is not passed across from angular?
thanks
It seems like you may be doing an extra step. You don't need to encode in json then pass in in json
return $http.post('reportLibrary/createlabel/', { LabelId: 101, LabelName: 'myname' }, {
then
public DTOs.ReportLabel CreateLabel([FromBody]ReportLabel reportLabel)
Take a look at the network values going by and you should see in debug tools or fiddler the actual posted values (form values).

JavaScript serialization of form not correct when added as a property of another object

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.

ASP.NET MVC: GET params not properly deserialized

I need to pass a complex object representing a data filter to an action using GET which returns a filtered data set in a csv file.
The filter object is something like this on the client (much more complex in actuality, simplified for brevity):
var filter = {
Folders = [
{ Positive: true, Reference: { Id: 19, Name: "Container" } },
{ Positive: true, Reference: { Id: 37, Name: "Bullseye" } },
]
}
The corresponding server side classes look something like this:
public class MyFilter
{
public List<MyComparison> Folders { get; set; }
}
public class MyComparison
{
public bool Positive { get; set; }
public MyReference Reference { get; set; }
}
public class MyReference
{
public int Id { get; set; }
public string Name {get; set; }
}
My action looks like this:
[HttpGet]
public FileContentResult Export(MyFilter filter, string sort, bool sortAscending)
{
string data = GetCsvData(filter, sort, sortAscending);
return this.File(StrToByteArray(data), "text/csv", "Data.csv");
}
I have tried calling this action from javascript like this:
function exportFilter(aFilter) {
var params = { filter: aFilter, sort: "Name", sortAscending: true };
var destination = "MyController/Export?" + decodeURIComponent($.param(params));
document.location = destination;
}
Within the action, both the sort and sortAscending parameters are properly populated. The filter is an object of type MyFilter, but its Folders property is null.
Is ASP.NET MVC incapable of properly deserializing complex parameters in this way (ie in the context of a GET)? What is the correct way to address this problem?
The databinding algorithm in Asp.net MVC is not very good in deserializing complex inputs in .NET types. In the best scenario, you would get some strange results and/or slow performance compared to other dedicated solutions. In this case, Json.NET provides much better results at deserializing json data in .NET types, and its very fast too.
You should pass these filters like an ordinary string parameter and, inside the action, deserialize it using Json.NET. Something like this:
using Newtonsoft.Json;
public ActionResult MyAction(string myFilters)
{
var deserializedObject = JsonConvert.DeserializeObject(myFilters);
}
It can bind complex objects/parameters, the problem is the way the params are being sent. For example, you're sending:
http://localhost/Home/Export?filter[Folders][0][Positive]=true&filter[Folders][0][Reference][Id]=19&filter[Folders][0][Reference][Name]=Container&filter[Folders][1][Positive]=true&filter[Folders][1][Reference][Id]=37&filter[Folders][1][Reference][Name]=Bullseye&sort=Name&sortAscending=true
But the MVC model binder expects this format:
http://localhost/Home/Export?filter.Folders[0].Positive=true&filter.Folders[0].Reference.Id=19&filter.Folders[0].Reference.Name=Container&filter.Folders[1].Positive=true&filter.Folders[1].Reference.Id=37&filter.Folders[1].Reference.Name=Bullseye&sort=Name&sortAscending=true
I'm not sure of the easiest way to build a string matching that pattern from a javascript object though.

Categories

Resources