Giter VIP home page Giter VIP logo

Comments (44)

canton7 avatar canton7 commented on September 17, 2024

Yep, write an IRequestQueryParamSerializer.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Ok, so how can I do that without returning a key that is used? to assembly it AFTER the custom serializer is called? Basically I need to return a string, not a KeyPair

from restease.

canton7 avatar canton7 commented on September 17, 2024

Which bits of the docs is unclear (I don't mean to be rude, but maybe the docs need improving here). Your serializer returns an IEnumerable<KeyValuePair<string, string>>, so in your case the serializer would return

new[]
{
    new KeyValuePair<string, string>("p", "test"),
    new KeyValuePair<string, string>("i", "1"),
}

How it gets that is up to you - I'd put an interface on Category which returns this or something like it, and check for that in your serializer.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

That works in only a simple use case :) I have about 30 different objects and about 100 different APIs all with different parameters. I basically created a custom extension method that will take any object and serialize it into a valid QueryString based on some attributes. I'd like to get RestEase to leverage my query string serialization as it is a lot more targeted at the 3rd party API I am using (they support things like and/or searching on multiple values for a property and date range searches).

from restease.

canton7 avatar canton7 commented on September 17, 2024

... or you could use the [QueryMap] stuff, and implement IDictionary<string, string> on your Category class.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

[QueryMap] might work, it just removes the "strong typed" nature of what I have and would require a lot of rework :-/ as I would need to return the serialization logic I have to return a IDictionary instead of a valid QueryString.

from restease.

canton7 avatar canton7 commented on September 17, 2024

Or, write a custom serializer which calls ToString(), parses it as a query string, and returns an IEnumerable<KeyValuePair<string, string>> as a result.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

I just realized that will not work either. Most of the APIs I work with support things like this

or(param1=testing|nottesting)

This is part of why I wrote my own serialization logic. Any chance you could consider adding support for allowing the serializer interface to return a single string (not a keypair) as an alternative?

from restease.

canton7 avatar canton7 commented on September 17, 2024

I'm afraid I'm not sure what you mean by or(param=testing|nottesting). I'm also not sure why this means that you can't deconstruct the query string returned by your pre-existing ToString() implementation, and pass that rather than the raw string.

Bear in mind that I can't break backwards compatibility, which means that I can't add methods to interfaces, or change the signature of existing methods.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

its an odd format, I know. What about adding support for another interface and then internally you can detect which one is implemented (the one with a Dictionary or the one with a string result)? That is usually how I handle enhancements when I need to

from restease.

canton7 avatar canton7 commented on September 17, 2024

I'm still not sure what you mean by or(param=testing|nottesting). You mean that goes directly into the query string part if the url? Doesn't that violate the spec?

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Either that or what about adding a flag to the QueryAttribute like "ignoreName" that would tell the builder to not use the parameter name and instead assume the caller took care of formatting it

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

I typed it in wrong :)

?param1=or(something|somethingelse)

It looks a bit like the OData Filter spec
http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions-complete.html#_Toc453752358

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Back to your [QueryMap] idea, are you saying that I should implement the IDictionary interface on all of my objects? I'll need to think about that as it might seriously change the default serialization that I leverage elsewhere for these classes (standard json serialization).

from restease.

canton7 avatar canton7 commented on September 17, 2024

OK, but I still don't understand why you can't write a custom serializer which takes the ?param1=or(something|somethingelse), parses it to a new KeyValuePair<string, string>("param1", "or(something|somethingelse)"), and returns that?

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

mostly because it really is a lot of extra work and processing that ideally isn't needed. If you think about it, there is code that is creating the query string, code that is splitting it, and code that is creating it again :) seems a little redundant and not ideal from a performance standpoint (string processing is not the best for high performance apps).

I am curious -- is there a reason the library needs total control on creating the querystring?

from restease.

canton7 avatar canton7 commented on September 17, 2024

