Multiple Bind(Prefix = "...") in ASP.Net Core 3 MVC - c#

I've an ASP.Net MVC 4 application that I'm porting to ASP.Net Core 3.0 MVC.
I'm trying to port this method
[HttpPost]
public ActionResult DoSave(
[Bind(Prefix = "new")]IEnumerable<C_Data> newItems,
[Bind(Prefix = "updated")]IEnumerable<C_Data> updatedItems,
[Bind(Prefix = "deleted")]IEnumerable<C_Data> deletedItems))
{
}
In the post AJAX (in JavaScript from the web browser) I'm sending the values as JSON like this
{
"new[0].Id":3,
"new[0].SID":"00000000-0000-0000-0000-000000000000",
"new[0].Name":"asd"
}
Here's the C_Data class
public class C_Data
{
[Key]
public int Id { get; set; }
public Guid SID { get; set; }
[Required]
[MaxLength(40)]
public string Name { get; set; }
}
But the three parameters are empty when this action is executed.
Here's the error I get in the ModelState
"The JSON value could not be converted to C_Data"
Anyone please can tell me how to port this method?
Thank you.
PD: This action is in an MVC controller not an API controller.

Here's a link that should help.
It looks like you should be able to use C_Data object, put it in an array, and stringify it in the AJAX call, receive an IEnumerable.

For Asp.Net Core, there are two ways to bind the model, ModelBinding and JsonInputFormatter. For sending request with json, it will use JsonInputFormatter and Bind will not work.
In general, I would suggest you try option below:
Controller Action
[HttpPost]
public ActionResult DoSave([FromBody]ItemModel itemModel)
{
return Ok("Worked");
}
Model
public class ItemModel
{
public IEnumerable<C_Data> NewItems { get; set; }
public IEnumerable<C_Data> UpdatedItems { get; set; }
public IEnumerable<C_Data> DeletedItems { get; set; }
}
Request Json
{
"newItems":[{
"Id":3,
"SID":"00000000-0000-0000-0000-000000000000",
"Name":"asd"
}]
}

Related

POST Image and Body Data at the same time in ASP.NET Core WEB API

I am trying to POST image file and a set of parameters using ASP.NET Core. Is there any option/solution to send both Model Data and Image at the same time in POST API. Here is the image of POST API in POSTMAN:
Here is body with Model Information:
If I do it like following code then my companyInfo data is null and image is there.
[HttpPost("PostInformation")]
public async Task<ActionResult<Company>> PostEmployeeJobCategories(IFormFile image, [FromForm]Company companyInfo)
{
}
If I do it like following code then I am getting Unsupported Media Type.
[HttpPost("PostInformation")]
public async Task<ActionResult<Company>> PostEmployeeJobCategories([FromForm]IFormFile image, [FromBody]Company companyInfo)
{
}
Any Advise, how to achieve the goal ?
Thank You
Adding [FromForm] attribute and sending everything through form-data tab in Postman works for me:
public class OtherData
{
public string FirstString { get; set; }
public string SecondString { get; set; }
}
public async Task<IActionResult> Post(IFormFile file, [FromForm]OtherData otherData)
{
return Ok();
}
As vahid tajari pointed out, you can also add your IFormFile to the class definition.
In asp.net core, you can send file and data together, so change your model to:
public class Company
{
public IFormFile Image { get; set; }
public string NameEn { get; set; }
public string Address { get; set; }
//......
}
And your action method to:
[HttpPost("PostInformation")]
public async Task<ActionResult<Company>>PostEmployeeJobCategories([FromForm] Company companyInfo)
{
}

Angular POST request received in NET Core Api as Null

