title: Why is Refit with custom handlers converting my GET requests into POST requests? (405 Method Not Allowed)
body: <p>I have a Xamarin app in which I am using Refit to call my ASP.NET Core Web API backend (.NET 6). On the iOS side of things, I use a custom <code>System.Net.Http.HttpClientHandler</code> to add the api key and bearer token to the requests. On Android, I do something very similar, but the handler inherits from <code>Xamarin.Android.Net.AndroidClientHandler</code>. The code in each file is identical, line for line, except the base classes that each inherits from, which are the standard base handler classes that are typically prescribed for iOS and Android when using Xamarin.</p>
  <p>One of the primary <code>GET</code> endpoints on my API takes a content body, which contains some potentially large filters. I realize that it's not standard to put a content body in a <code>GET</code> request, but I'm doing it because my query filters can be large and complex, and the filters are not suitable for placing in the query string of the request URL.</p>
  <p>In the iOS app, these requests work just fine: the requests are issued with the <code>GET</code> verb, and the backend processes the request and inspects the body content successfully, and returns a successful <code>200 OK</code> response.</p>
  <p>On the Android side of things, I'm getting a <code>405 Method Not Allowed</code>. The weird thing about this is that the debug output from my handler clearly shows that the request verb is <code>GET</code>, but the response from the backend indicates that a <code>POST</code> is being sent.</p>
  <pre><code>[7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request]======= Request Begin =======
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] GET /api/v1/event/lite?pageIndex=1&amp;pageSize=25 https/2.0
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] Host: [omitted]
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] apiKey: [omitted]
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] Authorization: Bearer
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] Content-Type: application/json; charset=utf-8
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] Content:
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] {&quot;includeUnpublished&quot;:false,&quot;includeOnlyOwnedItems&quot;:false,&quot;includeDeleted&quot;:false,&quot;startDate&quot;:&quot;2022-03-04T00:00:00&quot;,&quot;duration&quot;:&quot;1.00:00:00&quot;,&quot;isFree&quot;:null,&quot;favoriteOption&quot;:2,&quot;ageGroupIds&quot;:[],&quot;eventIds&quot;:[],&quot;venueIds&quot;:[],&quot;partyIds&quot;:[],&quot;tags&quot;:[]}...
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request] Duration: 00:00:01.1710510
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d -   Request]======= Request End =========
  
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response]======= Response Start ======
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] HTTPS/1.1 405 Method Not Allowed
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] api-supported-versions: 1.0
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Connection: keep-alive
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Date: Fri, 04 Mar 2022 16:42:42 GMT
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Request-Context: appId=cid-v1:8968a593-b085-4979-81da-5c5918483840
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Server: Microsoft-IIS/10.0
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] X-Android-Received-Millis: 1646412160790
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] X-Android-Response-Source: NETWORK 405
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] X-Android-Selected-Protocol: http/1.1
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] X-Android-Sent-Millis: 1646412160706
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] X-Powered-By: ASP.NET
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Allow: GET, DELETE
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Content-Length: 225
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Content-Type: application/json; charset=utf-8
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Content:
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] {&quot;error&quot;:{&quot;code&quot;:&quot;UnsupportedApiVersion&quot;,&quot;message&quot;:&quot;The HTTP resource that matches the request URI '[omitted]' with API version '1' does not support HTTP method 'POST'.&quot;,&quot;innerError&quot;:null}}...
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response] Duration: 00:00:00.0284520
  [7de9dcf3-79c2-4b76-a03e-f51af36b1a7d - Response]======= Response End ========
  EventApiClient.ReadLite failed: Response status code does not indicate success: 405 (Method Not Allowed). Retying in 0 ms...
  Response status code does not indicate success: 405 (Method Not Allowed).
  Error: Response status code does not indicate success: 405 (Method Not Allowed).
  </code></pre>
  <p>So, I fired up Charles Proxy and intercepted the request, just to make sure. And sure enough, the outbound request is a <code>POST</code>, even though I'm telling Refit to send a <code>GET</code>. It's as if Refit is seeing that I've specified body content, and is automatically (and silently) changing the verb of the request from <code>GET</code> to <code>POST</code>.</p>
  <p>I have even manually composed the request in Postman, and I am able to successfully get back data with a <code>200 OK</code>, just as I do in my iOS scenario.</p>
  <p>Is there something specific to the <code>AndroidClientHandler</code> that mangles requests by changing the verb when a content body is present? Or is there something inside of Refit itself that causes this behavior?</p>
  <p>UPDATE:
  After narrowing it down to being something within <code>AndroidClientHandler</code>, I opened an issue Xamarin.Android team here:
  <a href="https://github.com/xamarin/xamarin-android/issues/6813" rel="nofollow noreferrer">https://github.com/xamarin/xamarin-android/issues/6813</a></p>
  <p>UPDATE UPDATE:
  Both AndroidClientHandler and NSURLSessionHandler don't like when a body exists in a GET request. So, I simply decided to make my endpoints respond to the POST verb instead of GET. The entire impetus for this is that my query filters can be so large that they're not friendly for putting in a query string, which is typically where filter params go. So, I decided to pass the filters in the request body. That's where problems arose. So, now I'm just using POST for making my queries (because I control both the client and the server), even though that goes against convention. But I don't care...as long as the HTTP calls succeed, I'm not going to be a stickler for HTTP conventions.</p>