Yeah, but it's in the context of making a http request, which is thousands of times more expensive.

I would accept a pull request which added a [RawQueryString] attribute, which could be applied to a string parameter and which could cause it to be appended to the query string, but I'm trying to come up with a solution which will help you now, rather than in a few weeks.

RestEase consists of two layers: the first one constructs a RequestInfo object, which contains all the information from the method declaration and invocation, and the Requester, which turns that into a HttpRequestMessage.

The interface between these works with unserialized query strings (i.e. key/value pairs), because it's the responsibility of the Requester to turn these into the serialized query string.

I try and make some basic assumptions about how you want to do things to make life easy, but provide the tools to write your own customizations if needed. I don't want to add features to support no end of niche use-cases, where a custom serializer/formatter/requester will suffice. In this case, a custom serializer meets your use-case. That's why I'm pushing back a bit.

For the [QueryMap] option, you could have a method on your class which returns the IDictionary<string, string>....

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

So what I have done right now is I rewrote my serialization extension method to return a IDictionary<string, string> and then I am using the [QueryMap] to pass it in for now. Its not as clean as I would like -- but it works :)

The trouble with the custom serialize as it stands it is forces a name/value pair -- ideally the framework would provide two interfaces, one that supports the KeyValue pair (this would be for the simple cases) and one that would provide support for a valid string to returned (up to the developer to make sure the string meets there needs and is valid according to the API they are calling).

All that aside, I think the idea of a RawQueryString attribute is a very good alternative and I could potentially look at implementing that maybe tomorrow. The only issue I am running into is I cannot find the SDK version that your solution is using (MSFT has released a newer build and it doesn't work with the old version). So I need to either keep looking for the older one or figure out how to upgrade the solution (cannot seem to find what it is referencing in the older version though)

Also I completely understand the desire (and push back) to shy away from the constant "little tweaks" that end up resulting in a very difficult to maintain code base -- I do the same for a number of frameworks I maintain :)

Thanks for all the help!

from restease.

canton7 avatar canton7 commented on September 17, 2024

So what I have done right now is I rewrote my serialization extension method to return a IDictionary and then I am using the [QueryMap] to pass it in for now. Its not as clean as I would like -- but it works :)

You could write a custom serializer to call that method, if that would make it cleaner. That was one of my first suggestions.

The trouble with the custom serialize as it stands it is forces a name/value pair -- ideally the framework would provide two interfaces, one that supports the KeyValue pair (this would be for the simple cases) and one that would provide support for a valid string to returned (up to the developer to make sure the string meets there needs and is valid according to the API they are calling).

I'm not sure why you would want to take responsibility to serializing query parameters yourself, rather than relying on the framework: I never got a straight answer out of you (other than legacy reasons, perhaps?). One of the points of the framework is to take responsibility for properly encoding the query parameters off your plate.

I'm happy to have some sort of [RawQueryString] to cover those rare cases where the developer does want to do this for some obscure reason, but I don't want to bake support into too many parts of the framework (which increases the maintenance, documentation, testing, and cognitive burden).

from restease.

canton7 avatar canton7 commented on September 17, 2024

