RESTful request API design with a long list of request parameters

I need to design a RESTful request API that returns a collection of objects based on multiple filters. The usual HTTP method for this is GET. The only problem is that it can have at least a dozen filters, and if we pass all of them as request parameters, the URL can be quite long (long enough to be blocked by some kind of firewall).

Reducing the number of parameters is not an option.

One alternative that I might think of is to use the POST method for the URI and send filters as part of the POST body. This is against being RESTfull (making a POST call to request data).

Does anyone have any better design suggestions?

+131
Jan 07 '13 at 18:57
source share
4 answers

Remember that with the REST API, all this applies to your point of view.

Two key concepts in the REST API are endpoints and resources (entities). It is clear that the endpoint either returns resources via GET, or accepts resources through POST and PUT, etc. (Or a combination above).

It is assumed that with the help of POST, the data you send may or may not lead to the creation of a new resource and related endpoints, which, most likely, will not "live" under the POSTed url. In other words, when you send POST, you send data for processing. The POST endpoint is not where the resource can usually be found.

Quotation from RFC 2616 (with inappropriate parts omitted and highlighted relevant parts):

9.5 POST

The POST method is used to request that the source server accept the object enclosed in the request as a new subordinate resource identified by the Request-URI in the query string. POST is designed to allow a uniform method to cover the following functions:

  • ...
  • Providing a data block, for example, the result of submitting a form, to the data processing process
  • ...

...

An action performed by the POST method may not result in a resource that can be identified by a URI . In this case, either 200 (OK) or 204 (No content) is the corresponding response status, depending on whether the response includes an object that describes the result .

If the resource was created on the source server, the response MUST be 201 (created) ...

We are used to endpoints and resources representing "things" or "data", whether it is a user, a message, a book - regardless of what the problem area dictates. However, the endpoint may also expose another resource — for example, search results.

Consider the following example:

GET /books?author=AUTHOR POST /books PUT /books/ID DELETE /books/ID 

This is a typical REST CRUD. However, what if we added:

 POST /books/search { "keywords": "...", "yearRange": {"from": 1945, "to": 2003}, "genre": "..." } 

There is nothing superfluous at this endpoint. It receives data (entity) in the form of a request body. This data is the search criteria - DTO, like any other. This endpoint creates a resource (object) in response to a query: Search results . The search result resource is temporary, immediately transferred to the client without redirecting, and is not exposed to any other canonical URL.

It is still REST, except that entities are not books - the query object is the criteria for finding books, and the response object is the search results in books.

+114
Aug 13 '15 at 9:32
source share

Many people have agreed that a GET with a query string that is too long or too complex (for example, query strings do not handle nested data easily) can be sent as POST instead, with the complex / long data presented in the request body.

See the specification for POST in the HTTP specification. He is incredibly wide. (If you want to sail the battleship through a loophole in REST ... use POST.)

You lose some of the benefits of GET semantics ... for example, automatic retries because GET is idempotent, but if you can live with it, it might be easier to simply accept the processing of long or complex requests using POST.

(lol long digression ... I recently discovered that, according to the HTTP specification, GET can contain the body of a document. There is one section that says, to rephrase: "Any request can have a body of a document, except for those listed in this section" ... and the section to which it refers does not display a list. I searched and found a stream in which HTTP authors spoke about this, and this was intentional, so routers would not have to distinguish between different messages. However, in practice, many infrastructure objects throw a GET body, so you can GET filters presented in the body, for example, POST, but you'll roll the dice.)

+72
Jan 10 '13 at 9:02
source share

In short: do a POST, but override the HTTP method using the X-HTTP-Method-Override header.

Real request

POST / books

Entity body

{"title": "Ipsum", "year": 2017}

Headings

X-HTTP override method: GET

On the server side, check if the X-HTTP-Method-Override header exists, and then takes its value as a way to route to the endpoint in the backend. Also, take the body of the object as a query string. On the flip side, the request became just GET.

Thus, you keep the design in harmony with the principles of REST.

Edit: I know that this solution was originally intended to solve the problem of the verb PATCH in some browsers and servers, but it also works for me with the verb GET in the case of a very long URL, which is the problem described in the question.

+6
May 04 '17 at 23:32
source share

If you are developing in Java and JAX-RS, I recommend you use @QueryParam with @GET

I had the same question when I needed to view a list.

See an example:

 import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; @Path("/poc") public class UserService { @GET @Path("/test/") @Produces(MediaType.APPLICATION_JSON) public Response test(@QueryParam("code") final List<Integer> code) { Integer int0 = codigo.get(0); Integer int1 = codigo.get(1); return Response.ok(new JSONObject().put("int01", int0)).build(); } } 

URI pattern: 'poc / test? code = 1 & code = 2 & code = 3

@QueryParam automatically converts the query parameter 'orderBy = age & orderBy = name' to java.util.List.

-3
Mar 19 '18 at 17:05
source share



All Articles