【问题标题】:App running in Android Emulator fails to perform an HTTP Post to localhost在 Android Emulator 中运行的应用程序无法向 localhost 执行 HTTP Post
【发布时间】:2019-10-23 01:57:54
【问题描述】:

我无法使用在 Android 模拟器中运行的应用执行 HTTP Post

{StatusCode: 400, ReasonPhrase: 'Bad Request', 版本: 1.1, 内容: System.Net.Http.HttpConnection+HttpConnectionResponseContent,标题: { 服务器:Microsoft-HTTPAPI/2.0 日期:2019 年 10 月 23 日,星期三 00:58:01 GMT 连接:关闭转发:host=XXX.XXX.X.XX:XXXXX; proto=https 内容类型:文本/html; charset=us-ascii
内容长度:374 }}

设置:

  • 我使用的是Conveyor by Keyoti生成的IP地址
  • 我在模拟器上安装了Conveyor by Keyoti要求的安全证书
  • 我用 System.Web.Http.HttpPost 替换了 Microsoft.AspNetCore.Mvc.HttpPost 属性

模拟器:

  • 成功:HTTP 获取
  • 失败:HTTP 发布

集成测试:

  • 成功: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 Emulator 上观察到 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.'

有什么建议吗?

【问题讨论】:

  • 您是否将文件上传到服务器?还是只是简单的内容?

标签: xamarin xamarin.android android-emulator asp.net-core-webapi


【解决方案1】:

这可能不是您问题的直接答案,但我想建议 localtunnel。一种临时公开本地 api 的非常简单的方法,以便您可以在模拟器甚至物理设备上对其进行测试。我自己用过很多次,因为在终端中输入一行就可以启动它非常方便。

【讨论】:

  • 感谢您的链接。出于好奇,这个工具与我使用的 Conveyor 工具有什么不同吗?因此,他们似乎都在解决同一个问题。
  • 他们确实解决了同样的问题,但是您遇到的问题似乎与 Conveyor 有关,所以我认为使用 localtunnel 可能会解决它。
  • 在那个线程中,问题似乎是网络中的防火墙,所以也许这也是你的问题?尝试使用开放网络,还是通过手机共享互联网?
【解决方案2】:

以下reference 解决了我的问题。

基础设施:

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

【讨论】:

    【解决方案3】:

    看起来你有一个 .NET Core Api。 .NET Core 在 Asp.NET 中没有 System.WebHttpPost 属性和 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) =
    

    【讨论】:

    • 谢谢亚当。我尝试了您提供的建议。然而,我没有成功。使用该实现,我的自动化测试也没有成功。
    • 这个代码在 github Scott 我以后可以看看吗?
    猜你喜欢
    • 2020-04-30
    • 2016-04-17
    • 2019-11-24
    • 1970-01-01
    • 2021-03-05
    • 2014-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多