The only issue I am running into is I cannot find the SDK version that your solution is using (MSFT has released a newer build and it doesn't work with the old version). So I need to either keep looking for the older one or figure out how to upgrade the solution (cannot seem to find what it is referencing in the older version though)

What exactly is your problem (what are the symptoms)? I've just double-checked that the ".NET Core 1.0.0 - VS 2015 Tooling Preview 2" download installs the same SDK version as RestEase uses.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

In regards to the solution, when I try to load it, I get the following error

Image

And when I look at what was installed when I downloaded the latest version of the SKD
Image

As you can see, they do not match. The solution is looking for build 003121 and the latest download is 003131 (10 builds newer).

from restease.

canton7 avatar canton7 commented on September 17, 2024

Weird, the installer from here installs version 1.0.0.3121. (NOT 3131, sorry typo).

Anyway, the version of the SDK to use is in global.json (as that dialog says). You can either bump the version, or clear it entirely.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

In regards to your "straight answer" request, is because of complex scenarios that a framework may not handle naturally. I really love how easy RestEase makes things in terms of handling the standard "framework" of creating an api interface. Where I run into issues is when either the server doesn't support standards (this happens a lot), or when some custom conditional logic is needed.

In my case I run into things like

  • DTO object property names to not match API expectation (legacy code issue). I handle this with a custom attribute that provides the correct name (like JsonProperty)
  • Custom filter criteria (and/or/greater then/less than/between) type queries that require specific formatting
  • Ability to include some parameters without values (ex: /?param&parm1)
  • Ability to include/exclude parameters based on Nullable Types/values
  • Need to serialize to straight name/value pairs rather than nested json on the querystring (this seems to be the default for the library and I have not seen any api that can work with this yet)

All in all, yes you are correct, most if not all of these can be handled by a QueryMap -- it just feels a bit messy as it requires a custom method to be created that takes and object and creates a dictionary and then passes that dictionary to the framework (note, I found out that you take care of UrlEncoding which caused some issues with my code as I had some legacy code that was doing this already and I didn't know you took care of it). This dictionary idea leaves a lot of room for other developers to come behind and just add random properties which will then break the strong type map I have right now between my DTO and the API (this is done to help with stability and provide a very consistent behavior to other users of my wrapper)

Like I said, ideally I could just create a custom serializer and pass it in to override the default in its entirety, or maybe we extend the RestEase framework to have another querystring serializer that uses a name/value pair with some enhancements (like using JsonProperty/XmlElement to get the name), and supports nullable types.

Does that help?

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Ahh, had not looked at the globals file yet -- thanks :)

from restease.

canton7 avatar canton7 commented on September 17, 2024

Cool, thanks for the detailed explanation. I don't see anything in there which mandates the passing of a raw query string, rather than key/values pairs, however.

The custom serializers were intended to cover that use-case: get an instance of the class, make decisions on what parameters to return, and return them. This would let you keep your type-safety. The QueryMap is a lightweight alternative to this. A serializer which looks at name/values pairs would also be possible - I haven't written one, because it quickly becomes a rabbit hole (aliassing members, ignoring members, etc etc).

In terms of handling param&param1=value, I could see a use for allowing a null query parameter name, either in the method signature or returned from serializers (it does already on .net framework, but not .net core).

from restease.

canton7 avatar canton7 commented on September 17, 2024

mostly because it really is a lot of extra work and processing that ideally isn't needed. If you think about it, there is code that is creating the query string, code that is splitting it, and code that is creating it again :)

I wasn't referring to that here - I was referring to a general strategy for custom serialization of objects. The fact that you're already serializing them to string is legacy - if you were writing it from scratch now you'd serialize to an IEnumerable<KeyValuePair<string, string>>. That would be in line with what RestEase would want, and would work perfectly, I think?

So there's nothing you want to do which RestEase doesn't support - it's just that you want to go about it in a way which isn't in line with how RestEase works.

seems a little redundant and not ideal from a performance standpoint (string processing is not the best for high performance apps).

If you're worried about the performance implications of parsing a query string, then in all honesty you shouldn't be using RestEase. The cost of RestEase compared to pure HttpClient alone is small, but will far, far exceed the cost of parsing a query string.

I am curious -- is there a reason the library needs total control on creating the querystring?

It's the responsibility of the IRequester to construct it from parts. If you want to customise this, use your own IRequester: then you can take responsibility for creating it from parts.

That doesn't change the fact that the interface into the IRequester deals with key/value pairs. This is so that the IRequester can take responsibility to serializing it to a string: if it accepted a ready-made string - constructed by an earlier part of the process - then it wouldn't be able to take control of serializing it. It could take both a set of key/value pairs and a ready-made string, and combine them, but that hasn't been done because it is more complexity (more work to implement, document, it's confusing - when does it use a ready-made string, and when does it use key/value pairs ) and there's never been a need for it. It