Im POSTing some data via Angular 6, but my Core API keeps returning nulls:
Request:
{"id":0,"name":"test","weight":2,"frequency":2,"activityTypeModelId":3}
Response:
{id: 0, name: null, weight: 0, frequency: 0, activityTypeModelId: 0}
Controller:
[HttpPost("[action]")]
public IActionResult Add([FromForm]Model model)
{
return new JsonResult(model);
}
Angular, using HttpClient:
add(Model: model) {
return this.http.post(this.addUrl, model);
}
API Model:
public class Model
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int Weight { get; set; }
public int Frequency { get; set; }
public int ActivityTypeModelId { get; set; }
}
TS Model:
export class Model{
id?: number;
name?: string;
weight?: number;
frequency?: number;
activityTypeModelId?: number;
}
Everything works fine when I'm using Postman. I already tried with [FromBody]. Where is the problem?
I dont know why, but this fixed my issue:
I created a header:
const header = new HttpHeaders()
.set('Content-type', 'application/json');
Changed the POST function by adding a header and JSON.Stringyfy the object:
add(model: Model): Observable<Model> {
const body = JSON.stringify(c);
return this.http.post<Model>(this.addUrl, body, { headers: header} );
}
Changed [FromForm] to [FromBody].
Adding JSON.stringify(model) in the parameters of the http.post was not working.
JSON that is working with the CORE Api:
{"name":"test","weight":2,"activityTypeModelId":15}
JSON that is not working with the CORE Api:
{name:"test",weight:2,activityTypeModelId:15}
Without the header I encountered a 415 error from the API.
Try
return this.http.post(this.addUrl, JSON.stringify(model) );
I think that, in .NET Core 2.1 is (see https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.1)
HttpPost("[action]")]
//see that I put [FromBody]
public IActionResult Add([FromBody]Model model)
{
//OK is one of several IActionResult
return OK(model);
}
I had this issue and the problem was that I was trying to bind to an interface:
[HttpPost("[action]")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK]
public bool SaveResponse([FromBody]ISaveRequestViewModel request) =>
Service.SaveResponse(request.Responses);
I fixed it by changing it to a class:
[HttpPost("[action]")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK]
public bool SaveResponse([FromBody]SaveRequestViewModel request) =>
Service.SaveResponse(request.Responses);
This model also had to use classes instead of interfaces:
public class SaveRequestViewModel
{
public List<IQuestionResponseViewModel> Responses { get; set; }
}
Became
public class SaveRequestViewModel
{
public List<QuestionResponseViewModel> Responses { get; set; }
}
I guess the model binder just doesn't work with interfaces.

Pass angular querystring to API controller as C# class by http.get

I am sending request. And i want map parameters to C# class in Asp.Net Core Web.API controller. When i write to my method property name as parameter, it is working. But, i write class then it gave me error like "The input was not valid."
I am sending my request by Postman as "Get"request. My request is
http://localhost:5002/api/user/GetUsers?PageFirstIndex=0&IsSortAscending=true&PageSize=10&SortBy=Id
When i wrote like this, it is working and all parameters came with value.
public async Task<ServiceResult> GetUsers(string SortBy, bool IsSortAscending, int Page, byte PageSize)
{...}
But when i wrote like this as class, gave me error "The input was not valid.".
[HttpGet("GetUsers")]
public async Task<ServiceResult> GetUsers(QueryObject queryFilter)
{...}
public class QueryObject
{
public string SortBy { get; set; }
public bool IsSortAscending { get; set; }
public int PageFirstIndex { get; set; }
public byte PageSize { get; set; }
}
You need to use the FromQuery attribute.
public async Task<ServiceResult> GetUsers([FromQuery]QueryObject queryFilter) {}
See the model binding documentation.

.NET Core MVC POST to action not deserializing into model [duplicate]

This question already has answers here:
Asp.net Core 2 API POST Objects are NULL?
(5 answers)
Closed 5 years ago.
I am trying to make my .net core MVC website receive JSON that I send from a view through javascript. Sadly it seems the model is not being filled in with the data.
[HttpPost]
public ActionResult AddSubtitles(AddSubtitleSettingsModel model)
{
/* do things and save to database */
return RedirectToAction("Subtitling");
}
My model:
public class AddSubtitleSettingsModel
{
public HashSet<string> from { get; set; }
public HashSet<string> to { get; set; }
public decimal startupRateLessThanOneMinute { get; set; }
public decimal startupRateBetweenOneAndThreeMinutes { get; set; }
public decimal pricePerSubtitle { get; set; }
public decimal defaultRateTranslators { get; set; }
}
Request being sent to POST action:
I can tell the model is not being filled in because I put a breakpoint inside the action and I checked model, and everything was null/empty:
This is how I send my request from the javascript:
let fromValues: any = fromLanguages.getValue();
let toValues: any = toLanguages.getValue();
var data = {
from: fromValues.map((l: any) => l.value),
to: toValues.map((l: any) => l.value),
startupRateLessThanOneMinute: Number($('#srLt1m').val()),
startupRateBetweenOneAndThreeMinutes: Number($('#stBt13m').val()),
startupRateBetweenThreeAndFiveMinutes: Number($('#stBt35m').val()),
pricePerSubtitle: Number($('#pPS').val()),
defaultRateTranslators: Number($('#dRT').val())
}
fetch('/Settings/AddSubtitles', {
method: 'post',
body: JSON.stringify(data)
}).then((response: any) => {
console.log(response);
});
Any suggestions would be greatly appreciated.
Try adding [FromBody] to your Method
[HttpPost]
public ActionResult AddSubtitles([FromBody]AddSubtitleSettingsModel model)
{
/* do things and save to database */
return RedirectToAction("Subtitling");
}

ASP.NET MVC | HOW to update specific column through EF using Ajax

