45

I'm improving an existing API, and the requirement is to provide a single get method which can accept multiple search criteria and based on those criteria perform the query.

I'm using Spring MVC. The get method signature:

@GetMapping("/subscribers")
public ResponseEntity<List<SubscriberDTO>> getAllSubscribers(Pageable pageable, @RequestBody List<SearchCriteria> lstSearchCriteria)

The implementation is working as expected tested on Postman

Now I was going to Angular to implementing the front end and I can't find a way to send a body through HttpClient Get method...

I'm kind of stuck. Should I send the search criteria over headers? Or there's a better way of doing it?

1
  • 4
    GET requests shouldn't have bodies (see e.g. stackoverflow.com/questions/978061/http-get-with-request-body). I think you can do it through the HttpClient's general request method, but I wouldn't count this as an improvement to the API. Don't use headers, either; use query parameters. Commented Jan 12, 2019 at 21:53

6 Answers 6

37

As far as I can tell you cannot use HttpClient get to send a body. You could use the query at least for it to be idiomatic. (Or you can try to force the issue somehow).

Lets say you have an array with your criteria:

const criteria = [ {a: 25}, {b: 23} ];
http.get(url + '/?criteria='+ encodeURIComponent( JSON.stringify(criteria)));

Sending a body in a GET request is a violation of some RFC standard, and even though it might work you're bound to summon some arcane demons.

Sign up to request clarification or add additional context in comments.

5 Comments

would you mind adding a reference to your statement 'violation of some RFC standard' to support it?
RFC2616 section 4.3 and sections 5.1.1 - though in truth it's pretty vague and recommends that web servers discard the body when it's not allowed based on the request METHOD - I know most web servers do discard the BODY lines on a GET request. Indeed there is even some mods for apache to invert this behaviour.
has been removed
RFC7231 section 4.3.1 implies that a body in a GET request is allowed but not well defined.
Of course you can. HttpClient.request you can specify anything you want including body and method ("get" in our case)
9

In the service method, you can have your method like below which takes the optional parameters for the search query. And you can send your search parameter over Observe as below.

getAllSubscribers(
    page?,
    itemsPerPage?,
    userParams?
  ): Observable<PaginatedResult<Subscribers[]>> {
    const paginatedResult: PaginatedResultSubscribers[]> = new PaginatedResult<
      Subscribers[]
    >();

    let params = new HttpParams();

    if (page != null && itemsPerPage != null) {
      params = params.append("pageNumber", page);
      params = params.append("pageSize", itemsPerPage);
    }

    if (userParams != null) {
      params = params.append("minAge", userParams.minAge);
      params = params.append("maxAge", userParams.maxAge);
        }

       return this.http
      .get<Subscribers[]>(this.baseUrl + "/subscribers", { observe: "response", params })
      .pipe(
        map(response => {
          paginatedResult.result = response.body;
          if (response.headers.get("Pagination") != null) {
            paginatedResult.pagination = JSON.parse(
              response.headers.get("Pagination")
            );
          }
          return paginatedResult;
        })
      );
  }

The basic idea is the use of HttpParams.

2 Comments

Keep in mind that HttpParams is immutable. I spent a while figuring out why params.append() didn't work.
@Odys -Good Point. You can do new HttpParams().set(minAge", userParams.minAge).set("maxAge", userParams.maxAge)
9

There is an ongoing debate whether get request can have body or not. That I think is not a subject of this thread. If you do need to make get request with body ( for example the server imposes that) you can use request method:

request(method: string, url: string, options: {
        body?: any;
        headers?: HttpHeaders | {
            [header: string]: string | string[];
        };
       //.......
        
    }): Observable<ArrayBuffer>;

6 Comments

I don't think this is valid. I'm currently trying this out and while this does compile the end GET request is still missing a body.
@MBender You do realize that you are making claim that the angular standard implementation is faulty. Are you checking it on the server side ? Try making a request using curl and I bet you get the same result. The problem is probably in your overall networking setup, not the client.
@MBender That is exactly opposite to your previous statement. In any case inspect your code and setup.
Sorry, I messed up my last comment and didn't remember correctly. If I DO remember correctly the network inspection in chrome clearly didn't have a body for the GET request when using this. Unlikely to be a network issue.
This seems to not work with the basic angular-cli dev server setup, the request leaves the browser with no body in it. That is, on Firefox and angular 17.
|
5

Just to clarify some of the answers here, firstly, as stated Angular does not support supplying a body with a GET request, and there is no way around this. The reason for that is not Angular's fault but that of XMLHttpRequest (XHR), the API that browsers use for making requests. XHR does not support body with GET requests.

There is nothing in the current HTTP standards that says a Body with a GET request is invalid, only that it is not semantically well defined.

In terms of REST, in my opinion, supplying a body with a GET request is much preferable to using POST. So it is somewhat annoying that XHR does not support this.

3 Comments

Sure Angular supports it, See about using "request" method. Also in term of REST using body with GET can cause hard to track issues in the future. You will wonder why the application stopped working when "nothing" changed. It can take some efforts to realize that some network component like proxy or new Kubernetes setup does not support gets with body. Don't ask me how I know.
@user2555515 Please do double check this, I could not manage to get a get request out of the browser with a body.
See the first comment. Angular does produce the request. There is no guarantee that get+body is delivered however and some of intermediary components may ignore it.
-1

To make Amir's answer some more generic I have done the following to build params for the passed item.

To my generic data service I have added this variant:

// Get data for passed parameters or item containing filters
public getByItem<T>(apiMethod: string, item?: T, apiParms?: HttpParams): Observable<T> {
    if (!apiParms) apiParms = new HttpParams();
    if (item) {
        const keys = Object.keys(item) as Array<keyof T>;
        for (let key of keys) {
            apiParms = apiParms.append(key.toString(), item[key].toString());
        }
    }
    // Call generic method of data service
    return this.get<T>(apiMethod, apiParms); // => return this.http.get<T>(environment.apiUrl + apiMethod, { headers: this.defaultHeaders

Comments

-1

While it it is not recommended (the very first sentence from the GET method's documentation), technically you could do it:

  constructor(private http: HttpClient) {}

  doAGetWithBodyRequest(): void {
    this.http.request(
    // the type of request:
    'get',
    // the path:
    'https://localhost:8080',
    // the body:
    { body: 'your-body-in-here' }
  }

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.