from restease.

canton7 avatar canton7 commented on September 17, 2024

Just to make absolutely sure we're on the same page, my suggestion (legacy issues aside) would be:

public interface IQueryParamSerializable
{
    IEnumerable<KeyValuePair<string, string>> Serialize();
}

public class Category : IQueryParamSerializable
{
    public string P { get; set; }
    public int I { get; set; }

    public IEnumerable<KeyValuePair<string, string>> Serialize()
    {
        // Other logic to include/exclude parameters as required
        return new[] 
        {
            new KeyValuePair<string, string>("p", this.P),
            new KeyValuePair<string, string>("i", this.I),
        };
    }
}

public class QueryParamSerializer : IRequestQueryParamSerializer
{
    public IEnumerable<KeyValuePair<string, string>> SerializeQueryParam<T>(string name, T value)
    {
        var serializable = value as IQueryParamSerializable;
        if (serializable != null)
        {
            return serializable.Serialize();
        }
        return value.ToString();
    }

    public public IEnumerable<KeyValuePair<string, string>> SerializeQueryCollectionParam<T>(string name, IEnumerable<T> values)
    {
        // Something sensible? Throw NotSupported?
    }
}

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

That still assumes a custom serializer for every object (i have hundreds right now).

It's possible this might come down to something simpler and that is the wuth current tostring serializer. The issue I ran into at the start was I need an object serialized into a standard name/value pair without using the parameter name on the method in the output as it generated an invalid querystring. Is there a way to do that without resorting to a custom serializer or a querymap?

from restease.

canton7 avatar canton7 commented on September 17, 2024

That still assumes a custom serializer for every object (i have hundreds right now).

Don't you have that already, since each object has a custom ToString implementation? Again, legacy issues aside, I don't see the difference between your custom ToString implementation and my custom serialization method, except that mine properly handles things like encoding.

You didn't seem to be too averse to implementing IDictionary<string, string> on your classes, other than the lack of type-safety. Having a method which returns an IDictionary<string string> instead is similar, but keeps the type-safety.

The issue I ran into at the start was I need an object serialized into a standard name/value pair without using the parameter name on the method in the output as it generated an invalid querystring. Is there a way to do that without resorting to a custom serializer or a querymap?

I wouldn't be averse to allowing the key for a query param to be null, in which case just the value would be used. However it would (probably?) encode the value, which would be useless to you.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

No, I was looking to avoid that :) Here is an example of what I am doing right now

I wrote the following to help deal with all of the objects I have (right now handles almost everything except nested objects which I am not sure I need yet)

Gist for Serialization of Object

Then from that, I have define some of methods like this

public class Category
{
    [JsonProperty("categoryId")]
    int Id {get;set;}

    [JsonProperty("name")]
    string Name {get;set;}
}

public interface CategoryApi
{
    [Get("/Category")]
    public Task<Category> GetCategoryAsync([QueryMap] Category item);
}

then I use them like this

var c = new Category { Name = "someCategory", Id = 1234 };

var opts = new DictionarySerializeOptions { UrlEncodeKeyName = false, UrlEncodeValue = false };
var result = await _api.GetCategoryAsync(c.SerializeToDictionary(opts));

that seems to do what I need -- it results in a Http Get like this

http://localhost/Category?name=someCategory&categoryId=123

from restease.

canton7 avatar canton7 commented on September 17, 2024

I still don't understand why you're doing it like that, rather than using a custom IRequestQueryParamSerializer.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

So just to understand, you are suggestion something like this

