Comments (36)
Just refreshing myself with Nancy modelbinding and the very basics of it are
- it creates an instance of the type binding to
- it deserializes the body and applies that
- it then effectively loops over querystring/routedata and matches a property name with a route/qs name
- finds out if it can assign that value eg the instance doesnt have a value already
- tries to assign the instance property to a value
- it determines how to convert the value to the type by ITypeProvider eg/datetime, numeric with CanHandle/Handle methods
from carter.
@abazanov The retrieval for query variables isn't part of routeData, it's part of the request object.
request.Query.TryGetValue("q", out var qVariableStr)
Where qVariableStr
is a variable that is the output of the method. The result is always a string, it could be the default value of a string if the variable isn't defined on the URL so you have to check for that and then parse the string value to get your int value.
from carter.
Go for it!
from carter.
Yeah I was thinking about that the other day. I'm not sure how I feel about it. I never used it in Nancy and not sure of the usage stats on it. I guess we'll leave the issue open and see what people want.
from carter.
My use case is binding requests to query and command objects, which include the route data. For example, route POST "{bundleId:int}/entries/{entryId:int}"
could be bound to
public class AddEntryToBundleCommand
{
public int BundleId { get; set; }
public int EntryId { get; set; }
}
I find this extremely convenient and I'm actually already using this approach on top of Botwin with some custom code. I'm currently spiking a microservices library for internal use that would allow us to create new Botwin-based services simply by defining routes, actions and handlers for the actions. Just as an example, this is what my "ItemsService" route config looks like:
public class Routes : RoutesBase
{
public Routes()
{
Get("/items", Handle<ListItemsQuery>);
Post("/items", Handle<CreateItemCommand>);
Get("/items/{id:int}", Handle<ReadItemQuery>);
Put("/items", Handle<UpdateItemCommand>);
Delete("/items/{id:int}", Handle<DeleteItemCommand>);
}
}
This requires binding of route data, but as I said, I have implemented it in my library code. So if you and other people don't feel the need for binding of route data, no problem.
By the way, I noticed that validation isn't available separate from model binding. That is, there is no Validate(model)
method available. If route data model binding is not implemented, refactoring of model validation to its own method would simplify things on my end. And before you ask, yes, I could do a PR for the validation method refactoring if you want.
from carter.
I think if Botwin were to bind to route data, it shouldn't be the same method as binding from the Body, I've seen this confuse people in the past, and it introduces a plethora of questions: body + querystring contain the same property, which should take precedence is the obvious example).
I'm not completely against the idea, especially when it comes to complex query strings (reporting examples are ripe for this), but for day-to-day use, it shouldn't be necessary, so would be reluctant to have it there by default.
from carter.
I think the Validate separate method is a good idea. Would you like to submit a PR for that, should be a case of copying and pasting half the BindAndValidate method and putting it in a new method.
For model binding route data and querystring I can see the merit I think however the code could be quite nasty. I can take a look and maybe others will jump and also feel free to take a look but it's not my priority tbh
from carter.
Actually you can already get the route data out like so
this.Get("/actors/{id:int}", async (req, res, routeData) =>
{
var person = actorProvider.Get(routeData.As<int>("id"));
await res.Negotiate(person);
});
from carter.
Sure, I'll make a separate issue for the validation stuff and a PR for it tomorrow, if not tonight. I did know how to get the route data but as I want it bound in the model class automatically, I need to iterate available values and bind them to properties or fields using reflection.
from carter.
from carter.
Looking at MVC, it seems to bind querystring values to types too so might want to take a look at what they do for that
from carter.
At the moment I think maybe having a BindRouteData<Foo>
separate method might be better but that might change
from carter.
I created #77 to track the bind and validate issue
from carter.
Thanks! I'll have a look at the PR soon.
Regarding your comment about reflection, I'm trying things out for the base library I mentioned and currently doing this to naively bind route data to arbitrary model classes:
var actionType = typeof(T);
var action = request.Bind<T>() ?? Activator.CreateInstance<T>();
foreach (var value in routeData.Values)
{
var property = actionType.GetProperty(
value.Key,
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var typeConverter = TypeDescriptor.GetConverter(property.PropertyType);
property.SetValue(action, typeConverter.ConvertFromString(value.Value.ToString()));
}
If you can suggest something better and/or more robust, I wouldn't mind π.
from carter.
Oh, and a separate BindRouteData<Foo>
would be great for my purposes!
from carter.
That's pretty much all you can do I think π but I'm sure we can improve it
from carter.
@jchannon regarding 4, the way you explained means that the body would always win correct? I always get the body data and override it with route data.
Maybe making the Bind priority configurable would please everyone.
from carter.
Any update to this? I am looking for QS model binding and was deciding on if I should start working on something or not.
from carter.
from carter.
Ok, I finished a version last night that supports basic models (flat with no collections) from the Query String (not the URL/Route though). If all goes well I should be able to get collections working today or this weekend. Question, do we think we need json notation support for more complex objects right now?
from carter.
Not sure where JSON comes in for querystring/route data?
from carter.
It seems that a lot of newer API use the [] notation to work with nested objects in a query string
Ex:
/someroute/?name=sample&price[gte]=10&price[lte]=100
would parse to
public class QueryStringModel
{
public string Name {get;set;}
public PriceModel Price {get;set;}
}
public class PriceModel
{
public int Gte {get;set;}
public int Lte {get;set;}
}
Here is a quick example library from node: https://canjs.com/doc/can-param.html
from carter.
Personally I've never used it. I think get basic binding working then we can look at the more complicated approach however I'd like it to work off a defined standard if there is one for this type of querystring format
from carter.
Ok, simple solution is as follows :)
public static T BindQueryString<T>(this HttpRequest request, Newtonsoft.Json.JsonSerializer jsonSerializer = null)
{
var dct = request.Query.ToDictionary(key => key.Key, value => string.Join(",", value.Value.ToArray()));
JObject jObject;
if (jsonSerializer != null)
{
jObject = Newtonsoft.Json.Linq.JObject.FromObject(dct, jsonSerializer);
}
else
{
jObject = Newtonsoft.Json.Linq.JObject.FromObject(dct);
}
var result = jObject.ToObject<T>();
return result;
}
If we want to support array values we also will need a custom JsonConverter which is pretty easy to create.
from carter.
from carter.
Yep,I have a fork with the changes. As for the serializer, I am ok using the class wide one if we can remove the readonly as I find that I need to set it with custom settings a lot. Are you ok with that?
And for the arrays, I can do one of two things -- add a reference to another library that already has the converter or copy the code (its my code so I am ok with either).
Thoughts?
from carter.
I'd like to keep it class wide but then in time add options for serialization into CarterOptions.
I'd like to take a copy of the converter into Carter if that's the only option :)
from carter.
It would be awesome to support enums in the model binding.
Something like:
if (propertyType.IsEnum)
{
if (Enum.IsDefined(propertyType, int.Parse(val.Value[0])))
{
return Enum.Parse(propertyType, val.Value[0], false);
}
return null;
}
though that will only work for enums defined as int
from carter.
Hi Guys,
Sorry to barge in. I have a find type URL that looks like so companies/find?q=123
. Are we saying that Carter does not support query string type variables?
I know I can use it like so companies/find/{q}
but what if q
is not provided, meaning "just give me everything"? It does not hit the correct rout.
I hope somebody has some input about this.
EDIT
Seems, I can use companies/find/q={q}
which works, but again, if q
is empty then it does not match the route. Trying to see if q
can be made nullable or optional.
from carter.
@abazanov you can read the query string parameters of the request, the route will be companies/find
, and we have extension methods to help read the query string parameters. I don't have an editor to hand right now, but explore context.Request
from carter.
@snorith @JoeStead thank you for coming back to me. For some reason just skips over my route when I use companies/find
when I use companies/find/test
it works. Strange.
Again, thanks guys. Great help. I think I am close now.
from carter.
from carter.
Thanks guys. I appreciate it. I got it working now. And the optional value is a nice thing. Thanks @jchannon
from carter.
To read querystring values you can use Carter's extension methods to do var q = context.Request.Query.As("q", "my_optional_default_value");
This is worthy of being documented in the readme.
from carter.
I think it's important feature for people who'd like to port code from controllers to carter easily.
With MVC controllers you can bind to your custom classes easily from many different sources.
e.g. I used https://github.com/Biarity/Sieve and it provides SieveModel
class which you should bind with [FromQuery]
. How could I use SieveModel
with Carter?
The bind API is very limited right now as it only supports binding from body.
from carter.
I believe this is now kind of available in .NET6 and Carter 6
app.MapGet("/bind/{id:int}", (HttpContext context, int id) => { });
from carter.
Related Issues (20)
- [Question] Is it possible to do property injection on a CarterModulse version 7 and autofac HOT 1
- Conditionaly Load UnLoad Module HOT 1
- How to configure my filters HOT 1
- [QUESTION] Any sample code using .WithOpenApi HOT 1
- Add support for registering an endpoint filter using an endpoint filter factory HOT 2
- [Question] The future of Carter HOT 1
- Endpoints Not Visible in EndpointsApiExplorer HOT 4
- suggest for nested work HOT 1
- Error On Register IN DI HOT 5
- Removed support to apply endpoint conventions globally HOT 2
- Can't find testhost.deps.json HOT 1
- Unable to change the field of the response data body to uppercase HOT 2
- Minimal AP Versioning HOT 2
- Open Api Summary is not drawing on Swagger UI HOT 3
- Trying to get swagger working with my project. HOT 4
- carter is missing NuGet package README file HOT 1
- Make BindFile and BindFiles extension methods public HOT 1
- Consider making ICarterModule.AddRoutes a static abstract method HOT 8
- Is it possible to apply a global filter with Carter that applies to all endpoints of all Carter modules? HOT 2
- Please exclude fluent validators registration by default HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from carter.