问题
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?
回答1:
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.
回答2:
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
回答3:
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) =
来源:https://stackoverflow.com/questions/58514409/app-running-in-android-emulator-fails-to-perform-an-http-post-to-localhost