public class CustomQueryParamSerializer : IRequestQueryParamSerializer
{
    public IEnumerable<KeyValuePair<string, string>> SerializeQueryParam<T>(string name, T value)
    {
        var opts = new DictionarySerializeOptions { UrlEncodeKeyName = false, UrlEncodeValue = false };
        var result = value.SerializeToDictionary(opts));
        return result;
    }

    public public IEnumerable<KeyValuePair<string, string>> SerializeQueryCollectionParam<T>(string name, IEnumerable<T> values)
    {
        // Something sensible? Throw NotSupported? -- this one doesn't make sense to me.  I really cannot see why/when you would serialize an entire object as a single query string parameter?
    }
}

so you are saying that if I do the above then I can do the following, correct?

[Get("/Category")]
Task<Category> GetCategoryAsync([Query()] Category item);

and it will create a URL Query string like this

/Category?name=someName&categoryId=1234

from restease.

canton7 avatar canton7 commented on September 17, 2024

You'll need QuerySerializationMethod.Serialized, but yes.

this one doesn't make sense to me. I really cannot see why/when you would serialize an entire object as a single query string parameter?

I'm not sure what you mean by that. As the documentation states, the SerializeQueryCollectionParam method is called when you pass an IEnumerable<T> to a [Query] parameter. It doesn't return a single query string parameter - it returns a collection of them, just as SerializeQueryParam does.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Sorry about that -- for some reason I was thinking about this example :) when the default json serializer is used.

/Category?item={ name: 'someName', categoryId: '1234' }

That said, how do you see the result working for an Enumerable of certain objects? Would that result is something pretty odd when serializing the object properties? Or do you see it "collapsing" all of the property across the objects into multi-value pairs?

from restease.

canton7 avatar canton7 commented on September 17, 2024

Sorry about that -- for some reason I was thinking about this example :) when the default json serializer is used.

/Category?item={ name: 'someName', categoryId: '1234' }

I'm afraid I'm not sure what you mean. You'll need to tell RestEase to use your custom serializer, of course - that's all in the docs.

Do you mean you've managed to get it working, or not?

That said, how do you see the result working for an Enumerable of certain objects? Would that result is something pretty odd when serializing the object properties? Or do you see it "collapsing" all of the property across the objects into multi-value pairs?

The default behaviour is documented - search for "You can also have an array of query parameters:".

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

That sample above is what RestEase does out of the box with the standard Json serializer when I pass in in object.

And I am testing the suggestion now.

from restease.

canton7 avatar canton7 commented on September 17, 2024

Right, I've added support for [RawQueryString] too.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

That is great news :)

First, let me say thank you for the help and for a rather excellent library. Even with trying to get this working in a way that works with my library your framework has saved me a LOT of time and hassle -- many thanks!

