简体   繁体   中英

How to use HttpClient to send content in body of GET request?

Currently to send a parameterized GET request to an API interface I am writing the following code:

api/master/city/filter?cityid=1&citycode='ny'

But I see that there is a limit on the URL length of 2,083 characters.

To avoid this I would like to send the parameters in json format in the content body for a GET request.

However, I see that none of the Get methods for the HttpClient allow for a content body to be sent. For the POST I could see there is a method within HttpClient named PostAsync that allows for a content body.

Is there a way to send parameters for a GET request not in the URL in order to avoid the URL length limit?

Please read the caveats at the end of this answer as to why HTTP GET requests with bodies are, in general, not advised.


  • If you are using .NET Core , the standard HttpClient can do this out-of-the-box. For example, to send a GET request with a JSON body:

     HttpClient client = ... ... var request = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri("some url"), Content = new StringContent("some json", Encoding.UTF8, ContentType.Json), }; var response = await client.SendAsync(request).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
  • .NET Framework doesn't support this out-of-the-box (you will receive a ProtocolViolationException if you try the above code). Thankfully Microsoft has provided the System.Net.Http.WinHttpHandler package that does support the functionality - simply install and use it instead of the default HttpClientHandler when constructing your HttpClient instances:

     var handler = new WinHttpHandler(); var client = new HttpClient(handler); <rest of code as above>

    Reference: https://github.com/dotnet/corefx/issues/28135#issuecomment-467261945


Caveats:

  • HTTP GET with a body is a somewhat unconventional construct that falls in a gray area of the HTTP specification - the end result is that many older pieces of software either cannot handle such a request at all, or will explicitly reject it because they believe it to be malformed. You need to make very sure that the endpoint you're trying to send such a request to does support it, or at best you will get an HTTP error code back; at worst the body will be silently discarded. This can lead to some head-scratching debugging!
  • Caching proxy servers, again particularly older ones, may cache GET requests based only on the URL because they don't expect a body to be present. This could either result in the least recent request being cached forever (which will break your software), or that the only request ever cached is the most recent one issued (which will prevent caching from working as intended). Again, this can be very painful to figure out.

I can't use .NET core and I don't want to install System.Net.Http.WinHttpHandler , which has a ton of dependencies. I solved it by using reflection, to trick WebRequest that it is legal to send body with a GET request (which is according to latest RFC). What I do is to set ContentBodyNotAllowed to false for HTTP verb "GET".

var request = WebRequest.Create(requestUri);

request.ContentType = "application/json";
request.Method = "GET";

var type = request.GetType();
var currentMethod = type.GetProperty("CurrentMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);

var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod, false);

using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
    streamWriter.Write("<Json string here>");
}

var response = (HttpWebResponse)request.GetResponse();

Note, however, that the attribute ContentBodyNotAllowed belongs to a static field, so when its value changes, it remains in effect for the rest of the program. That's not a problem for my purposes.

After spending a lot of time I couldn't make neither HttpClient nor WebRequest work in my .Net Core project, apparently the server was not getting my data and it returned an error saying some specific data was not available in my request. At last only RestSharp worked in my case (the variable myData is a Dictionary<string,string> instance in the following code):

var client = new RestClient("some url");
var request = new RestRequest(Method.GET);
foreach (var d in myData)
    request.AddParameter(d.Key, d.Value);
var result = (await client.ExecuteAsync(request)).Content;

I use WebRequest. The value is in variable json.

            var request = WebRequest.Create(requestUri);

            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
            
            request.ContentType = "application/json";
            request.Method = "GET";
            request.Headers.Add("Authorization", "Bearer " + Token);

            var type = request.GetType();
            var currentMethod = type.GetProperty("CurrentMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);

            var methodType = currentMethod.GetType();
            methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod, false);

            using (var streamWriter = new StreamWriter(request.GetRequestStream()))
            {
                streamWriter.Write(JsonConvert.SerializeObject(Entity));
            }

            var response = request.GetResponse();
            var responseStream = response.GetResponseStream();
            if (responseStream != null)
            {
                var myStreamReader = new StreamReader(responseStream, Encoding.Default);
                var resultEntity= myStreamReader.ReadToEnd();
                myStreamReader.ReadToEnd());
            }
            responseStream.Close();
            response.Close();

Java spring restTemplate can do this.

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import java.net.URI;

public class HttpComponentsClientRestfulHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {

    @Override
    protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
        if (httpMethod == HttpMethod.GET) {
            return new HttpGetRequestWithEntity(uri);
        }
        return super.createHttpUriRequest(httpMethod, uri);
    }

    /**
     * 定义HttpGetRequestWithEntity实现HttpEntityEnclosingRequestBase抽象类,以支持GET请求携带body数据
     */
    private static final class HttpGetRequestWithEntity extends HttpEntityEnclosingRequestBase {
        public HttpGetRequestWithEntity(final URI uri) {
            super.setURI(uri);
        }
        @Override
        public String getMethod() {
            return HttpMethod.GET.name();
        }
    }
}

    /**
     * get RestTemplate
     *
     * @return
     */
    private RestTemplate getTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        //修改restTemplate的RequestFactory使其支持Get携带body参数
        restTemplate.setRequestFactory(new HttpComponentsClientRestfulHttpRequestFactory());
        return restTemplate;
    }

private String query(String url, Map<String, Object> paramBody) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity requestEntity = new HttpEntity(JsonUtil.of(paramBody), httpHeaders);
        RestTemplate template = getTemplate();
        ResponseEntity response = template.exchange(url, HttpMethod.GET, requestEntity, String.class);
        Object result = response.getBody();
        return result.toString();
    }

similar to above answer but less code

var request = new HttpRequestMessage
{
    Method = HttpMethod.Get,
    RequestUri = targetUri,
    Content = new StringContent(payload.Payload),
};
var response = await client.SendAsync(request).ConfigureAwait(false);
var responseInfo = await response.Content.ReadAsStringAsync();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM