I have created an API in ASP.NET Core 2.2 framework as per below sample
[HttpPost]
[Authorize]
public IActionResult AddInvoiceRest([FromBody]AddInvoiceRetailRest[] InvoiceDetail)
{
}
here AddInvoiceRetailRest is class object. I want to pass multiple objects as array.
I am doing testing with postman. I have passed this array from raw body as per below sample.
{
"InvoiceDetail": [
{
"InvoiceMst": [
{
"InvoiceNo": 0,
"CustId": 0,
"SubTotal": 93,
"TaxAmount": 13
}
]
}
]
}
problem is here in api i received blank array without adding [Frombody] and when i add [FromBody] , Api doesn't call and shows error like below
{
"InvoiceDetail": [
"The input was not valid."
]
}
definition of class is
public class AddInvoiceRetailRest
{
public AddInvoiceMst[] InvoiceMst { get; set; }
public AddInvoiceItemDetail[] InvoiceItemDetail { get; set; }
public AddInvoicePaymentDetail[] InvoicePaymentDetail { get; set; }
[Optional]
public AddInvoiceItemLogDetail[] InvoiceItemLog { get; set; }
[Optional]
public string BillDetail { get; set; }
[Optional]
public PumpCartObject[] InvoicePumpCart { get; set; }
[Optional]
public InvoiceFeeOrDeposite[] InvoiceFeeOrDeposite { get; set; }
}
Here in the question, I just put a sample of the request, not all keys.
Can someone please let me know what I'm doing wrong?
Your issue is that you are expecting array in your API but you are passing a single object json and not an array.
Try this:
[
{
"InvoiceMst":
[
{
"InvoiceNo": 0,
"CustId": 0,
"SubTotal": 93,
"TaxAmount": 13
}
]
}
]
The posted data does not match the expectation of the action.
One option is to create a model that matches the expected data
public class InvoiceDetailModel {
public AddInvoiceRetailRest[] InvoiceDetail { get; set; }
}
And bind to that in the action
[HttpPost]
[Authorize]
public IActionResult AddInvoiceRest([FromBody]InvoiceDetailModel model) {
if(ModelState.IsValid) {
AddInvoiceRetailRest[] invoiceDetail = model.InvoiceDetail;
//...
}
return BadRequest(ModelState);
}
For example:
api/file/occurrence?sha256=...
[HttpGet]
[Route("api/file/")]
public async Task<IHttpActionResult> GetFileBySha256Async([FromUri] FilesBySha256RequestDTO requestDTO)
{
}
api/file/occurrence?sha256=...&from_date=..&to_date=..
[HttpGet]
[Route("api/file/")]
public async Task<IHttpActionResult> GetFileBySha256AndDateAsync([FromUri] FilesBySha256AndDateRequestDTO requestDTO)
{
}
And the DTOs:
public class FilesBySha256RequestDTO
{
public string sha256 { get; set; }
}
public class FilesBySha256AndDateRequestDTO
{
public string sha256 { get; set; }
public DateTime? from_date { get; set; }
public DateTime? to_date { get; set; }
}
How can I accomplish this behavior? I am getting the following exception:
"ExceptionMessage": "Multiple actions were found that match the request: \r\nGetFileBySha256Async on type Cynet.Client.WebAPI.Controllers.FileController\r\nGetFileOccurrencesSha256 on type Cynet.Client.WebAPI.Controllers.FileController
It is not possible to distinguish the route between two because api/file/occurrence?sha256=...&from_date=..&to_date=.. and api/file/occurrence?sha256=... is the same thing for the framework. The first thing you can do is changing the second route like api/fileOnDate/. If it is impossible to do it, you can define a third function and use it as a manual router such as;
[HttpGet]
[Route("api/file/")]
public async Task<IHttpActionResult> GetFileBy([FromUri] FilesBySha256AndDateRequestDTO requestDTO)
{
if (!requestDTO.from_date.HasValue && !requestDTO.to_date.HasValue)
{
return await this.GetFileBySha256Async(new FilesBySha256RequestDTO() { sha256 = requestDTO.sha256 });
}
else
{
return await this.GetFileBySha256AndDateAsync(requestDTO);
}
}
private async Task<IHttpActionResult> GetFileBySha256Async(FilesBySha256RequestDTO requestDTO)
{
}
private async Task<IHttpActionResult> GetFileBySha256AndDateAsync(FilesBySha256AndDateRequestDTO requestDTO)
{
}
hope it helps.
Since am new to web api, i am finding some difficulty to post json List to Web API.
Json
[
{
"ItemId":20,
"RegId":"VISIT0001778",
"BLoadDetailId":"8/31/2018 12:28:10 PM",
"OrderReferenceNo":null,
"StartTime":"0001-01-01T00:00:00",
"InvalidItemMsg":"",
"InvalidItemstatus":false,
"BLoadingBay":"Chute 009",
"BLoadingBayCode":null,
"BLoadingBayID":7,
"RFID":7123,
"GangId":2,
"BOrderTransfer":false,
"BLoadedBags":0.0,
"BRemainingBags":0.0,
"BConversionValue":null,
"WHid":2
}
]
class :
public class clsStartTimeUpdate
{
public int ItemId { get; set; }
public string RegId { get; set; }
public string BLoadDetailId { get; set; }
public string OrderReferenceNo{ get; set; }
public DateTime StartTime { get; set; }
public string InvalidItemMsg { get; set; }
public bool InvalidItemstatus { get; set; }
public string BLoadingBay { get; set; }
public string BLoadingBayCode { get; set; }
public int? BLoadingBayID { get; set; }
public long? RFID { get; set; }
public int? GangId { get; set; }
public bool BOrderTransfer { get; set; }
public decimal BLoadedBags { get; set; }
public decimal BRemainingBags { get; set; }
public string BConversionValue { get; set; }
public int? WHid { get; set; }
}
Json request
http://localhost:49290/api/config/Post?StartTimeDetails=[enter image description here][1][{%22ItemId%22:20,%22RegId%22:%22VISIT0001778%22,%22BLoadDetailId%22:%228/31/2018%2012:28:10%20PM%22,%22OrderReferenceNo%22:null,%22StartTime%22:%222001-01-01T00:00:00%22,%22InvalidItemMsg%22:%22%22,%22InvalidItemstatus%22:false,%22BLoadingBay%22:%22Chute%20009%22,%22BLoadingBayCode%22:null,%22BLoadingBayID%22:7,%22RFID%22:7123,%22GangId%22:2,%22BOrderTransfer%22:false,%22BLoadedBags%22:0.0,%22BRemainingBags%22:0.0,%22BConversionValue%22:null,%22WHid%22:2}]
Method WebAPI
[HttpPost]
public HttpResponseMessage Post([FromUri]List<clsStartTimeUpdate> StartTimeDetails)
{
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
result:
[{"ItemId":0,"RegId":null,"BLoadDetailId":null,"OrderReferenceNo":null,"StartTime":"0001-01-01T00:00:00","InvalidItemMsg":null,"InvalidItemstatus":false,"BLoadingBay":null,"BLoadingBayCode":null,"BLoadingBayID":null,"RFID":null,"GangId":null,"BOrderTransfer":false,"BLoadedBags":0.0,"BRemainingBags":0.0,"BConversionValue":null,"WHid":null}]
return result doesnot assign the values as in the Json.
May be this is a simple situation , but i really appreciate the help.
It seems that you want to convey your json with HttpGet request instead of HttpPost then you can follow below,
1) Send Json with HttpGet
Method: Get
Url: http://localhost:49290/api/config/MyGet?StartTimeDetails=[{%22ItemId%22:20,%22RegId%22:%22VISIT0001778%22,%22BLoadDetailId%22:%228/31/2018%2012:28:10%20PM%22,%22OrderReferenceNo%22:null,%22StartTime%22:%220001-01-01T00:00:00%22,%22InvalidItemMsg%22:%22%22,%22InvalidItemstatus%22:false,%22BLoadingBay%22:%22Chute%20009%22,%22BLoadingBayCode%22:null,%22BLoadingBayID%22:7,%22RFID%22:7123,%22GangId%22:2,%22BOrderTransfer%22:false,%22BLoadedBags%22:0.0,%22BRemainingBags%22:0.0,%22BConversionValue%22:null,%22WHid%22:2}]
Web Api Method:
[HttpGet]
public HttpResponseMessage MyGet(string StartTimeDetails)
{
List<clsStartTimeUpdate> clsStartTimeUpdates = JsonConvert.DeserializeObject<List<clsStartTimeUpdate>>(StartTimeDetails);
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
Note: Its bad practice to send huge json in query string, so for use HttpPost instead
2) Send Json with HttpPost
Method: Post
Url: http://localhost:49290/api/config/MyPost
Data:
[
{
"ItemId":20,
"RegId":"VISIT0001778",
"BLoadDetailId":"8/31/2018 12:28:10 PM",
"OrderReferenceNo":null,
"StartTime":"0001-01-01T00:00:00",
"InvalidItemMsg":"",
"InvalidItemstatus":false,
"BLoadingBay":"Chute 009",
"BLoadingBayCode":null,
"BLoadingBayID":7,
"RFID":7123,
"GangId":2,
"BOrderTransfer":false,
"BLoadedBags":0.0,
"BRemainingBags":0.0,
"BConversionValue":null,
"WHid":2
}
]
Web Api Method:
[HttpPost]
public HttpResponseMessage MyPost([FromBody]List<clsStartTimeUpdate> StartTimeDetails)
{
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
For complex types Always use [FromBody] in the argument.
[HttpPost]
public HttpResponseMessage Post([FromBody]List<clsStartTimeUpdate> StartTimeDetails)
{
return base.BuildSuccessResult(HttpStatusCode.OK, StartTimeDetails);
}
And then specify your query object in Body.
Note: To specify the value in the body, You will need an API client like Postman or Swagger.
https://www.getpostman.com/
In Postman,
Select Post method and specify the URL,
Then go to "Body" tab and select raw.
Specify JSON as type.
In the body, paste your data.
{ [
{
"ItemId":20,
..........
}
]}
The Other answer by #ershoaib is the real fix for the problem that OP is facing. However, I am leaving this answer as it is the standard which should be followed.
Since you are using post you should expect the data in the controller method to come from body. See related issue here
I'm just starting out with .NET Web API programming, and I have a question for seasoned .NET developers - what is the "correct" way to pass an object reference into a Create endpoint?
I have the following models:
public class Task
{
public int ID { get; set; }
public string Title { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And my Controller - endpoint to create a Task:
[HttpPost]
public async Task<IActionResult> PostTask([FromBody] Models.Task task)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Task.Add(task);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTask", new { id = task.ID }, task);
}
By default, this has some interesting behavior. It expects an entire User model to be passed into the POST request (see below), and will actually create the user when passed:
{
"id": 0,
"title": "string",
"user": {
"id": 0,
"firstName": "string",
"lastName": "string"
}
}
I understand technically why it would do this, but this is definitely not acceptable behavior in a real app - so my question is - what is the "correct/appropriate" way to pass a UserID and do validation on the model in .NET? Should I forego the use of "ModelState.IsValid" and do manual validation?
As a secondary question - I am using NSwag to generate Swagger docs from my API, and it's showing "id" as a parameter that needs to be passed into the POST method. Obviously, ID cannot be passed as it's generated in code, but is there a way to get Swagger to not show ID as being a passable property?
Then create a data transfer model that exposes only the data you want sent over the wire.
public class NewTaskDto {
[Required]
public string Title { get; set; }
[Required]
public int UserId { get; set; }
}
and map the data to your models on the server, along with what ever validation is required. For example checking that the UserId exists and is valid.
[HttpPost]
public async Task<IActionResult> PostTask([FromBody] NewTaskDto data) {
if(data != null) {
validateUserId(data.UserId, ModelState);
}
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
Models.Task task = MapDataToTask(data); //create Task and copy members
await saveTask(task);
return CreatedAtAction("GetTask", new { id = task.ID }, task);
}
That way the doc will only see the and report on the exposed members.
The attribute [Required] is mandatory and then you can check parameter.
I have a post method, the parameter School is always null every time it is being called. I make sure to send the right data across, I am using Postman, but when I inspect it with a breakpoint, I get a null object.
What I send with Postman:
{
"Id": "",
"Name": "Test"
}
Controller:
[HttpPost]
public IActionResult Post([FromBody] School school)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest();
}
var schoolData = Mapper.Map<School, Data.School>(school);
var schoolModel = _schoolRepository.AddSchool(schoolData);
return CreatedAtRoute("GetSchool", new { id = schoolModel }, schoolModel);
}
catch (Exception e)
{
return LogException(e);
}
}
Model:
public class School
{
public Guid Id { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public IEnumerable<Teacher> Teachers { get; set; }
public IEnumerable<Class> Classes { get; set; }
}
The problem is that your model requires a Guid and you are sending an empty string:
"Id": ""
Regardless of the Id not being [Required], the model still cannot parse the data. What you can do, however, is to exclude it from binding:
public IActionResult Post([FromBody][Bind(Include = nameof(School.Name))] School school)
Or, a better option is to use a ViewModel. In this case, however, since you only have one property, I'd just use this:
public IActionResult Post([FromBody]string name)