Using the IRequestQueryParamSerializer along with the customer serializer that I wrote seems to be a winner right now (I'll try the RawQueryString out shortly). So again, thank you for helping me wrap my head around the way to extend your library. Everything seems to be working as I need it right now which is great as I can move on to the next problem.

On a related but different topic, can you help me understand the value of the default serialization options?

Consider this signature for what follows

[Get("/Catalog.json")]
Task<Catalog> GetItemAsync(Catalog item = null);

For example, if I use [Query(QuerySerializtionMethod.Default)] I get a URL that looks like this which doesn't seem to offer any real value as it just passes the name of the class as a value.

https://localhost/Catalog.json?item=Models.Item

Or if I use [Query(QuerySerializtionMethod.Serialized)] with NO custom serializer I get a URL that looks like this one below. While I know it is valid, I am at a loss to see how it is useful as it seems to take the entire object, convert it to json and pass it on the query string?

https://local/Catalog.json?item=%7b%22itemID%22%3a2%2c%22systemSku%22%3anull%2c%22defaultCost%22%3anull%2c%22avgCost%22%3anull%2c%22msrp%22%3anull%2c%22discountable%22%3anull%2c%22Prices%22%3anull%2c%22tax%22%3anull%2c%22archived%22%3anull%2c%22itemType%22%3anull%2c%22description%22%3anull%2c%22modelYear%22%3anull%2c%22upc%22%3anull%2c%22ean%22%3anull%2c%22customSku%22%3anull%2c%22manufacturerSku%22%3anull%2c%22createTime%22%3anull%2c%22timeStamp%22%3anull%2c%22categoryID%22%3anull%2c%22category%22%3anull%2c%22taxClassID%22%3anull%2c%22taxClass%22%3anull%2c%22departmentID%22%3anull%2c%22department%22%3anull%2c%22itemMatrixID%22%3anull%2c%22itemAttributesID%22%3anull%2c%22itemAttributes%22%3anull%2c%22manufacturerID%22%3anull%2c%22manufacturer%22%3anull%2c%22noteID%22%3anull%2c%22note%22%3anull%2c%22seasonID%22%3anull%2c%22season%22%3anull%2c%22defaultVendorID%22%3anull%2c%22itemECommerceID%22%3anull%2c%22CustomFieldValues%22%3anull%7d

Am I missing something? It would seem that by default the OOTB behavior for complex objects seems to be more specialized that used in general REST calls?

Also, if I use the ToString method, it would seem that it only allows me to pass 1 value to the framework would would mean that I would need to either take the entire object and encode it in something specialized (like json) or I could only return 1 property value.

Example

public override string ToString()
{
    return SerializeThisObject();
}

or

public override string ToString()
{
    return this._propertyValue;
}

I know you have created some documentation, however I am struggling to see the use cases for all of these when it comes to any object that is complex (not a primitive type as it makes complete sense for most build int primitive types).

from restease.

canton7 avatar canton7 commented on September 17, 2024

I'll try the RawQueryString out shortly

Cool! It hasn't been released yet, but there's a custom nuget feed with intermediate builds at https://ci.appveyor.com/nuget/restease.

For example, if I use [Query(QuerySerializtionMethod.Default)] I get a URL that looks like this which doesn't seem to offer any real value as it just passes the name of the class as a value.

Yep, that called 'ToString()' on the value, as documented. That's a good default behaviour for scalar types, which 99.9% of parameters are. As you can see, it doesn't really work with more complex objects. I didn't want to have a rule like "The default serialization method for primitives is ToString, while the default for other types is Serialized" - that's just too confusing. Pick a good, easy-to-understand default which covers the majority of use-cases, and provide switches to let people change that behaviour to something more complex.

Or if I use [Query(QuerySerializtionMethod.Serialized)] with NO custom serializer I get a URL that looks like this one below. While I know it is valid, I am at a loss to see how it is useful as it seems to take the entire object, convert it to json and pass it on the query string?

Yep, that defaulted to the JsonRequestParamSerializer. It's useful if you wrote your class to be serializable to JSON 😉. Of course it's useless if you didn't want to do this, but what else am I supposed to do? Why are you using QuerySerializationMethod.Serialized with the JsonRequestParamSerializer if you didn't want your query param to be serialized as JSON?

Some APIs want requests of the format ?json={"some": ["json", "object"]}. Serialized request params are intended to cover this case.

The documentation provides an example of creating a custom serializer, e.g. if you want to pass XML. You can build on that to write your own serialization behaviour if that's what you want.

I made the request param serializer more powerful than this use-case requires (in letting you specify a range of key/value pairs), because I expected people to need to do some really weird things. I didn't have a use-case in mind. I never realistically expected someone to take an entire pre-existing object, and roll a ton of logic to convert it into different query params under different conditions 😄. It's supported anyway though, by both the serializer here, and by [QueryMap].

Also, if I use the ToString method, it would seem that it only allows me to pass 1 value to the framework would would mean that I would need to either take the entire object and encode it in something specialized (like json) or I could only return 1 property value.

Yep - the model for query parameters is that the name of the name/value pair is provided by the parameter name (or the [Query] attribute), and the value is something which you pass in at runtime. Hence it makes sense that each parameter provides 1 value. As I said before this is only really useful for primitives, but 99.9% of query parameters are primitives, so it's a pretty sensible default.

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Thanks :) I am not sure I am in agreement on the ToString covering 99% of the use cases though as I would think that a log of people (like me) would assume the library would do a "best pass" at converting the properties to query string params.

