Angularjs resource sending empty object to api controller - asp.net

Hi I have agularjs resources below that is working fine.
return {
formDis: $resource('/api/pDisc/:id', { id: '#id' }, { update: { method: 'POST' } })
};
angularjs cotroller using the resource is
$scope.submit = function (form) {
console.log(form.dis.length);
console.log(form.dis);
for (var i = 0; i < form.dis.length; i++) {
pRepository.formDis.update(form.dis[i], function () {
{
alert("Saved");
}
});
};
};
WebConfig is
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
MVC Api that is recieving it is
// POST api/projDisc
public HttpResponseMessage Postproject_discussion(pDis pDis)
{
db.project_discussion.Add(pDis);
db.SaveChanges();
}
Class is
[DataContract(IsReference = false)]
[Serializable]
public partial class pDis
{
public int pDis_id { get; set; }
public int pro_id { get; set; }
public string message {get; set;}
public virtual pro pro { get; set; }
public virtual people people { get; set; }
}
}
When I run it I get error in my api at
db.project_discussion.Add(pDis);
Error is
An exception of type 'System.Data.Entity.Validation.DbEntityValidationException' occurred in EntityFramework.dll but was not handled in user code
I get this error because api is receiving empty object.
In angular controller I can see the objects in console being passed properly from form.
Once the object is submitted to the resource and from it to api there is something wrong that it ends up as empty object. Please let me know how to fix it.

Based on your comment:
I can see the right data being {"pro_id":"221","message":"sdsfsd"
pDis_id:""}
My best guess is that the model binder is not able to convert to the pDis type.
One thing I notice is that your property pDis_id is an int (non-nullable), and you are passing it pDis_id:"". I think the model binder will not know what to do in that case.
Try supplying an integer value for pDis_id, or not supplying it at all.

Related

ASP.Net Core - How to get foreign key relation working on POST request for a web-api?

