net core API Object Array Parameter always null - c#

I have this class
public enum Comparison
{
Eq,
Neq
}
public class Filter
{
public string Name { get; set; }
public string Value { get; set; }
public Comparison Comparison { get; set; }
}
My Action should accept an Array of Filters
public async Task<IActionResult> Index([FromQuery(Name = "filter")]Filter[] filter)
{
return Ok();
}
filter is always an empty array with this url
https://localhost:5001/configtemplates?filter=%7B%0A%20%20%22Name%22%3A%20%22Name%22%2C%0A%20%20%22Value%22%3A%20%22Test%22%2C%0A%20%20%22Comparison%22%3A%201%0A%7D&filter=%7B%0A%20%20%22name%22%3A%20%22Description%22%2C%0A%20%20%22value%22%3A%20%22Test%22%2C%0A%20%20%22comparison%22%3A%201%0A%7D.
When change the type of filter to string, it maps a string[]. therefore i expect the issue to be in the binding of Filter[].
How can i make it work bind to Filter[]?
UPDATE:
Filter[] can be parsed if the URL is in this format https://localhost:5001/configtemplates?filter%5B0%5D.n=Name&filter%5B0%5D.c=6&filter%5B0%5D.v=bla&filter%5B1%5D.n=Name&filter%5B1%5D.c=6&filter%5B1%5D.v=bli&filter%5B2%5D.n=ID&filter%5B2%5D.c=6&filter%5B2%5D.v=blu
which is OK for now but i am not sure if this is official standard. e.g. swagger does not work.
Thank you!

You can use System.Text.Json to Deserialize it.
Example link: https://localhost:5001/api/test/Index?filter=[{%22name%22:%22HalloWelt%22},{%22Name%22:%22bbbb%22}]
public async Task<IActionResult> Index([FromQuery(Name = "filter")]string filter)
{
List<Filter> filters = System.Text.Json.JsonSerializer.Deserialize<List<Filter>>(filter);
return Ok();
}

Related

.NET Core [FromBody] annotation assigns default value instead of returning 400 error if value is missing

I have created a controller method similar to
[HttpPost]
public ActionResult<MyDTO> Post([FromBody] MyDTO myDTO)
{
// do something with myDTO...
Ok();
}
and MyDTO:
namespace MyNamespace.DTO
{
public class MyDTO
{
[Required]
public int someNumber { get; set; }
[Required]
public bool someBool { get; set; }
[Required]
public DateTimeOffset someDate { get; set; }
}
}
When I send a post with a JSON body that doesn't have either defined, instead of returning a 400 or an exception of some sort, it just assigns default (falsy) values to them.
When I defined a List of another DTO class, it did return a 400 code.
How can I make the API return an error when certain required values are not provided instead of just using default values?
You have to manually control it. There's no other way.
Try something like
[HttpPost]
public ActionResult<MyDTO> Post([FromBody] MyDTO myDTO)
{
if(YourComprobation(myDto)){
BadRequest();
}
Ok();
}
Found an answer here.
I just had to make all the fields nullable and have a
if (!ModelState.IsValid)
// return 400
in the controller.

Custom Property name for FromUrl Model

I have a model which is used to bind QueryString, that follows the naming conversation of c# but the QueryString is in a different naming conversation. How to provide a custom property name for model properties that are assigned vis FromUrl?
// Will NOT work
public class FormatDatabaseRequest
{
[JsonProperty("_type")]
public string Type { get; set; }
[JsonProperty(Name = "awef_flag")]
public string AwefFlag { get; set; }
}
// Controller.cs
[HttpPost]
public async Task<HttpResponseMessage> FormatDatabaseAsync([FromUri] FormatDatabaseRequest request) {}
// Sample URL (QueryString MUST be named _type and awef_flag)
// https://localhost:43521/myControllerName?_type=asdfa&awef_flag=asdf
If you want to get fields from URL like that, I recommend using [FromQuery] attribute, like so:
public async Task<HttpResponseMessage> Get([FromQuery] FormatDatabaseRequest data)
Then, such URL
https://localhost:43521/myControllerName?type=asdfa&awefflag=asdf
Will be parsed correctly to your object :)
json has nothing to do with query string. I don't understand why you don't like underscore properties, but you can hide them like this
public class FormatBaseRequest
{
public string _Type { get; set; }
public string Awef_flag{ get; set; }
}
public class FormatDatabaseRequest:FormatBaseRequest
{
public string Type
{
get { return _Type; }
set { _Type=value ; } //or leave just get
}
public string AwefFlag
{
get { return Awef_flag; }
set { Awef_flag=value ; } //or leave just get
}
}
you can use it for query string and for c#

Passing Complex Object to .netcore1.1 Webapi though Query string

I'm trying to pass a complex object though the query string but for some reason its not working. I have a complex object that looks like this:
public class QueryOptions
{
public QueryParameter[] Parameters = new QueryParameter[0];
}
And I've tried to sent it a few ways but nothing is working:
My webapi method looks like this:
[HttpGet]
[AllowAnonymous]
public async Task<TDTO[]> GetList([FromQuery] QueryOptions queryOptions)
{
return await this._service.GetList(queryOptions);
}
I've tried with and with out the FromQuery Attribute neither are working.
The url queries looks like this :
/api/users?Parameters[0].PropertyName=FirstName&Parameters[0].Value=GTitzy&Parameters[0].FilterCondition=0
I've also tried with the name of the object appened to the beginning. The request gets sent but the queryOptions always have no parameters.
How can I pass this complex object through the query string?
Assuming
public class QueryParameter {
public string PropertyName { get; set; }
public string Value { get; set; }
public string FilterCondition { get; set; }
}
You need to update your model to expose public properties for [FromQuery] to know what to bind to.
public class QueryOptions {
public QueryParameter[] Parameters { get; set; }
}
You should also consider reading Model Binding: Customize model binding behavior with attributes

