Custom ASP.Net Core JSON model binder - asp.net-core

My posted JSON object is this:
{{
"emails": [
{
"To": "info#gmail.com",
"Subject": "Subject",
"Body": "Body",
"ID": "d3d13242-6eff-4c57-b718-ef5ad49fe301"
},
{
"To": "hr#gmail.com",
"Subject": "Subject",
"Body": "Body",
"ID": "101edaf0-fcb4-48fc-9e9e-0d7492b591b0"
}
]
}}
By default ASP.NET model binder will not bind this JSON object and as you can see here I get always null when I send post request to the API:
[HttpPost, Route("Send")]
public async Task<IActionResult> Send(Email[] emails)
{
var toSave = from email in emails
select new EmailQueueItem
{
Html = true,
To = email.To,
Subject = email.Subject,
Body = email.Body
};
await Database.BulkInsert(toSave.ToArray());
return Ok();
}
emails property is always null. I would appreciate any help for creating custom model binder that handel this kind of JSON objects.

The issue is that you are actually sending an object containing one property named emails, not an array, to the controller
Option one:
The client object needs to contain just the array
[
{
"To": "info#gmail.com",
"Subject": "Subject",
"Body": "Body",
"ID": "d3d13242-6eff-4c57-b718-ef5ad49fe301"
},
{
"To": "hr#gmail.com",
"Subject": "Subject",
"Body": "Body",
"ID": "101edaf0-fcb4-48fc-9e9e-0d7492b591b0"
}
]
Then read the array from the request body
public async Task<IActionResult> Send([FromBody]Email[] emails)
Option 2:
When you define the array like this in the client
{
"emails":...
}
You need to match that object setup on the controller by defining a model which contains a property called emails
public class RequestModel
{
public Email[] emails { get; set; }
}
Then in the controller method, use the model and read it from the body
public async Task<IActionResult> Send([FromBody]RequestModel emails)

Related

SQL Server stored procedure in .NET Core 6 Web API to produce JSON data used in Angular app