I am building a simple to-do list api using ASP.Net Core. It has two main two main models, a List model and a Task model. Each List has many Tasks. I build the models like this:
List Model:
namespace ToDoList.Models
{
public class List
{
[Key]
public int ListId { get; set; }
[Required]
[StringLength(25)]
public string Title { get; set; }
public string Colour { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
public List()
{
Tasks = new List<Task>();
Colour = "secondary";
}
}
}
Task Model:
namespace ToDoList.Models
{
public class Task
{
[Key]
public int TaskId { get; set; }
[Required]
[StringLength(50)]
public string Title { get; set; }
public bool Done { get; set; }
public int ListId { get; set; }
public virtual List List { get; set; }
public Task()
{
Done = false;
}
}
}
When I send a post request to create a new task I am struggling to get the created task to be added to the Icollection part of the List model.
My Controller looks like this:
// POST: api/Tasks
[HttpPost]
public async Task<ActionResult<Models.Task>> PostTask(Models.Task task)
{
_context.Tasks.Add(task);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTask", new { id = task.TaskId }, task);
}
If I send this data as JSON as a POST request:
{ title: "A New Task", listId: 11 }
I create this Task:
{"taskId":16,"title":"A New Task","done":false,"listId":11,"list":null}
As you can see it has the right listId but the list attached is null.
Also the task does not get added to the list.Tasks collection.
{"listId":11,"title":"Learn ASP.Net Core","colour":"secondary","tasks":[]}
As you can see tasks is still empty.
How do I get it set up that when ever a task is created it is always add to List.Tasks and then Tasks.List has the correct list attached to it, not null.
Also On my SQL Sever Database I expected to see a Tasks in the Lists table but I don't. Can anyone explain why?
SQL Sever Database Columns Picture
You could load the List entity from your DbContext and add it to the Task object you are returning:
[HttpPost]
public async Task<ActionResult<Models.Task>> PostTask(Models.Task task)
{
_context.Tasks.Add(task);
await _context.SaveChangesAsync();
task.List = _context.Lists.Single(task.ListId);
return CreatedAtAction("GetTask", new { id = task.TaskId }, task);
}
or you could return an instance of the Task loaded from the DbContext with included List:
var taskFromDb = _context.Tasks.Include(x => x.List).Single(x => x.Id = task.Id);
return CreatedAtAction("GetTask", new { id = task.TaskId }, taskFromDb);
To get a list with tasks, it needs to be loaded from the DbContext:
var listWithTasks = _context.Lists.Include(x => x.Tasks).Single(x => x.Id == task.ListId);

How to receive multiple object parameters in GET Action in ASP.NET Core 2.2 REST API?

I have common scenario where I want to list some items in web client app (I am using vue.js and axios) that are fetched from the backend (asp.net core 2.2 REST application). The client should pass some filtering and pagination options to the backend in order to get items it needs to display on the current page.
However, the problem I have is that GET action defined on server Controller can't deserialize parameters passed to it from HTTP request.
Here is the backend code:
// these are classes that represent parameters sent in HTTP Get request
public class Filter
{
public bool IsActive { get; set; }
public string Name { get; set; }
}
public class Pagination
{
public string Page { get; set; }
public string ItemsPerPage { get; set; }
}
This is the action in controller:
[HttpGet]
public IActionResult GetItems([FromQuery]Filter filter, [FromQuery]Pagination pagination)
{
// ... get items from db with specified filter and pagination
return Ok();
}
Here is the axios get request from the client:
axios.get('https://localhost:44336/api/items', {
params: {
filter: {
isLive: true,
name: 'test123'
},
pagination: {
page: 5,
itemsPerPage: 10
}
}
})
Notice that I use axios params to specify two nested objects, one filter and other pagination. This doesn't work and parameters passed to GetItems() action are objects with default values (eg. isLive=false, name=null, page=0, itemsPerPage=0...)
Is there a way to make this work and have two separate objects passed and properly deserialized by GetItems() action?
For your current request from axios, the request will be https://localhost:44320/api/SampleData/GetItems?filter={"isActive":true,"name":"test123"}&pagination={"page":5,"itemsPerPage":10} which will not be able to bind by default modelbinder.
You could implement your own model binder like
public class NestedModelBinder<T> : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelName = bindingContext.ModelName;
var request = bindingContext.HttpContext.Request.Query.TryGetValue(modelName, out var modelValue);
if (modelValue.Count ==0)
{
return Task.CompletedTask;
}
try
{
var model = JsonConvert.DeserializeObject<T>(modelValue);
if (model == null)
{
return Task.CompletedTask;
}
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.CompletedTask;
}
}
}
Useage
[HttpGet("[action]")]
public IActionResult GetItems([ModelBinder(typeof(NestedModelBinder<Filter>))]Filter filter, [ModelBinder(typeof(NestedModelBinder<Pagination>))]Pagination pagination)
{
var request = HttpContext.Request.Query;
// ... get items from db with specified filter and pagination
return Ok();
}
One more note: keep Filter.IsActive at server side and filter.isLive at client side the same.
View Model:
public class ViewModel
{
public Filter Filter{ get; set; }
public Pagination Pagination { get; set; }
}
Action method:
public IActionResult GetItems([FromQuery] ViewModel model)
Client:
params: {
filter.isActive: true,
filter.name: 'test123',
pagination.page: 5,
pagination.itemsPerPage: 10,
}

Properly receive a nested object via post

I'm trying to send a complex object as a form object over to a mvc controller. However I just receive a new object with default values.
Here's the object (just a bit trimmed to make it simpler for the example)
public class AddProjectPopupModel
{
public long SelectedCountryId;
public Project ResultProject { get; set; }
}
public class Project
{
public long Number;
public string Name;
}
And here's how I create the form:
#using (Html.BeginForm("AddProjectResult", "Popups", FormMethod.Post, new { id = "addProjectForm"}))
{
#Html.EditorFor(model => model.ResultProject.Number, new { htmlAttributes = new { Id = "number", Class = "validate", Value = 0 } })
#Html.EditorFor(model => model.ResultProject.Name, new { htmlAttributes = new { Id = "name", Class = "validate" } })
}
Now I have a button which calls the following javascript:
var form = $("#addProjectForm");
var data = form.serialize();
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: data,
success: function(result) {
debugger;
}
});
And the controller:
[HttpPost]
public ActionResult AddProjectResult(AddProjectPopupModel model)
{
return Json(new { Success = true });
}
I've also tried to change the controller parameters to:
public ActionResult AddProjectResult(long SelectedCountryId, Project ResultProject)
And here the SelectedCountryId gets filled properly, but not the Project.
I'd love to use the whole AddProjectPopupModel as parameter and not it's specific values.
However I can't figure out to do it properly.
Fiddler output for the call:
Debugger values in controller:
Your problem is that in your project object you're using fileds
public class Project
{
public long Number;
public string Name;
}
and they should be properties so they can be navigated to
public class Project
{
public long Number { get; set; }
public string Name { get; set; }
}
This should work
I recommend reading Model Binding for more info

Web Api POST for inserting data with Complex type is always null

