I have applications with a simple [HttpPost] method in controller that should accept an object from the query body (query from postman).
However, I have an error every time because null comes to the method - not object.
Model:
public class MeetupDto
{
public string Name { get; set; }
public string Organizer { get; set; }
public DateTime Date { get; set; }
public bool IsPrivate { get; set; }
}
Method:
[HttpPost]
public ActionResult Post([FromBody]MeetupDto model)
{
...
}
The method works fine when I shrink the model down to the name and organizer only. When there's a date or a bool, it doesn't work.
Postman (body raw+json):
https://localhost:44398/api/meetup/
"name": "JsEvent",
"organizer": "chrome",
"date": "2020-07-26 15:20:00",
"isPrivate": "false"
You have two problems:
You're sending a string for IsPrivate, when it's expecting a bool
You're not using the correct date format
Change your JSON to this:
// Note the 'T' in the date
{
"name": "JsEvent",
"organizer": "chrome",
"date": "2020-07-26T15:20:00",
"isPrivate": false
}
and it should bind your model correctly.
I am using WebAPI to get the POST message. The body of the POST message:
{"datetime":"2017-01-06T20:40:44.2401244Z","filename":"somefilename.csv","datasourcename":"MyDataSource","error":"The uploaded file has no tags."}
The header included with the POST:
Content-type: application/json
My action method that handle the message:
[HttpPost]
public void SaveMessage([FromBody] string msg)
{
MyModel w = JsonConvert.DeserializeObject<MyModel>(msg);
db.MyModels.Add(w);
db.SaveChanges();
}
My question is how do I encode the JSON so that I can parse it? I can't change the incoming message. What can I do in my method so that I can read it? Right now msg always give me null.
Given that you have no control over the data being sent, then as suggested in the comments, make sure you have a model that matches the desired object model of the JSON
public class MyModel {
public DateTime datetime { get; set; }
public string filename { get; set; }
public string datasourcename { get; set; }
public string error { get; set; }
}
And take advantage of the model binding capability of the Web API framework and let the action bind a strongly typed model from the body of the request.
[HttpPost]
public IHttpActionResult SaveMessage([FromBody] MyModel model) {
if(ModelState.IsValid) {
db.MyModels.Add(w);
if(db.SaveChanges() > 0)
return Ok();
}
return BadRequest();
}
If there are any issues with saving the message the action will return HTTP Status Code 400 Bad Request, otherwise it will return HTTP Status Code 200 OK.
In servicestack, I am trying to process a webhook which sends the following JSON body to a service stack endpoint:
{
"action": "actionType1",
"api_version": "1.00",
"data": {
"id": "a8d316b8-10a7-4440-a836-9bd354f656db",
//VARIABLE other properties / structure
}
}
Which I am trying to map to the following request object:
[Route("/public/Webhookhandler", HttpVerbs.Post)]
public class MyCustomRequst
{
public string action { get; set; }
public string api_version { get; set; }
public string data { get; set; } //Will be the remaining JSON
}
However, when the service stack framework processes this - the value in "data" is the correct part of the JSON body, but with all of the quotes removed - so it is no longer valid.
I have tried to override the serialization for the whole request object using something like this:
JsConfig<MyCustomRequst>.DeSerializeFn = DeserializeMyRequestDto;
public MyCustomRequst DeserializeMyRequestDto(string rawBody)
{
var result = rawBody.FromJson<MyCustomRequst>();
return result
}
But even in this case, the value of the "rawBody" variable is still the correct JSON data but with all the quotes removed, e.g.
{
action:actionType1,
api_version:1.00,
data:{id:a8d316b8-10a7-4440-a836-9bd354f656db}
}
Am I doing something wrong here? I am unsure whether I am trying to make service stack do something it is not intended to do, or whether I am missing something that would make this work.
Any help would be appreciated :-)
Your DTO should closely match the shape of the JSON, if it's always a flat object you can use a string Dictionary, e.g:
[Route("/public/Webhookhandler", HttpVerbs.Post)]
public class MyCustomRequst
{
public string action { get; set; }
public string api_version { get; set; }
public Dictionary<string,string> data { get; set; }
}
If it's a more nested object structure you can use a JsonObject for a more flexible API to parse dynamically.
I'm trying to do filtering function for KendoUI Grid.
Kendo sends data as form-data:
take:20
skip:0
page:1
pageSize:20
filter[filters][0][operator]:eq
filter[filters][0][value]:abc
filter[filters][0][field]:No
filter[logic]:and
I tried to deserialize it using dto:
public class FilteringRule
{
public string Field { get; set; }
public string Operator { get; set; }
public string Value { get; set; }
}
public class FilteringInfo
{
public string Logic { get; set; }
public FilteringRule[] Filters { get; set; }
}
public class FilteredQuery
{
FilteringInfo Filter { get; set; }
//...
}
but deserialization fails:
'filter[filters][0][operator]' does not exist on type 'FilteredQuery'
'filter[filters][0][value]' does not exist on type 'FilteredQuery'
'filter[filters][0][field]' does not exist on type 'FilteredQuery'
'filter[logic]' does not exist on type 'FilteredQuery'
How to make this work? When I manually send json instead of form-data it works.
Can kendo send json? Setting contentType: "application/json" in datasource doesn't help. Kendo still sends data as form-data, servicestack throws error: "unable to bind request".
ServiceStack allows sending complex objects via QueryStrings using the lightweight JSV Syntax which from your example would look something like:
?filter={filters:[{operator:eq,value:abc,field:no}],{logic:and}}
But since Kendo is unlikely to support this syntax, you would be better off sending JSON if it supports it which would naturally map to your DTOs.
I'm using a REST client that I have already used to post other complex objects in my database.
I can't post too much code because it is work-related but here is what I am trying to send to the web API from my rest client:
{"BinID":"PBN0012","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-07-11T17:51:49.670028+00:00","ModifiedByUID":"00000000-0000-0000-0000-000000000000","BatchBins":[],"BinsInUses":[]}
That is the object after serialized by JsonConvert.
I'm using HttpWebResponse (and getting an HTTP 500 Internal Server Error - and System.ArgumentException on Xamarin "Illegal Characters in Path".
Even when I try to post directly through SOAP UI I get this message:
{
"$id": "1",
"Message": "An error has occurred."
}
This is what I get when I type "(IP Address....)/Api/Bins/GetBins into my browser:
[{"$id":"1","BatchBins":[],"BinsInUses":[{"$id":"2","Bin":{"$ref":"1"},"BinID":"PBN0001","EntrySequence":1}],"BinID":"PBN0001","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"3","BatchBins":[],"BinsInUses":[{"$id":"4","Bin":{"$ref":"3"},"BinID":"PBN0002","EntrySequence":2}],"BinID":"PBN0002","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"5","BatchBins":[],"BinsInUses":[{"$id":"6","Bin":{"$ref":"5"},"BinID":"PBN0003","EntrySequence":3}],"BinID":"PBN0003","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"7","BatchBins":[],"BinsInUses":[{"$id":"8","Bin":{"$ref":"7"},"BinID":"PBN0004","EntrySequence":4}],"BinID":"PBN0004","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"9","BatchBins":[],"BinsInUses":[],"BinID":"PBN0005","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"10","BatchBins":[],"BinsInUses":[],"BinID":"PBN0006","BinType":"Dryer","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"11","BatchBins":[],"BinsInUses":[],"BinID":"PBN0007","BinType":"Dryer","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"12","BatchBins":[],"BinsInUses":[],"BinID":"PBN0008","BinType":"Dryer","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"13","BatchBins":[],"BinsInUses":[],"BinID":"PBN0009","BinType":"Dryer","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"14","BatchBins":[],"BinsInUses":[],"BinID":"PBN0010","BinType":"Dryer","IsDeleted":false,"ModifiedDateTime":"2014-06-02T00:00:00","ModifiedByUID":"b6c1b64f-874b-4180-949a-97c77cc4194c"},{"$id":"15","BatchBins":[],"BinsInUses":[],"BinID":"PBN0011","BinType":"Wash","IsDeleted":false,"ModifiedDateTime":"2014-07-03T15:34:11.67","ModifiedByUID":"9f05ab31-2521-4a7e-9258-b7092aae5058"}]
The Bin class consists of:
public partial class Bin
{
public Bin()
{
this.BatchBins = new HashSet<BatchBin>();
this.BinsInUses = new HashSet<BinsInUse>();
}
public string BinID { get; set; }
public string BinType { get; set; }
public bool IsDeleted { get; set; }
public System.DateTime ModifiedDateTime { get; set; }
public System.Guid ModifiedByUID { get; set; }
public virtual ICollection<BatchBin> BatchBins { get; set; }
public virtual ICollection<BinsInUse> BinsInUses { get; set; }
}
And the table for "Bin" in the database has the following columns:
BinID (PK, nvarchar(100),not null)
BinType (nvarchar(5), not null)
IsDeleted (bit, not null)
ModifiedDateTime (datetime,not null)
ModifiedByUID (uniqueidentifier, not null)
I know I used the correct types for each of these considering my other objects passed through just fine. Any Suggestions on how to get the json string to post??
Just so everybody knows, in case they get this kind of error and can't find a solution, you might have the same problem I had.
I'd copied and pasted methods in my API to save time (because many methods/classes had similar names) and I ended up with two methods with the same exact name; I had forgotten to change the method name. Just for future reference.