28

I have a REST API with GETs operations which receive a (long) list of parameters (8 parameters, for example). The aim of this operation is to search and filter elements.

Which is the best practice to manage this scenario?:

(1) Receive a list of parameters?:

public ResultType Get(int p1, int p2, string p3...) { ... }

(2) Or receive an object that encapsulate these parameters?:

public class MyClass
{
    public int X { get; set; }
    public int Y { get; set; }
    public string Z { get; set; }
}

public ResultType Get(MyClass parameter) { ... }

Think in an eCommerce scenario in which you want to search and filter "products" by name, description, category, brand, model, price, etc...

3
  • 1
    Position is lesser of an identifier than a proper name for a value.
    – S.D.
    Aug 31, 2018 at 12:27
  • Why do you have 8 parameters? What do they do? Is there some decision logic involved (and is it a mess)? Can you split that api/method into several methods that take 3 or 4 parameters? Aug 31, 2018 at 13:17
  • 2
    8 parameters is just an example. My Get operation is a "search" method. Think in an eCommerce scenario in which you want to search and filter "products" by name, description, category, brand, model, price, etc... I've edited the question to add context Sep 2, 2018 at 11:45

3 Answers 3

17

I have a REST API with GETs operations which receive a (long) list of > parameters. Which is the best practice to manage this scenario?

AFAIK, there is no firmly established best practice (sorry). However, one can make some recommendations:

  • Try to avoid using POST (instead of GET) if the request is safe (i.e. side-effect free, in particular not modifying data). If the parameters are very large, you may have to use POST to get around length limitations, but usually this is not a problem (most software supports quite long URLs), and safe requests should use GET to allow optimizations such as caching and prefetching.

  • If you use GET, you must send your parameters as URL parameters1). In that situation, the natural and common solution is to use a list of parameters, so that's what I'd recommend. Yes, the list will be long, but the REST API is (probably) intended for programmatic use, so I don't see a problem with this. Users of the API are free to encapsulate the parameters in an object inside their own code.

  • Technically, you could also put an object into an URL parameter (as JSON, XML or whatever), but that is unusual, so I would avoid it if possible.

1) Strictly speaking, you can use a body with a GET request, but this is unusual and generally not recommended; see e.g. HTTP GET with request body.


Finally, just as with methods in source code that have long parameter lists, you might want to consider whether the REST API needs a refactoring. Just like in source code, such a long parameter list indicates the API endpoint is doing many different things. Does it make maybe make sense to split it up, or provide default settings? But that's a different question...

6
  • Technically you can also send a body with a GET although unusual
    – Ewan
    Aug 31, 2018 at 11:08
  • also, are you sure you mean idempotent, I would say 'cacheable' GetTodaysDayName() might be considered idempotent, but you wouldn't want to cache it
    – Ewan
    Aug 31, 2018 at 11:12
  • Let the GET vs POST battle commence!!!! :)
    – Ewan
    Aug 31, 2018 at 11:43
  • @Ewan: As to caching GetTodaysDayName: Assuming you need today's name ten times a second, and don't care to use the wrong day for a few seconds, you do most likely want to cache it. Still, I wrongly used "idempotent", what I had in mind was "safe" (=not modifying). Edited.
    – sleske
    Aug 31, 2018 at 11:49
  • 1
    @Ewan constructive battles in which the "opponents" enrich the response of each other are more than welcome. Thanks to both of you for a very comprehensive overview on all the options !
    – Christophe
    Aug 31, 2018 at 11:50
7

Best practice is to POST the parameters as an object.

This avoids the URL length limit and other problems with query strings. If you send multiple parameters in JSON then an object is the standard way of doing it, so deserialising to one makes sense.

You can avoid creating random 'Request' objects that are only used in your Controllers by deserialising to a dynamic object if you like; although casting to the right types afterwards can be equally messy.

In the old days you would have been able to have multiple parameters in your controller action automatically bound to an incoming JSON object's fields. But this no longer works out of the box in .net core.

Although there is this, which I might be tempted to try

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-2.1#application-model-usage-in-webapicompatshim

Edit: Just going to add a few points on the use of GET

  • Caching: GET will be cached by clients that obey the HTTP spec. But the spec is designed to make webpages load faster. The caching is helpful if your API result has the same cache requirements as a webpage, but unhelpful if it doesn't. You can add your own caching of POST responses in the client if required.

  • URL length: This is mainly an issue when you are sending an array. Obviously an array can easily get very long and the query string parameter names will be repeated for each item. However, even if you are only sending one string, technically that string can be very very long.

  • Logging: By default many web servers will log the entire query string. Often you don't want parameter data ending up in plain text logs.

  • GET with body: This would seem to be the perfect answer, satisfying REST purists while allowing a nice data structure, but it is unusual and frowned upon, a POST is the standard way to send a body.

9
  • 6
    I don't think this is agreed-upoin best practice. In particular, if the request is idempotent, it is best practice to use GET and not PUT (for cacheability, among other things). And URL length limits are quite large nowadays, so not necessarily a problem.
    – sleske
    Aug 31, 2018 at 9:52
  • @sleske If you think GET is better you should write an answer about why. String z can be 4Mb long though
    – Ewan
    Aug 31, 2018 at 9:58
  • actually just googling and the max json string length seems uncertain
    – Ewan
    Aug 31, 2018 at 10:02
  • 1
    @ThomasJunk what context do you refer to? the string is called 'z', there could be literally anything in it. whats the GDPR fine $20m?
    – Ewan
    Sep 2, 2018 at 14:59
  • 1
    @ThomasJunk the standard log will also have the ip. But yes we digress. the point isnt that you log the info, its that you dont realise you are logging the info and get caught out in an audit. Best practice is to only log specfic info, not anything the user sends
    – Ewan
    Sep 2, 2018 at 15:15
2

My approach:

  1. Implement a custom URL parameters builder class and encode (base 64) the entire query parameters with StringBuilder + delimiter char to differentiate each parameter.

  2. The receiving side should use the same builder to decode and construct the query parameter object.

  3. The get URL looks like this: http(s)://www.mysite.com/paramsId, where paramsId is a base64 encoded parameter list object.

    @Data
    @Builder(builderClassName = "Builder")
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    class OrderParamsBuilder {       
        private String price;
        private String quantity;
        private int itemsCount;
        // add more parameters here
    }
    

Another easy way is to go for a POST request, which I do not recommend.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.