I'm quite new to Web-Api concepts. Trying to insert data using the basic project provided by Microsoft (Adding a simple Test Client to ASP.NET Web API Help Page).
http://blogs.msdn.com/b/yaohuang1/archive/2012/12/02/adding-a-simple-test-client-to-asp-net-web-api-help-page.aspx
Below is the code for Inserting data into Headoffice table.(HeadofficeID int ,HeadofficeName varchar(50),Notes varchar(1000),Isactive bit)
[POST("create")]
public HttpResponseMessage Post([FromUri]HeadOfficeModel headOffices)
{
if (_headOfficeBLL.Insert(headOffices) > 0)
return Request.CreateResponse(HttpStatusCode.Created, headOffices);
else
return Request.CreateResponse(HttpStatusCode.BadRequest, headOffices);
}
Headofficemodel Class
public partial class HeadOfficeModel : AtlAuthenticationModelBase
{
public int HeadOfficeId { get; set; }
public string HeadOfficeName { get; set; }
public string Notes { get; set; }
public bool IsActive { get; set; }
}
In the front end when i try to send the data from URI or Body only null values are getting inserting. While debugging all i can see in Headoffice model is null values.Below are the different ways i tried to insert data
1) {"HeadOfficeName":"TestHO1", "Notes":"notes", "IsActive":true}
2) {"TestHO1", "notes", true}
3) ={"headOffices":{"HeadOfficeName":"TestHO1","Notes":"notes","IsActive":false}}
and also tried to change the code as below
public HttpResponseMessage Post([FromUri]HeadOfficeModel headOffices)
public HttpResponseMessage Post([FromBody]HeadOfficeModel headOffices)
public HttpResponseMessage Post([ModelBinder]HeadOfficeModel headOffices)
Been trying to fix this from two days. When i send the data as complex type its not working else as separate parameters (changing the method to accept parameters) its working fine
public int Post(string Name, string Notes, bool Active)
{
HeadOfficeModel objHOM = new HeadOfficeModel();
objHOM.HeadofficeName = Name;
objHOM.Notes = Notes;
objHOM.IsActive = Active;
return _headOfficeBLL.Insert(objHOM);
}
Below is the html code where i m hiting while inserting
<script>
testClientModel = {
HttpMethod: '#Model.ApiDescription.HttpMethod',
UriPathTemplate: #Html.Raw(Json.Encode(Model.ApiDescription.RelativePath)),
UriParameters: [
#foreach (var parameter in Model.ApiDescription.ParameterDescriptions)
{
if (parameter.Source == System.Web.Http.Description.ApiParameterSource.FromUri)
{
#:{ name: "#parameter.Name", value: "" },
}
}
],
Samples: {
#Html.Raw(#String.Join(",", Model.SampleRequests.Select(s => String.Format("\"{0}\": \"{1}\"", s.Key, HttpUtility.UrlEncode(s.Value.ToString()))).ToArray()))
},
BaseAddress: '#applicationPath'
};
</script>
Can you please help me where am i going wrong. Attaching screenshot.
Entered both in URI and Body just to show that i tried different ways.
enter image description here

How to post JSON data to SQL using ajax post & knockout

I have a pretty straightforward view model:
var ProjectViewModel = {
ProjectName: ko.observable().extend({ required: "" }),
ProjectDescription: ko.observable().extend({ required: "" }),
ProjectStartDate: ko.observable(),
ProjectEndDate: ko.observable()
};
I want to save this data that is located in my viewmodel to my SQL server.
I have a class defining this View Model in my Server Side Code:
public class Projects
{
public string ProjectName { get; set; }
public DateTime ProjectStartDate { get; set; }
public DateTime ProjectEndDate { get; set; }
public string ProjectDescription { get; set; }
}
I also have this web method to receive the code:
[WebMethod]
public bool SaveProject(string[] JSONDATA)
{
TaskNinjaEntities entities = new TaskNinjaEntities();
foreach (var item in JSONDATA)
{
Console.WriteLine("{0}", item);
}
return true;
}
And finally I have this POST that does not want to send the data to the server:
function SaveMe() {
var data = ko.toJSON(ProjectViewModel);
$.post("CreateProject.aspx/SaveProject", data, function (returnedData) {
});
}
I get nothing from the returned data in this post method, also added breakpoint in server side code, and it doesn't hit it at all. My URL is correct and the Viewmodel converts to JSON without hassle.
Make the web method static.
[WebMethod]
public static bool SaveProject(string[] JSONDATA)
{
TaskNinjaEntities entities = new TaskNinjaEntities();
foreach (var item in JSONDATA)
{
Console.WriteLine("{0}", item);
}
return true;
}

Resources