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#
Related
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();
}
I am using Postman to make a Get call. In the URL I have optional parameters with underscore in it. I want to assign those values to a class by using DataContract but I can't-do it. If I read them separately then there is no issue.
This is new to me so exploring what is the best approach to do it. Found some links where the suggestion is to go for an individual parameter but want to make sure I am not missing anything here.
Call: http://{{url}}/Host?host_name=Test&host_zipcode=123&host_id=123
Working: I can read these parameter values if I read them as an individual parameter.
[HttpGet]
[Route("api/Host")]
public async Task<HostResponse> GetHostInfo([FromUri (Name = "host_name")] string hostName, [FromUri (Name = "host_zipcode")] string hostZipCode, [FromUri(Name = "host_id")] string hostId)
{
}
Not Working: When I try to use class by using DataContract, I can't read it.
[HttpGet]
[Route("api/Host")]
public async Task<HostResponse> GetHostInfo([FromUri] HostInfo hostInfo)
{
}
[DataContract]
public class HostInfo
{
[DataMember(Name = "host_name")]
public string HostName { get; set; }
[DataMember(Name = "host_zipcode")]
public string HostZipCode { get; set; }
[DataMember(Name = "host_id")]
public string HostId { get; set; }
}
I also tried:
public class DeliveryManagerStatus
{
[JsonProperty(PropertyName = "country")]
public string Country { get; set; }
[JsonProperty(PropertyName = "delivery_provider")]
public string DeliveryProvider { get; set; }
[JsonProperty(PropertyName = "job_id")]
public string JobId { get; set; }
}
How can I assign these properties to a class?
You can use IModelBinder (details) implementation to parse it. Here is a DataMember based example (takes key names from DataMember attribute):
public class DataMemberBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var props = bindingContext.ModelType.GetProperties();
var result = Activator.CreateInstance(bindingContext.ModelType);
foreach (var property in props)
{
try
{
var attributes = property.GetCustomAttributes(typeof(DataMemberAttribute), true);
var key = attributes.Length > 0
? ((DataMemberAttribute)attributes[0]).Name
: property.Name;
if (bindingContext.ValueProvider.ContainsPrefix(key))
{
var value = bindingContext.ValueProvider.GetValue(key).ConvertTo(property.PropertyType);
property.SetValue(result, value);
}
}
catch
{
// log that property can't be set or throw an exception
}
}
bindingContext.Model = result;
return true;
}
}
and usage
public async Task<HostResponse> GetHostInfo([FromUri(BinderType = typeof(DataMemberBinder))] HostInfo hostInfo)
In quick search I wasn't able to find any AttributeBased binder try and share if you will find it
Using your HostInfo class change your call to:
http://{{url}}/Host?hostInfo.host_name=Test&hostInfo.host_zipcode=123&hostInfo.host_id=123.
I would recommend changing your route to accept the complex type in the body if possible due to the limitations in query string length.
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
I have an ASP.NET Web Api 2 project with several response models. In an attempt to create a smaller payload I am offering the user the option to collapse entities to just a id and a href link, which I would like to generate automatically. I would like all my main resource response models to inherit from a base response model that has just the href and id. If I have a resource Foo, this looks something like this:
public class ResourceResponseModel
{
public string Href { get; private set; }
public string Id { get; private set; }
protected ResourceResponseModel(string id)
{
Id = id;
}
}
public class FooModel : ResourceResponseModel
{
public string Name { get; private set; }
private ExampleModel (string id, string name)
: base(id)
{
Name = name;
}
internal static FooModel From(Foo foo)
{
return new FooModel(
foo.Id,
foo.Name
);
}
}
When my controller is called, this model is serialized with Microsoft.AspNet.Mvc.Json(object data)
This seems to work great, except when I look at the response I end up with, it puts the base class attributes at the end:
{
"name": "Foo 1",
"href": "api/abcdefg",
"id": "abcdefg"
}
Is there an easy way to get the base attributes to appear before the resource attributes?
You can solve this by setting the JsonProperty attribute on you properties and pass in Order.
public class ResourceResponseModel
{
[JsonProperty(Order = -2)]
public string Href { get; private set; }
[JsonProperty(Order = -2)]
public string Id { get; private set; }
protected ResourceResponseModel(string id)
{
Id = id;
}
}
Order seems to default to zero and then get sorted from low to high when serialized. Documentation can be found here.
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"