I have a SQL Server stored procedure that has an ID parameter and returns a string in JSON format that is needed in the Angular app.
Here is a sample of the JSON needed:
[
{
"type": "date",
"name": "asofdate",
"ui":
{
"label": "As Of Date",
"placeholder": "Enter a date"
},
"validators": { "required": "true" }
},
{
"type": "select",
"name": "scope",
"ui": { "label": "Scope", "placeholder": "Select a scope" },
"validators": { "required": "true" },
"source": [
{ "value": 1, "text": "ABC" },
{ "value": 2, "text": "CDE" },
{ "value": 3, "text": "FGI" }
]
}
]
Here is a what the result of running the stored procedure looks like:
When I run the Web API passing the ID parameter to the stored procedure, I would like to capture the response as a JSON object to be used in the Angular app.
But the Web API is returning this:
[
{
"jsonResponse": "[
{
\"type\":\"date\",
\"name\":\"asofdate\",
\"ui\":{\"label\":\"As Of Date\",\"placeholder\":\"Enter a date\"},
\"validators\":{\"required\":\"true\"}
}
,
{
\"type\":\"select\",
\"name\":\"scope\",
\"ui\":{\"label\":\"Scope\",\"placeholder\":\"Select a scope\"},
\"validators\":{\"required\":\"true\"},
\"source\":[{\"value\":1,\"text\":\"ABC\"},{\"value\":2,\"text\":\"DEF\"},{\"value\":3,\"text\":\"GHI\"}]}
}
]
Is there a way to get the JSON response from the Web API without all the "\" and without:
{
"jsonResponse": "
so that it matches the sample above?
Here is the code from the Web API:
[HttpGet("{ReportID}")]
public async Task<ActionResult<IEnumerable<usp_ReportParameterResult>>> GetReportParameters(int ReportID)
{
if (_context.usp_ReportParameterAsync == null)
{
return NotFound();
}
var op = new OutputParameter<int>();
var JSONresponse = await _context.usp_ReportParameterAsync(ReportID, op);
if (JSONresponse == null)
{
return NotFound();
}
return JSONresponse;
}
The stored procedure uses JSON_QUERY and JSON PATH to create the needed nested arrays.
So, in the angular code I have the following hard-coded:
TESTDATA:any[] = [
{
type:'text',
name:'firstName',
validators:{
required:true
},
ui:{label:'First Name',placeholder:'Enter Your First Name'}
}
,
{
"type":"date",
"name":"asofdate",
"ui":{"label":"****As Of Date","placeholder":"Enter a date","class":["date-picker-wrapper"]},
"validators":{"required":"true"}
}
]
What I need is instead of this data being hrad-coded it is being dynamically generated from a Web API.
The hard-coded data looks like the following from browser debug:
[![enter image description here][2]][2]
From the web api data looks like the following:
It is not an array like the TESTDATA. Is the a way to get response from web api into an array format as required?
Actually, easiest solution was to remove the backlashes in the Angular app by simply doing the following:
for (let item of this.formattedJSON) {
item.ui = JSON.parse(item.ui);
item.validators = JSON.parse(item.validators);
}

Can't post list of object to a web api

What am i doing wrong here (i'm using .net core 3.1):
Object:
public class Member
{
public int NUM {get;set;}
}
Post Action
[HttpPost]
public async Task<IActionResult> Post([FromBody] IEnumerable<Member> members)
JSON
{
[
{"NUM": 5},
{"NUM": 4}
]
}
Error i'm getting from Postman
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|fc86d5d0-498dec466e59f3c5.",
"errors": {
"$": [
"The JSON value could not be converted to System.Collections.Generic.IEnumerable`1[TRS.Gemini.MemberApi.Controllers.Member]. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
]
}
}
I tried to post a single object rather than a collection and that worked fine. The object in the end of course will be a lot more complex but i wanted to start simple.
For the object you've defined, the valid JSON input would be:
[
{"NUM": 5},
{"NUM": 4}
]
Note the lack of { } brackets.
The payload you've specified is also not valid JSON.

Return string from Web API .NET Core get operation

I have a get operation that I want to return a string from. An example would be
"000875"
When I return this string from a controller in my Web API Controller in full .NET, it formats it like this:
{
"Property": "000875"
}
When I return the string in my converted .NET Core Controller it returns this:
{
"$id": "1",
"$type": "System.Net.Http.HttpResponseMessage, System.Net.Http",
"Version": "1.1",
"Content": {
"$id": "2",
"$type": "System.Net.Http.StringContent, System.Net.Http",
"Headers": [
{
"Key": "Content-Type",
"Value": [
"application/json; charset=utf-8"
]
}
]
},
"StatusCode": "OK",
"ReasonPhrase": "OK",
"Headers": [],
"TrailingHeaders": [],
"RequestMessage": null,
"IsSuccessStatusCode": true
}
It is interesting to note that the value is not even in there!
I am running some interesting JSON Serialization to make BreezeJs work with .NET Core. It is possible that it is the cause of this weirdness:
.AddNewtonsoftJson(opt =>
{
// Let Breeze say how we serialize. This adds in the Type and Id options the way breeze expects
var jsonSerializerSettings = JsonSerializationFns.UpdateWithDefaults(opt.SerializerSettings);
......
I am hoping for a way to get strings through without all this mess. Can that be done?
I get the impression that the subject action definition returns HttpResponseMessage.
public HttpResponseMessage MyAction(....
HttpRequestMessage is no longer a first class citizen in asp.net-core framework and will be treated as a normal model and serialized.
That explains the JSON you are seeing with your controller
The syntax needs to be updated to return IActionResult derived responses
public IActionResult MyAction() {
//...
return Ok("000875");
}
ActionResult<T>
public ActionResult<string> MyAction() {
//...
if(somecondition)
return NotFound();
return "000875";
}
or the model itself.
public string MyAction() {
//...
return "000875";
}
Reference Controller action return types in ASP.NET Core Web API

Web API Post won't bind data to method parameters

I hate to ask because there are many questions dealing with this issue, but I haven't gotten any to post any data to an ASP.NET Web API 2 method. This is all coming and going to the same host and port.
Here's my server side code:
[RoutePrefix("api/help")]
public class HelpAPIController : ApiController
...
public class Item {
public string name { get; set; }
public string position { get; set; }
}
[Route]
[HttpPost]
public void Post([FromBody] Item[] stuff)
; // we get here but data is always null or zero items in array
}
Javascript:
"use strict";
var a = [{ "name": "me", "position": "here" },
{ "name": "me", "position": "here" },
{ "name": "me", "position": "here" }];
jQuery.ajax({
type: "POST",
datatype: "application/json",
url: "/api/help/",
data: { "stuff": JSON.stringify(a) },
success: function (data) { alert(data); },
error: function (error) {
...
}
});
Actual request content (no hard returns):
stuff:[{"name":"me","position":"here"},
{"name":"me","position":"here"},
{"name":"me","position":"here"}]
You are not passing a content type to the server. datatype is what response type you expect from the server. contentType is what you are sending. The default contentType in jQuery is application/x-www-form-urlencoded; charset=UTF-8. You need to add/edit:
dataType: "json",
contentType: "application/json",
You don't need to wrap the data array with an object. This will do:
data: JSON.stringify(a),
Try adding contentType: "application/json" in your Ajax POST.
You have dataType, which I think is what you expect back from server whereas contentType is what you are sending.

Using .Net Core Web API with JsonPatchDocument

I am using JsonPatchDocument to update my entities, this works well if the JSON looks like the following
[
{ "op": "replace", "path": "/leadStatus", "value": "2" },
]
When i create the object it converts it with the Operations node
var patchDoc = new JsonPatchDocument<LeadTransDetail>();
patchDoc.Replace("leadStatus", statusId);
{
"Operations": [
{
"value": 2,
"path": "/leadStatus",
"op": "replace",
"from": "string"
}
]
}
if the JSON object looks like that the Patch does not work. I believe that i need to convert it using
public static void ConfigureApis(HttpConfiguration config)
{
config.Formatters.Add(new JsonPatchFormatter());
}
And that should sort it out, the problem is i am using .net core so not 100% sure where to add the JsonPatchFormatter
I created the following sample controller using the version 1.0 of ASP.NET Core. If I send your JSON-Patch-Request
[
{ "op": "replace", "path": "/leadStatus", "value": "2" },
]
then after calling ApplyTo the property leadStatus will be changed. No need to configure JsonPatchFormatter. A good blog post by Ben Foster helped me a lot in gaining a more solid understanding - http://benfoster.io/blog/aspnet-core-json-patch-partial-api-updates
public class PatchController : Controller
{
[HttpPatch]
public IActionResult Patch([FromBody] JsonPatchDocument<LeadTransDetail> patchDocument)
{
if (!ModelState.IsValid)
{
return new BadRequestObjectResult(ModelState);
}
var leadTransDetail = new LeadTransDetail
{
LeadStatus = 5
};
patchDocument.ApplyTo(leadTransDetail, ModelState);
if (!ModelState.IsValid)
{
return new BadRequestObjectResult(ModelState);
}
return Ok(leadTransDetail);
}
}
public class LeadTransDetail
{
public int LeadStatus { get; set; }
}
Hope this helps.