Web Api: Pass an empty parameter in a query string - asp.net-mvc

I'm looking for a proper way to handle empty parameters in a query string. Web Api does not accept query strings as "?id=1&category=", which seems reasonable, but I need to handle this case.
The quick and dirty solution is to use a custom value which will be interpreted on the server side (say "(empty)" for example) but I'm not satisfied with it...
Any suggestion ?
Thanks.

One way I have dealt with this in the past is to make a class to hold your paramaters and then use to ModelBinder attribute to bind your query parameters to the class properties.
So your class would look something like this:
public class QueryParams
{
public string Category {get; set;}
public int Id {get; set;}
}
And the method in your api controller would look like this:
public objectToReturn Get([ModelBinder] QueryParams)
{
//code here
}
This way if one of the parameters in the query string has no value it will simply be ignored.

You can use this attribute to achieve what you want.
[DisplayFormat(ConvertEmptyStringToNull = false)]
If "category" is missing then it will be null.
Otherwise, if "category=" or "category= " then will be an empty string or whitespace.

Related

Swagger Query Parameter name prefix

I have a Request class as follows:
public class GetCareTypesRequest
{
/// <summary>Tpa Partition</summary>
[Required]
public string TpaPartition { get; set; }
}
I am passing entire object as a single query parameter instead of individual in Controller method as follows:
public async Task<IHttpActionResult> Get([FromUri] GetCareTypesRequest request) {}
When I go to Swagger page, I am seeing "request" as a prefix for TpaPartition parameter:
Even Request URI is coming as: http://localhost:52354/api/CareTypes?request.tpaPartition=0000010000010001
I am expecting it to be like: http://localhost:52354/api/CareTypes?tpaPartition=0000010000010001 and I still want to send entire object as a single query parameter.
What should I do to achieve this? I came across few articles which suggest to create a custom ModelBinder but any easy way would be very helpful.

Customize swagger schema for a nested type using swashbuckle

I have an api (ASP.NET Core 3.0) that allows users to search a document database using various query parameters to filter and order the results. One of the parameters is an Order parameter that defines how to order the results. The acceptable values are limited to the values of an enum.
I now need to add more behavior to the enum, so I re-wrote it as an Enumeration Class so that I can add object-oriented behavior to it. The problem I now have is that Swashbuckle flattens out the properties of the Enumeration rather than leaving it as a single parameter. Here are my enumeration and parameter classes:
// Enumeration
public class DocSearchOrder : Enumeration {
public static readonly DocSearchOrder DocType = new DocSearchOrder(2, nameof(DocType));
public static readonly DocSearchOrder DocTypeDesc = new DocSearchOrder(3, nameof(DocTypeDesc));
public static readonly DocSearchOrder DocDate = new DocSearchOrder(4, nameof(DocDate));
public static readonly DocSearchOrder DocDateDesc = new DocSearchOrder(5, nameof(DocDateDesc));
public DocSearchOrder(int value, string name) : base(value, name) {
}
}
// Search Parameters
public class DocSearchParameters {
public DocSearchOrder? Order { get; set; }
// Lots of other search parameters
}
Then the method that uses it:
public async Task<IActionResult> GetAsync([FromQuery] DocSearchParameters searchParams) {
// Do the search
}
Swashbuckle flattens searchParams.Order into DocSearchOrder.Id and DocSearchOrder.Name.
The behavior I want to achieve is for my Swagger UI to continue to show a dropdown of the available values (DocSearchOrder.Name) that a user can select from with the parameter named "Order". You then pass one of those string values and a custom model binder converts the string to the Enumeration class instance.
I've tried writing a custom IOperationFilter but that only seems to work for modifying the schema of types passed to the GetAsync method, I can't intercept the schema generation for searchParams.Order. I thought what I'd be able to do is somehow intercept the schema generation for any property that is an Enumeration and generate an enum schema for it instead of an object schema, but I don't know how to intercept it.
So my question is: is there a way to customize the schema generation for a nested type? Or is there another way to go about this and I'm doing it all wrong? :)
How about a regular enum:
public enum DocSearchOrder
{
DocType = 2,
DocTypeDesc = 3,
DocDate = 4,
DocDateDesc = 5
}
I think that would that be easier, and there should not give you much trouble
Here is an example from one of mine:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=TestEnum#/TestEnum/TestEnum_Get