Either way -- I am good! Everything is working at this point. Thanks again for all the help/support. I may put a blog article together around the more complex object scenario (I'll send you a link if I do) just in case others might find it useful.

from restease.

canton7 avatar canton7 commented on September 17, 2024

Thanks :) I am not sure I am in agreement on the ToString covering 99% of the use cases though as I would think that a log of people (like me) would assume the library would do a "best pass" at converting the properties to query string params.

As I said, the normal model for query params is that each parameter you pass into the method corresponds to one value in the query string.

What would Task FooAsync([Query("mykey") MyObject obj); produce? In all of your suggestions, "mykey" gets thrown away. That's unintuitive. In the default model, "mykey" is always used as the parameter key. You can change this behaviour with a custom serializer, but at that point it's not unexpected any more, as you've explicitly decided to throw "mykey" away.

Also, where do you draw the line? Why should I serialize an int differently to other types? Where does the line get drawn? Do I serialize a DateTime using its ToString method, or by pulling out each of its many properties? Of course using its ToString method is the only sensible thing to do.

The same goes for many other BCL types: pulling out their properties is silly, and their ToString method returns what you actually want to put into the query param. Should I draw the line at classes/structs? Then why is String a special case? Should I draw it at BCL types vs custom types? That's very arbitrary and very, very unexpected. There's no right answer.

I'd much rather adopt a simple and obvious strategy across the board, and let people opt in to more complex behaviour.

I may put a blog article together around the more complex object scenario (I'll send you a link if I do) just in case others might find it useful.

Cool! If it's good, I may link it from the README 😄

from restease.

ravensorb avatar ravensorb commented on September 17, 2024

Hmm, might be easier and more intuitive then if the default serializer threw an error on complex object?

from restease.

canton7 avatar canton7 commented on September 17, 2024

I think you missed the point of what I was trying to say...

It sounds like you would never want RestEase to call ToString (removing support for scalars), to always try and serialise all properties on an object (so a DateTime would serialise to something really wrong), to throw an exception if the object being serialised is exceeds some weird complexity metric (so someone adds a property to an object being serialised and it suddenly throws in their face: try justifying that behaviour to someone in an issue), and all of this would remove the meaning of the "name" in [Query("name")], which removes the very first base use case for RestEase.

Now that can't possibly be what you're saying, but it's what your words appear to mean.

Let me try and put this another way.

Your use case is strange. It's not usual to serialise an object into query params. Into the body of a request? Sure. But the query params are typically fixed for a given method. At the the most, some might be optional.

Therefore RestEase does not focus on your use case. It focuses on making the more usual use cases nice and easy, and it provides the tools to make your use case possible. You have [QueryMap] and custom param serialisers: two tools to help you.

Someone once said that a good library should make the common things easy, and the uncommon things possible. That's the model I try and follow.

If I wanted to add primary support for your use case, I would add another attribute, similar to [QueryMap], which took an abject and serialised its properties to query parameter keys and values. It would not allow the user to specify a name for the method parameter, because that would not be used. It would probably have a different model for custom serialisers.

Your use case is not the one that [Query] parameters are trying to solve (it just so happens that query param serialisers are powerful enough to subvert into doing what you want). I am not going to change how [Query] works to better suit your use case, because it's intended to solve a different and fundamentally incompatible problem.

Hopefully they makes sense to you. Feel free to design a different model for splatting an object into multiple query parameters in a separate issue: if I like it, I'll agree to accept PRs for it). However the current model for [Query] is trying to solve a different problem, and I'm going to keep them focused on that.

from restease.

canton7 avatar canton7 commented on September 17, 2024

Released, therefore closing this.

from restease.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.