-1

I'm unable to perform an HTTP Post with an app running in an Android Emulator.

{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers: { Server: Microsoft-HTTPAPI/2.0 Date: Wed, 23 Oct 2019 00:58:01 GMT Connection: close Forwarded: host=XXX.XXX.X.XX:XXXXX; proto=https Content-Type: text/html; charset=us-ascii
Content-Length: 374 }}

Setup:

  • I'm using an IP address generated by Conveyor by Keyoti
  • I installed a security certificate on the emulator required by Conveyor by Keyoti
  • I swapped out Microsoft.AspNetCore.Mvc.HttpPost attribute with System.Web.Http.HttpPost

Emulator:

  • Successful: HTTP Get
  • Failed: HTTP Post

Integration Test:

  • Successful: HTTP Post (using same endpoint)

Code:

I wrote an automated test that calls the same HTTP Post implementation. Because I executed the same code successfully on my laptop via an automated test, I don't think the actual code is the issue:

open Microsoft.AspNetCore.Mvc
open Newtonsoft.Json

[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
    inherit ControllerBase()

    [<System.Web.Http.HttpPost>]
    member x.Post([<FromBody>] json:string) =

        ...

Summary:

In conclusion, I have isolated the environment to the Android Emulator and not my laptop. Hence, the emulator can successfully trigger an HTTP Get. However, it fails to perform a HTTP Post even though my laptop device can do both.

UPDATE:

I applied guidance from this Xamarin Android ASP.Net Core WebAPI document.

Specifically, I installed another security certificate on the Android emulator.

I was then able to observe an HTTP Get on the Android Emulator.

However, I continue to get an error for HTTP Post.

OperationCanceledException

Physical Device:

If I run the app from a physical android device I observe the following:

{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
  Date: Wed, 23 Oct 2019 13:33:20 GMT
  Server: Kestrel
  Transfer-Encoding: chunked
  Forwarded: host=xxx.xxx.x.xx:xxxxx; proto=https
  Content-Type: text/plain
}}

New Update:

I disabled debugging on just my code on the server implementation and discovered the following exception:

Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: 'Bad chunk size data.'

Any suggestions?

Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118

3 Answers3

0

this might not be a direct answer to your question, but i would like to suggest localtunnel. a very easy way to temporarily expose your local api so that you can test it either on emulator or even physical device. Have used this alot my self, as it is very convenient to just type a single line in terminal to start it.

Joachim Haglund
  • 775
  • 5
  • 15
  • Thanks for the link. Out of curiosity, is this tool any different from the Conveyor tool that I'm using? Hence, it seems they're both solving the same problem. – Scott Nimrod Oct 30 '19 at 12:43
  • 1
    they do solve the same issue indeed, however it seems like the problem you are having is related to Conveyor, so i figure witching to localtunnel might solve it. – Joachim Haglund Oct 30 '19 at 12:47
  • I was unsuccessful: https://github.com/localtunnel/localtunnel/issues/221 – Scott Nimrod Oct 30 '19 at 13:31
  • in that thread it seems the problem was firewall in the network, so perhaps that is your issue too? try using an open network, or share internet from your phone? – Joachim Haglund Oct 30 '19 at 13:36
0

The following reference solved my issue.

Infrastructure:

type GlobalHttpClient private () =

    static let mutable (httpClient:System.Net.Http.HttpClient) = null

    static member val Instance = httpClient with get,set

Xamarin.Android project:

using Android.Http;
using Android.Net;
using Javax.Net.Ssl;
using System.Net.Http;
using Xamarin.Android.Net;
using Xamarin.Forms;
using WebGatewaySupport;

[assembly: Dependency(typeof(HTTPClientHandlerCreationService_Android))]
namespace Android.Http
{
    public class HTTPClientHandlerCreationService_Android : IHTTPClientHandlerCreationService
    {
        public HttpClientHandler GetInsecureHandler()
        {
            return new IgnoreSSLClientHandler();
        }
    }

    internal class IgnoreSSLClientHandler : AndroidClientHandler
    {
        protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
        {
            return SSLCertificateSocketFactory.GetInsecure(1000, null);
        }

        protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
        {
            return new IgnoreSSLHostnameVerifier();
        }
    }

    internal class IgnoreSSLHostnameVerifier : Java.Lang.Object, IHostnameVerifier
    {
        public bool Verify(string hostname, ISSLSession session)
        {
            return true;
        }
    }
}

Xamarin.Forms App:

switch (Device.RuntimePlatform)
{
    case Device.Android:
        GlobalHttpClient.Instance = new HttpClient(DependencyService.Get<IHTTPClientHandlerCreationService>().GetInsecureHandler());
        break;

    default:
        ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        GlobalHttpClient.Instance = new HttpClient(new HttpClientHandler());
        break;
}

Client Gateway:

let postTo (baseAddress:string) (resource:string) (payload:Object) =

    GlobalHttpClient.Instance.BaseAddress <- Uri(baseAddress)
    let encoded = Uri.EscapeUriString(resource)

    let result  = GlobalHttpClient.Instance.PostAsJsonAsync(encoded, payload) |> toResult
    result
Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118
-2

Looks like you have a .NET Core Api. .NET Core does not have System.Web in Asp.NET. The HttpPost attribute and HttpGet attributes should come from Microsoft.AspNetCore.Mvc namespace which you have open.

Also since you are using the ApiController attribute model binding will just work as long as you bind to a model and not just a json string.

Create a model that you want the json to bind to and use that type for your parameter on Post and remove the FromBody attribute. Also if you do that you probably don't need newtonsoft.json.

open Microsoft.AspNetCore.Mvc

[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
    inherit ControllerBase()

    [<HttpPost>]
    member x.Post(thing:TypeOfThing) =
James Wong
  • 4,529
  • 4
  • 48
  • 65
awright18
  • 2,255
  • 2
  • 23
  • 22