Bind query parameters to a model in ASP.NET Core

I am trying to use model binding from query parameters to an object for searching.
My search object is
[DataContract]
public class Criteria
{
[DataMember(Name = "first_name")]
public string FirstName { get; set; }
}
My controller has the following action
[Route("users")]
public class UserController : Controller
{
[HttpGet("search")]
public IActionResult Search([FromQuery] Criteria criteria)
{
...
}
}
When I call the endpoint as follows .../users/search?first_name=dave the criteria property on the controller action is null.
However, I can call the endpoint not as snake case .../users/search?firstName=dave and the criteria property contains the property value. In this case Model Binding has worked but not when I use snake_case.
How can I use snake_case with Model Binding?
You need to add [FromQuery] attribute to the model properties individually
public class Criteria
{
[FromQuery(Name = "first_name")]
public string FirstName { get; set; }
}
Solution for .net core 2.1, 2.2, 3.0 and 3.1
Or without attributes you can do something like this which is cleaner I think (of course if the model properties are same as query parameters).
Meanwhile I use it in .net core 2.1, 2.2 and 3.0 preview & 3.1.
public async Task<IActionResult> Get([FromQuery]ReportQueryModel queryModel)
{
}
For anyone that got here from search engine like me:
To make it work on asp.net core 3.1+
public async Task<IActionResult> Get([FromQuery] RequestDto request);
public class RequestDto
{
[FromQuery(Name = "otherName")]
public string Name { get; set; }
}
Will read json property otherName into RequestDto.Name so basically you have to use FromQuery in 2 places.
Above answers are IMHO too complicated for such a simple thing already provided in asp.net framework.
In my case, I had an issue where my parameter name was option and in my class I also had the property called option so it was collapsing.
public class Content
{
public string Option { get; set; }
public int Page { get; set; }
}
public async Task<IActionResult> SendContent([FromQuery] Content option)
changed the parameter to something else:
public async Task<IActionResult> SendContent([FromQuery] Content contentOptions)
According to #Carl Thomas answer, here is the easier and the strongly typed way to have snake case FromQuery name:
CustomFromQuery
public class CustomFromQueryAttribute : FromQueryAttribute
{
public CustomFromQuery(string name)
{
Name = name.ToSnakeCase();
}
}
StringExtensions
public static class ObjectExtensions
{
public static string ToSnakeCase(this string o) => Regex.Replace(o, #"(\w)([A-Z])", "$1_$2").ToLower();
}
Usage
public class Criteria
{
[CustomFromQuery(nameof(FirstName))]
public string FirstName { get; set; }
}
If the
public async Task<IActionResult> Get([FromQuery] RequestDto request);
not work for anyone, you can try [FromRoute]
public async Task<IActionResult> Get([FromRoute] RequestDto request);.
In your dto you must keep the [FromQuery]
public class RequestDto
{
[FromQuery(Name = "otherName")]
public string Name { get; set; }
}

WebApi Newtonsoft.Json.JsonConvert.DeserializeObject <Class> Error

I am doing a WebApi Method in Visual Studio 2013 and I want to Deserialize a Class Type. My Class is like this
[JsonObject]
class JOTA
{
[JsonProperty("ProductId")]
public int ProductId { get; set; }
[JsonProperty("Name")]
public string Name { get; set; }
}
My call is like this.
public void ReturnListProd(JOTA PP)
{
JOTA product = Newtonsoft.Json.JsonConvert.DeserializeObject<JOTA>(PP);
}
I have a compile error
'Network.Json.Json.Converter[] has some invalid argument'
But, if a define an ArrayList
public void ReturnListProd(ArrayList PP)
{
JOTA product = Newtonsoft.Json.JsonConvert.DeserializeObject<JOTA>(PP[0].ToString());
}
I have no error. But in this case, it does not help on what I need.
What I am missing?
Thanks
If you want a JOTA object to become a string representation of itself (serialize it) then you should be using
string serialized = Newtonsoft.Json.JsonConvert.SerializeObject(PP);
If you want the string to become a JOTA object then you are using
JOTA product = Newtonsoft.Json.JsonConvert.DeserializeObject<JOTA>(serialized);
the problem is that you are trying to deserialize an object that is not serialized (already deserialized).
You don't need the attributes if the property names are not different.
public class JOTA
{
public int ProductId { get; set; }
public string Name { get; set; }
}
public void ReturnListProd(JOTA PP)
{
var product = PP; //not really needed you can just use PP directly
}
You only need to deserialize if you are receiving a json string. Because you are using WebAPI I would suggest changing your API endpoint to a proper REST endpoint. Example:
[HttpPost, Route("api/products/add")]
public IHttpActionResult ReturnListProd([FromBody]JOTA PP)
{
try
{
//do something with passed in data
var name = PP.Name;
//always at minimum return a status code
return Ok();
}
catch
{
//returns 500
return InternalServerError();
}
}
Then change your ajax url from:
url: "yourController/ReturnListProd"
to:
url: "/api/products/add"

Categories