3

I've been trying to send a POST request using HttpClient without a body using the PostAsync method. However, to my utter bewildement, the request is getting sent as a GET and not a POST.

enter image description here

Naturally, the request fails with a 405 Method Not Allowed becase the server doesn't allow GET.

Code sample:

var response = await new HttpClient().PostAsync("https://api.creativecommons.engineering/v1/auth_tokens/token?client_id=adsf&client_secret=asdf&grant_type=client_credentials", null);
Console.WriteLine(response.RequestMessage);

Produces:

Method: GET, RequestUri: 'https://api.creativecommons.engineering/v1/auth_tokens/token/?client_id=adsf&client_secret=asdf&grant_type=client_credentials', Version: 1.1

Example in .NET Fiddle

I've also tried using Flurl, but lead to the same result (I am guessing it is using HttpClient under the hood).

Any ideas why PostAsync is sending the request as a GET rather than POST?


Update

This question got quite a bit of hate without offering an ounce of constructive criticism... Well done StackOverflow, really well done, so much for being more welcoming...

If you come across a similar issue, here are the three things to look for:

  1. HttpClient will automatically redirect if the response is 301 (Redirect). It won't return you 301 as a status code!
  2. As it does the redirect it changes the request from POST to GET. This is technically correct, because in order to preserve the method when redirecting the server needs to return 307 and not 301.
  3. Check the address in the code against the one in the RequestMessage. This may be obvious in some cases, but in my case, the redirect involved adding a single / not something easily visible in the debugger window!
https://api.creativecommons.engineering/v1/auth_tokens/token?client_id=adsf&client_secret=asdf&grant_type=client_credentials
https://api.creativecommons.engineering/v1/auth_tokens/token/?client_id=adsf&client_secret=asdf&grant_type=client_credentials
                                                            ^    
Dawid O
  • 6,091
  • 7
  • 28
  • 36
  • 1
    Thanks Dawid. Upvoted. Very confusing issue, especially when integration testing. I burned a lot of time, couldn't figure out why my very specific POST requests end up as GET. I left some setup steps below for integration test setup – jens Jul 23 '21 at 16:14
  • You just saved me hours, thanks man! – Giacomo De Liberali Mar 26 '23 at 07:53

2 Answers2

1

I posted null to that body, looks like it is throwing a 405 and returning some information to look into. I believe the display of "GET" is deceiving.

EDIT: Ok, so to revise:

First the post executes and receives a 301 from the server.

Fiddler showing a POST with a 301 response

Followed by a redirect to another endpoint (as a GET) with the result of 405. Thus, the net result of your request manifested as a GET with a 405. My mistake.

enter image description here

Timothy Stepanski
  • 1,186
  • 7
  • 21
  • You need to clarify. There doesn't appear to be anything 'deceiving' about that code. A POST request, that (probably through my fault) seems to be sent as a GET, which then the server rejects (returns 405), there is no response (even with proper keys in place). – Dawid O Feb 24 '21 at 20:29
  • Yes, but this not not what I am asking... I understand 405 error. My question is why is PostAsync sending a GET request rather than a POST. I've changed the title just to make this clearer. – Dawid O Feb 24 '21 at 20:46
  • 1
    Because of the 301. Add a slash after the word token at the end of the route, right before the ? starting the query string. That will fix your issue. – Timothy Stepanski Feb 24 '21 at 20:54
  • Great thanks. Might be worth making it clearer in the answer that HttpClient will automatically redirect on 301 (Redirect) - apparently, without any trail to trace it back. I am surprised other people didn't run into this issue before. – Dawid O Feb 24 '21 at 21:30
0

When utilizing integration testing you will run into this issue due to https redirect when the default startup contains this

var opt = new RewriteOptions().AddRedirectToHttps();
app.UseRewriter(opt);
app.UseHttpsRedirection();

It's not enough to use the CustomWebApplicationFactory suggestion from Microsoft. You'll have to configure your own TestServer in your *Fixture, example below

            const string baseUrl = "https://localhost:5001";
            const string environmentName = "Test";
            var contentRoot = Environment.CurrentDirectory;

            Configuration = new ConfigurationBuilder()
                .SetBasePath(contentRoot)
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{environmentName}.json", true)
                .AddEnvironmentVariables()
                .Build();
            
            var builder = new WebHostBuilder()
                .UseUrls(baseUrl)
                .UseContentRoot(contentRoot)
                .UseEnvironment(environmentName)
                .UseConfiguration(Configuration)
                .UseStartup<TestStartup>();

            Server = new TestServer(builder) {BaseAddress = new Uri(baseUrl)};

Additionally if you follow DDD and have your Controllers in a different project, make sure your API project contains Assembly reference

services.AddControllers(options =>
                    options.Filters.Add(new HttpResponseExceptionFilter()))
                .AddApplicationPart(typeof(Startup).Assembly)
                .AddApplicationPart(typeof(MyController).Assembly);
jens
  • 534
  • 1
  • 8
  • 17