RestSharp: Cannot Send GET Request with Content-Type Header with RestSharp v107

After upgrading to v107, I started getting the following error when trying to execute a GET request with the Content-Type: application/force-download header: Cannot send a content-body with this verb-type.

The header is the only parameter in the request, but it seems like RestSharp transparently adds a body causing the error.

All attempts below causes the error mentioned when executing the request: restRequest.AddHeader("Content-Type", "application/force-download"); restRequest.Parameters.AddParameter(new BodyParameter("", "", "application/force-download", DataFormat.None)); restRequest.AddStringBody("", "application/force-download");

While it may not be a good practice to use this header parameter with a GET request, it is unfortunately required for me to perform some operation with a system from a vendor and the last version of RestSharp does not support this anymore.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 18 (12 by maintainers)

Commits related to this issue

Most upvoted comments

@VictorRH have you read the docs following the link I posted? RestSharp adds the necessary headers automatically if the request has a body.

Here’s the code that would work:

var options = new RestClientOptions("https://enqvvdg27q3o.x.pipedream.net/") {
    UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
};
var payload = "key=value&foo=bar";
var client = new RestClient(options);
var request = new RestRequest("", Method.Post)
    .AddHeader("Accept-Language", "en-US,en;q=0.9")
    .AddHeader("Origin", "urlorigin")
    .AddHeader("Referer", "urlReferer")
    .AddStringBody(payload, "application/x-www-form-urlencoded");

var response = await client.ExecutePostAsync(request);

and here is what the request looks like:

image

Notice that the content-type header is present, although if you use HttpTracer you won’t see it. It’s because HttpTracer does not print out content headers, it only shows you request headers.

I managed to apply a fix with a utility class using System.Net.HttpClient directly. The implementation might be too dirty for many, but the usage is very simple and clean:

// using System.Net.Http;
// using System.Net.Http.Headers;
// using System.Linq;
// using System.Threading;
// using System.Threading.Tasks;
// using System.Collections.Generic;
// using System.Reflection;
// using RestSharp;

public static class HttpMessageUtils
{
    public const string ContentTypeHeaderName = "Content-Type";

    class HandlerAllowingContentTypeHeaderOnGetRequests : DelegatingHandler
    {
        public HandlerAllowingContentTypeHeaderOnGetRequests(
            HttpMessageHandler p_innerHttpMessageHandler
        ) : base(p_innerHttpMessageHandler)
        { }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage p_request, CancellationToken p_cancellationToken)
        {
            FieldInfo invalidHeadersField =
                typeof(HttpHeaders).GetField("invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance) ??   // System.Net.Http v2.2+
                typeof(HttpHeaders).GetField("_invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance)     // System.Net.Http before v2.2
            ;

            HashSet<string> invalidHeaders = (HashSet<string>)invalidHeadersField.GetValue(p_request.Headers);

            if (invalidHeaders.Contains(ContentTypeHeaderName))
            {
                invalidHeaders.Remove(ContentTypeHeaderName);

                string contentTypeHeaderValue = p_request.Content.Headers.ContentType.MediaType;
                string requestContent = await p_request.Content.ReadAsStringAsync();

                if (string.IsNullOrWhiteSpace(requestContent))
                    p_request.Content = null;
                else
                    p_request.Content.Headers.Remove(ContentTypeHeaderName);

                p_request.Headers.Add(ContentTypeHeaderName, contentTypeHeaderValue);
            }

            HttpResponseMessage response = await base.SendAsync(p_request, p_cancellationToken);
            return response;
        }
    }

    public static HttpClient GetHttpClient(this RestClient p_restClient)
    {
        return ((TypeInfo)p_restClient.GetType())
            .DeclaredProperties
            .First(p => p.Name == "HttpClient")
            .GetValue(p_restClient)
            as HttpClient
        ;
    }

    private static FieldInfo GetHttpMessageHandlerField(this HttpClient p_httpClient)
    {
        return ((TypeInfo)p_httpClient.GetType().BaseType)
            .DeclaredFields
            .First(f => f.Name == "handler")
        ;
    }

    public static HttpMessageHandler GetMessageHandler(this HttpClient p_httpClient)
    {
        return p_httpClient.GetHttpMessageHandlerField()
            .GetValue(p_httpClient)
            as HttpMessageHandler
        ;
    }

    public static void SetMessageHandler(this HttpClient p_httpClient, HttpMessageHandler p_newHttpMessageHandler)
    {
        p_httpClient.GetHttpMessageHandlerField().SetValue(p_httpClient, p_newHttpMessageHandler);
    }

    public static HttpMessageHandler GetInnerMessageHandler(this RestClient p_restClient)
    {
        return p_restClient.GetHttpClient().GetMessageHandler();
    }

    public static void AllowContentTypeHeaderOnGetRequests(this RestClient p_restClient)
    {
        HttpMessageHandler innerMessageHandler = p_restClient.GetInnerMessageHandler();
        HttpMessageHandler newMessageHandler = new HandlerAllowingContentTypeHeaderOnGetRequests(innerMessageHandler);
        p_restClient.GetHttpClient().SetMessageHandler(newMessageHandler);
    }
}
RestClientOptions restClientOptions = new(p_baseURL) { Credentials = p_credentials };
RestClient restClient = new(restClientOptions);
RestRequest restRequest = new(p_relativeResourcePath, p_method);

restRequest.AddHeader("Content-Type", "application/force-download");
restClient.AllowContentTypeHeaderOnGetRequests();
RestResponse restResponse = await restClient.ExecuteAsync(restRequest, p_method);