Getting timestamp() value using C# client

Just a quick one, I have a node that gets a value set using the value of timestamp(), when I query for the node using
public List<Thing> ListThings()
{
return client.Cypher
.Match("(thing:Thing)")
.Return<Thing>("thing").Results.ToList();
}
I have a class called Thing that looks like this:
public class Thing{
public string name {get; set;}
public int id {get;set;}
public DateTimeOffset timestamp{get;set}
}
We create the 'thing' ahead of time by using:
Create(thing:Thing{id:6,name:'thing1', timestamp:timestamp()}) return thing
I get every value back apart from timestamp when calling from my script, which is a little annoying, any ideas? I get all the values back using the query in Neo4j browser, so I was wondering if I was actually doing something wrong?
Thanks
Tatham is right, the timestamp() returns a long that's not convertible into DateTimeOffset or DateTime. One of the problems is that the returned value is Milliseconds since 1/1/1970, so you need to calculate that, unfortunately, JSON.Net can't do that automagically.
If you can change your Thing class you could have a property that isn't serialized but is calculated by the Timestamp property, something like this:
public class Thing
{
private static DateTime _epoch = new DateTime(1970, 1, 1);
public string id { get; set; }
public string name { get; set; }
private long _ts;
public long timestamp
{
get { return _ts; }
set
{
_ts = value;
TimestampAsDateTime = _epoch.AddMilliseconds(_ts);
}
}
[JsonIgnore]
public DateTime TimestampAsDateTime { get; set; }
}
The TimestampAsDateTime property would only ever be available in your code.
If you go to the Neo4j console and just run RETURN timestamp(), you'll see the result is a number like 1440468541547. That can't be de-serialized to a DateTimeOffset, especially as there's no offset component. You'll need to use a different type in C#: probably long, or maybe DateTime.
Thank you for your excellent answers, you solved the problem, but I am using a slightly overlooked (on my part) solution.
As the data type for timestamp() is long I am just going to return that from the service. We have the ability to handle that perfectly using the clients in-built Date object.
It was a mistake on my part, I wrongly assumed that timestamp would have been DateTime, but then I got an error about it and was advised to use DateTimeOffset, which I did, never really considered long as an option.
Please remember I am from a MSSQL background and some things, although easier to do, take a different perspective to grasp.
Thanks to you both though, appreciate the time taken to assist.

How can I exclude some public properties from being serialized into a JsonResult?

