Angular HttpClient Get method with body
Asked Answered
D

5

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?

Darr answered 12/1, 2019 at 21:45 Comment(1)
GET requests shouldn't have bodies (see e.g. https://mcmap.net/q/40417/-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.Averil
N
36

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.

Nettienetting answered 12/1, 2019 at 22:2 Comment(5)
would you mind adding a reference to your statement 'violation of some RFC standard' to support it?Lackaday
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.Nettienetting
has been removedLinderman
RFC7231 section 4.3.1 implies that a body in a GET request is allowed but not well defined.Ramrod
Of course you can. HttpClient.request you can specify anything you want including body and method ("get" in our case)Quadripartite
J
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.

Jackijackie answered 12/1, 2019 at 22:15 Comment(2)
Keep in mind that HttpParams is immutable. I spent a while figuring out why params.append() didn't work.Ewold
@Ewold -Good Point. You can do new HttpParams().set(minAge", userParams.minAge).set("maxAge", userParams.maxAge)Gaud
Q
6

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>;
Quadripartite answered 9/4, 2022 at 22:0 Comment(5)
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.Etheleneethelin
@Etheleneethelin 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.Quadripartite
@Etheleneethelin That is exactly opposite to your previous statement. In any case inspect your code and setup.Quadripartite
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.Etheleneethelin
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.Mercia
O
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.

Operative answered 25/11, 2021 at 23:3 Comment(3)
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.Quadripartite
@Quadripartite Please do double check this, I could not manage to get a get request out of the browser with a body.Mercia
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.Quadripartite
H
-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
Hoffer answered 30/7, 2019 at 19:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.