Im New to ASP.NET MVC.
im just learning MVC and i am stuck in a situation where i want to update data in database using Ajax and EF.
I am using code first approach.
I have two projects in my solution. First is The Web MVC project named as Gem, The other i have entities in it with project name Gem.Domain
I have this entity named Category with file name Category.cs
namespace Gem.Domain
{
public class Category
{
public virtual int CategoryID { get; set; }
public virtual int ParentCategory { get; set; }
public virtual string Name { get; set; }
[MaxLength]
public virtual string Description {get;set; }
public virtual ICollection<Product> Products { get; set; }
}
}
with datasource file
namespace Gem.Domain
{
public interface IStoreDataSource
{
IQueryable<Product> Products { get; }
IQueryable<Category> Categories { get; }
}
}
Now in other Project Named Web
I have Area Registered with Name Admin which contains some controllers, but to be specific CategoriesController.cs reside in it.
And i have this method in this CategoriesController
public string UpdateCategoryName_DT(Category category)
{
return "Just A Test";
}
Finally coming to view.
I want to use ajax on a popup that appears on datatables.
Ajax request works fine.. and request do generates to correct method.
Here is my ajax code.
//Category Name Update Using Ajax.
$('#datatable').on('click', '.editable-submit', function () {
var rowID = $(this).parents('tr').find('td:eq(0)').text();
var updatedCategoryName = $(this).parents('div').siblings('div.editable-input').find('input').val();
var postData = {
ID: rowID,
CategoryName: updatedCategoryName
};
//For Ajax Request.
$.ajax({
type:"POST",
data:JSON.stringify(postData),
url:"#Url.Action("UpdateCategoryName_DT", "Categories", new { area = "Admin" })",
success: function (output) {
console.log(output);
}
});
});
Below is generated Ajax Request screen cap, using firebug for showing post info.
Main Question:
I want to know how to get the posted values in the controller through this ajax request and update the category name in database on base of posted values.
e-g i am getting ID of row and New Category name in ajax post, and i want to update the record in category table in database using entity framework.
How to do it, and what is the right method as i am using ajax.
I have tried youtube and tutorials but i am not understanding it.
I have DBContext with name of StoreDb which resides in Gem Project and Infrastructure Folder.
namespace Gem.Infrastructure
{
public class StoreDb : DbContext, IStoreDataSource
{
public StoreDb() : base("GemStoreConnection")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
IQueryable<Product> IStoreDataSource.Products
{
get
{
return Products;
}
}
IQueryable<Category> IStoreDataSource.Categories
{
get
{
return Categories;
}
}
}
}
plus i am using structuremap.mvc5 dependency resolution.
namespace Gem.DependencyResolution {
using Domain;
using Infrastructure;
using StructureMap.Configuration.DSL;
using StructureMap.Graph;
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IStoreDataSource>().Use<StoreDb>();
}
#endregion
}
}
i am new but i have did this setup using tutorial for what i have understood so far. but there is nothing related to ajax so i need little help with ajax.
-=-=-=-=-=-=-=-=-=-=
Update:
Used a debugger, i think i am getting null, values are posting fine but i am getting null in controller ?
=-=-=-=-=-=-=-=-=--=-=-=-=-
Update 2:
i removed the JSON.stringify() and changed the Posted Data to this
var postData = {
CategoryID: rowID,
Name: updatedCategoryName
};
As now it matches to schema, so its working now..
but on other hand it also exposes my DB Schema. What if i want to post Ajax with Different ValuesNames, other than the database column names, what to do in that case?
Based on your comments, I think you are not much familiar with HTTP concept.
The simpliest way to read the object from ajax request (or any other POST request) is to update your Category model property names to match the once in json request (you can keep the first letter upper in c#, the rest has to be the same). Right now, your model has CategoryID and CategoryName, but in the json request, you are sending ID and Name parameters. Then, you need to add [FromBody] attribute to you action:
public string UpdateCategoryName_DT([FromBody]Category category)
{
return "Just A Test";
}
The attribute tells the framework, that it should parse the json from body of the request and creates an instance of Category object. Then you will not get null in as seen in your updated question.
UPDATE
You can have different names in json request and in database. You just need to use JsonProperty attribute on the property.
public class Category
{
[JsonProperty(PropertyName = "yourDesiredNameInJsonRequest")]
public virtual int CategoryID { get; set; }
public virtual int ParentCategory { get; set; }
[JsonProperty(PropertyName = "yourAnotherDesiredNameInJsonRequest")]
public virtual string Name { get; set; }
[MaxLength]
public virtual string Description {get;set; }
public virtual ICollection<Product> Products { get; set; }
}
To clarify the code above - the attribute will tell Json.Net to deserialize property "yourDesiredNameInJsonRequest" in your json and save it to Category.CategoryID field. It is just mapping.

Categories