Android 模拟器中的应用程序 运行 无法对本地主机执行 HTTP Post
App running in Android Emulator fails to perform an HTTP Post to localhost
我无法在 Android 模拟器中使用应用 运行ning 执行 HTTP Post .
{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 }}
设置:
- 我正在使用 Conveyor by Keyoti
生成的 IP 地址
- 我在Conveyor by Keyoti
要求的模拟器上安装了安全证书
- 我用 System.Web.Http.HttpPost
换掉了 Microsoft.AspNetCore.Mvc.HttpPost 属性
模拟器:
- 成功:HTTP 获取
- 失败:HTTP Post
集成测试:
- 成功:HTTP Post(使用相同的端点)
代码:
我编写了一个调用相同 HTTP Post 实现的自动化测试。
因为我通过自动化测试在笔记本电脑上成功执行了相同的代码,所以我认为实际代码不是问题所在:
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) =
...
总结:
总而言之,我已将环境隔离到 Android 模拟器,而不是我的笔记本电脑。因此,模拟器可以成功触发 HTTP Get。但是,它无法执行 HTTP Post,即使我的笔记本电脑设备可以执行这两项操作。
更新:
我应用了此 Xamarin Android ASP.Net Core WebAPI document 中的指导。
具体来说,我在 Android 模拟器上安装了另一个安全证书。
然后我能够在 Android 模拟器上观察到 HTTP Get。
但是,我继续收到 HTTP 错误 Post。
OperationCanceledException
物理设备:
如果我 运行 来自物理 android 设备的应用程序,我会观察到以下内容:
{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
}}
新更新:
我在服务器实现上仅对我的代码禁用了调试并发现了以下异常:
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: 'Bad chunk size data.'
有什么建议吗?
看起来您有一个 .NET Core Api。 .NET Core 在 Asp.NET 中没有 System.Web
。 HttpPost
属性和 HttpGet
属性应该来自您打开的 Microsoft.AspNetCore.Mvc
命名空间。
此外,由于您使用的是 ApiController
属性,只要您绑定到模型而不仅仅是 json 字符串,模型绑定就可以正常工作。
创建一个您希望 json 绑定的模型,并将该类型用于 Post
上的参数并删除 FromBody
属性。此外,如果您这样做,您可能不需要 newtonsoft.json
。
open Microsoft.AspNetCore.Mvc
[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
inherit ControllerBase()
[<HttpPost>]
member x.Post(thing:TypeOfThing) =
这可能不是您问题的直接答案,但我想建议
localtunnel。一种临时公开本地 api 的非常简单的方法,以便您可以在模拟器甚至物理设备上对其进行测试。我自己用过很多,因为只需在终端中键入一行即可启动它非常方便。
以下 解决了我的问题。
基础设施:
type GlobalHttpClient private () =
static let mutable (httpClient:System.Net.Http.HttpClient) = null
static member val Instance = httpClient with get,set
Xamarin.Android 项目:
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 应用程序:
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;
}
客户端网关:
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
我无法在 Android 模拟器中使用应用 运行ning 执行 HTTP Post .
{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 }}
设置:
- 我正在使用 Conveyor by Keyoti 生成的 IP 地址
- 我在Conveyor by Keyoti 要求的模拟器上安装了安全证书
- 我用 System.Web.Http.HttpPost 换掉了 Microsoft.AspNetCore.Mvc.HttpPost 属性
模拟器:
- 成功:HTTP 获取
- 失败:HTTP Post
集成测试:
- 成功:HTTP Post(使用相同的端点)
代码:
我编写了一个调用相同 HTTP Post 实现的自动化测试。 因为我通过自动化测试在笔记本电脑上成功执行了相同的代码,所以我认为实际代码不是问题所在:
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) =
...
总结:
总而言之,我已将环境隔离到 Android 模拟器,而不是我的笔记本电脑。因此,模拟器可以成功触发 HTTP Get。但是,它无法执行 HTTP Post,即使我的笔记本电脑设备可以执行这两项操作。
更新:
我应用了此 Xamarin Android ASP.Net Core WebAPI document 中的指导。
具体来说,我在 Android 模拟器上安装了另一个安全证书。
然后我能够在 Android 模拟器上观察到 HTTP Get。
但是,我继续收到 HTTP 错误 Post。
OperationCanceledException
物理设备:
如果我 运行 来自物理 android 设备的应用程序,我会观察到以下内容:
{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
}}
新更新:
我在服务器实现上仅对我的代码禁用了调试并发现了以下异常:
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: 'Bad chunk size data.'
有什么建议吗?
看起来您有一个 .NET Core Api。 .NET Core 在 Asp.NET 中没有 System.Web
。 HttpPost
属性和 HttpGet
属性应该来自您打开的 Microsoft.AspNetCore.Mvc
命名空间。
此外,由于您使用的是 ApiController
属性,只要您绑定到模型而不仅仅是 json 字符串,模型绑定就可以正常工作。
创建一个您希望 json 绑定的模型,并将该类型用于 Post
上的参数并删除 FromBody
属性。此外,如果您这样做,您可能不需要 newtonsoft.json
。
open Microsoft.AspNetCore.Mvc
[<ApiController>]
[<Route("api/[controller]")>]
type RegisterController () =
inherit ControllerBase()
[<HttpPost>]
member x.Post(thing:TypeOfThing) =
这可能不是您问题的直接答案,但我想建议 localtunnel。一种临时公开本地 api 的非常简单的方法,以便您可以在模拟器甚至物理设备上对其进行测试。我自己用过很多,因为只需在终端中键入一行即可启动它非常方便。
以下
基础设施:
type GlobalHttpClient private () =
static let mutable (httpClient:System.Net.Http.HttpClient) = null
static member val Instance = httpClient with get,set
Xamarin.Android 项目:
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 应用程序:
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;
}
客户端网关:
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