I have a custom viewmodel which serialized using a JsonResult. The ViewModel has some properties which have to be public, but at the same time these properties should not be visible in the resulting Json output.
I've already tried using the [NonSerialized] attribute, but that did not seem to have any effect.
Is there any simple way to do this? Or would I have to code my own result type (in which case I probably won't bother)?
You can put a [ScriptIgnore] attribute on the members that shouldn't be serialized. See ScriptIgnoreAttribute Class in MSDN for an example.
Just create an interface to return instead of a class.
public interface IMyViewModel {
string MyPublicProperty { get; set; }
}
Then create a class that inherits the interface
public class MyViewModel : IMyViewModel {
public string MyPublicProperty { get; set; }
public string MyNotSoPublicProperty { get; set; }
}
And return the interface, not the class, in the Controller Action
public JsonResult MyJson(){
IMyViewModel model = new MyViewModel();
return Json(model);
}
And the resulting JSON will be
{
'MyPublicProperty': ''
}
One of the challenges in client-side scripting is, that if you're changing your classes, you have no idea whether you're destroying the client-side implementation or not. If you use interface types in your JSON, you understand that if you change the interface, you're doing something that potentially may be killing the client side implementation. And it also saves you from double-checking the client side in vain if you're changing something that is NOT in the inteface (thus not being serialized).
Also, many times, your ViewModels might have large collections or complex types in them that you don't necessarily want to output to the client. These might take a long time to serialize or expose information that simply does not belong into the client code. Using interfaces will make it more transparent to know what is being in the output.
Also, using attributes such as [ScriptIgnore] on a property only applies to a specific scenario (JavaScript Serialization) forcing you to face the exact same problem if you're later serializing to XML for example. This would unnecessarily litter your viewmodels with tons of attributes. How many of them you really want in there? Using intefaces applies anywhere and no viewmodel needs to be littered with extra attributes.
Have a look at JSON.NET from James Newton-King. It'll do what you're looking for.
Extend the JavaScriptConverter class to not include properties with the NonSerializedAttribute. Then you can create a custom ActionResult that uses your JavaScriptConverter to serialize the object.
This creates a solid and testable class without having to (re)generate wrapper classes or using anonymous objects.
You can create a wrapper class that exposes only those properties that you want in the JsonResult. In the example below, Cow has 2 properties - "Leg" and "Moo". Suppose you want to only expose "Leg" as a property. Then
Dim cw as CowWrapper = New CowWrapper(c)
will return a wrapper class that only exposes "Leg". This is also useful for things like DataGridView if you only want to display some subset of the properties.
Public Class Cow
Public ReadOnly Property Leg() as String
get
return "leg"
end get
end Property
Public ReadOnly Property Moo() as String
get
return "moo"
end get
end Property
end class
Public Class CowWrapper
Private m_cow as Cow = Nothing
Public Sub New(ByVal cow as Cow)
m_cow = cow
end Sub
m_cow = cow
Public ReadOnly Property Leg() as String
get
return m_cow.Leg()
end get
end Property
end Class
Not exactly the answer you're looking for, but you can cheat Json() using the following code and anonymous classes:
MyModel model = ...;
return Json(new MyModel {model.Prop1, model.Prop2});
I needed the answer to this for ASP.NET Core 6.x and couldn't find it.
I finally found the answer and it is :
[System.Text.Json.Serialization.JsonIgnore]
Here's an example in a class
class Sample{
// Item will not be serialized
[System.Text.Json.Serialization.JsonIgnore]
String Item{get;set;}
// Count will be serialized
int Count{get;set;}
}

Default Values in LINQ Modelling

To put it in basic form, my database table doesn't allow nulls for varchars, it must have blanks. My model doesn't allow nulls so it won't insert a record if I leave form fields empty. If an empty form field appears I want a default value of blank to be used instead. I've tried, for example, the following without any luck:
[Column]
[DisplayName("WMD Company")]
[DefaultValue(" ")]
public string WMDCompany { get; set; }
So instead, in my controller action I have to do a check like the following:
if(myModel.WMDCompany == null) myModel.WMDCompany = " ";
Which is plain nasty to me. Is there any way of getting [DefaultValue(" ")] to work?
Cheers
What about something like this:
private string wmdCompany;
public string WMDCompany
{
get
{
if (this.wmdCompany == null)
{
return string.Empty;
}
return this.wmdCompany;
}
set
{
this.wmdCompany = value;
}
}
The DefaultValue attribute is not used. LINQ to SQL has not support for DB defaults unfortunately. That property is intended for use in API extension if I remember, but I don't know of any that use it.
Two approaches to get around this you could use.
First update your data layer, by appropriately controlling the property, and setting it to null. Use a partial class to extend your data class, and implement the OnCreated() partial method, and in this set the value to String.Empty.
partial void OnCreated()
{
MyProp = String.Empty;
}
Secondly, you could change your DBML representation to allow nulls, but in your database, use a trigger to convert NULLs to empty strings.
I'd go with the first approach myself - assuming you can't just use NULLs as suggested by Adrian
Inserting spaces as a placeholder for NULL seems like a very obscure method to me. Why don't you just change your table design to allow NULL values?

Resources