【问题标题】:HttpClient ObjectDisposedException AndroidHttpClient ObjectDisposedException Android
【发布时间】:2018-06-22 15:32:22
【问题描述】:

当服务器关闭时,我正在测试对我的 API 的 HTTP 请求。 它应该收到一个错误响应,但相反,它返回 null 并且它给了我这个异常: System.ObjectDisposedException:无法访问已关闭的 Stream。

这仅在 Android 中发生,iOS 我收到错误响应。这是我的代码:

using (HttpClient client = new HttpClient())
{
    try
    {
        //pedido de token

        var loginInfo = new StringContent(JsonConvert.SerializeObject(userAuth).ToString(), Encoding.UTF8, "application/json");

        var requestToken = await client.PostAsync(URLs.url + URLs.getToken, loginInfo);
        var receiveToken = await requestToken.Content.ReadAsStringAsync();

它没有到达 ReadAsString,在 PostAsync 中抛出异常。

【问题讨论】:

  • 如果服务器关闭,您将不会收到错误响应。您将一无所获,因为服务器无法响应您的请求。
  • 由于服务器已关闭,因此无法访问任何内容,并且不会发送任何响应!所以这个应用真正在做的是什么都不读。
  • 我确实收到了回复。状态码为 200,并在 iOS 中返回带有错误的 HTML 页面。仅在 Android 中响应为空。
  • 如果您收到响应,那么您的服务器没有关闭。在您的 android 项目中,转到 properties -> android build,然后查看您的 HttpClient 实现是什么。我的是 AndroidClientHandler,但自从我设置它以来已经很久了,所以如果它不起作用,那么使用它的人比我更频繁地需要帮助你。
  • 我的也是AndroidClientHandler。我正在访问一个无法访问的网址“A”,因此它将我重定向到网址“B”。 iOS 似乎可以毫无问题地处理这个问题,但 Android 不是。

标签: c# xamarin xamarin.forms httpwebrequest


【解决方案1】:

不要丢弃HttpClient。它旨在被重用并处理多个同时请求。

以下是有关 HttpClient 工作原理的更多信息:https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

这是我用于 Xamarin.Forms 应用程序中所有 HttpClient 服务的通用实现:

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Http;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;

using Newtonsoft.Json;

using Xamarin.Forms;

namespace NameSpace
{
    public abstract class BaseHttpClientService
    {
        #region Constant Fields
        static readonly Lazy<JsonSerializer> _serializerHolder = new Lazy<JsonSerializer>();
        static readonly Lazy<HttpClient> _clientHolder = new Lazy<HttpClient>(() => CreateHttpClient(TimeSpan.FromSeconds(30)));
        #endregion

        #region Fields
        static int _networkIndicatorCount = 0;
        #endregion

        #region Events
        public static event EventHandler<string> HttpRequestFailed;
        #endregion

        #region Properties
        static HttpClient Client => _clientHolder.Value;
        static JsonSerializer Serializer => _serializerHolder.Value;
        #endregion

        #region Methods
        protected static async Task<T> GetObjectFromAPI<T>(string apiUrl)
        {
            using (var responseMessage = await GetObjectFromAPI(apiUrl).ConfigureAwait(false))
                return await DeserializeResponse<T>(responseMessage).ConfigureAwait(false);
        }

        protected static async Task<HttpResponseMessage> GetObjectFromAPI(string apiUrl)
        {
            try
            {
                UpdateActivityIndicatorStatus(true);

                return await Client.GetAsync(apiUrl).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                OnHttpRequestFailed(e.Message);
                Report(e);
                throw;
            }
            finally
            {
                UpdateActivityIndicatorStatus(false);
            }
        }

        protected static async Task<TResponse> PostObjectToAPI<TResponse, TRequest>(string apiUrl, TRequest requestData)
        {
            using (var responseMessage = await PostObjectToAPI(apiUrl, requestData).ConfigureAwait(false))
                return await DeserializeResponse<TResponse>(responseMessage).ConfigureAwait(false);
        }

        protected static Task<HttpResponseMessage> PostObjectToAPI<T>(string apiUrl, T requestData) => SendAsync(HttpMethod.Post, apiUrl, requestData);

        protected static async Task<TResponse> PutObjectToAPI<TResponse, TRequest>(string apiUrl, TRequest requestData)
        {
            using (var responseMessage = await PutObjectToAPI(apiUrl, requestData).ConfigureAwait(false))
                return await DeserializeResponse<TResponse>(responseMessage).ConfigureAwait(false);
        }

        protected static Task<HttpResponseMessage> PutObjectToAPI<T>(string apiUrl, T requestData) => SendAsync(HttpMethod.Put, apiUrl, requestData);

        protected static async Task<TResponse> PatchObjectToAPI<TResponse, TRequest>(string apiUrl, TRequest requestData)
        {
            using (var responseMessage = await PatchObjectToAPI(apiUrl, requestData).ConfigureAwait(false))
                return await DeserializeResponse<TResponse>(responseMessage).ConfigureAwait(false);
        }

        protected static Task<HttpResponseMessage> PatchObjectToAPI<T>(string apiUrl, T requestData) => SendAsync(new HttpMethod("PATCH"), apiUrl, requestData);

        protected static async Task<TResponse> DeleteObjectFromAPI<TResponse>(string apiUrl)
        {
            using (var responseMessage = await DeleteObjectFromAPI(apiUrl).ConfigureAwait(false))
                return await DeserializeResponse<TResponse>(responseMessage).ConfigureAwait(false);
        }

        protected static Task<HttpResponseMessage> DeleteObjectFromAPI(string apiUrl) => SendAsync<object>(HttpMethod.Delete, apiUrl);

        static HttpClient CreateHttpClient(TimeSpan timeout)
        {
            HttpClient client;
            switch (Device.RuntimePlatform)
            {
                case Device.iOS:
                case Device.Android:
                    client = new HttpClient();
                    break;
                default:
                    client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip });
                    break;
            }

            client.Timeout = timeout;
            client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));

            return client;
        }

        static async Task<HttpResponseMessage> SendAsync<T>(HttpMethod httpMethod, string apiUrl, T requestData = default)
        {
            using (var httpRequestMessage = await GetHttpRequestMessage(httpMethod, apiUrl, requestData).ConfigureAwait(false))
            {
                try
                {
                    UpdateActivityIndicatorStatus(true);

                    return await Client.SendAsync(httpRequestMessage).ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    OnHttpRequestFailed(e.Message);
                    Report(e);
                    throw;
                }
                finally
                {
                    UpdateActivityIndicatorStatus(false);
                }
            }
        }

        protected static void UpdateActivityIndicatorStatus(bool isActivityIndicatorDisplayed)
        {
            if (isActivityIndicatorDisplayed)
            {
                Device.BeginInvokeOnMainThread(() => Application.Current.MainPage.IsBusy = true);
                _networkIndicatorCount++;
            }
            else if (--_networkIndicatorCount <= 0)
            {
                Device.BeginInvokeOnMainThread(() => Application.Current.MainPage.IsBusy = false);
                _networkIndicatorCount = 0;
            }
        }

        static async ValueTask<HttpRequestMessage> GetHttpRequestMessage<T>(HttpMethod method, string apiUrl, T requestData = default)
        {
            var httpRequestMessage = new HttpRequestMessage(method, apiUrl);

            switch (requestData)
            {
                case T data when data.Equals(default(T)):
                    break;

                case Stream stream:
                    httpRequestMessage.Content = new StreamContent(stream);
                    httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                    break;

                default:
                    var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(requestData)).ConfigureAwait(false);
                    httpRequestMessage.Content = new StringContent(stringPayload, Encoding.UTF8, "application/json");
                    break;
            }

            return httpRequestMessage;
        }

        static async Task<T> DeserializeResponse<T>(HttpResponseMessage httpResponseMessage)
        {
            httpResponseMessage.EnsureSuccessStatusCode();

            try
            {
                using (var contentStream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false))
                using (var reader = new StreamReader(contentStream))
                using (var json = new JsonTextReader(reader))
                {
                    if (json is null)
                        return default;

                    return await Task.Run(() => Serializer.Deserialize<T>(json)).ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                Report(e);
                throw;
            }
        }

        static void OnHttpRequestFailed(string message) => HttpRequestFailed?.Invoke(null, message);

        static void Report(Exception e, [CallerMemberName]string callerMemberName = "") => Debug.WriteLine(e.Message);
        #endregion
    }
}

【讨论】:

  • 我尝试删除 using 语句(处理 dispose),但仍然出现异常。
  • 如果你不处理 HttpClient,你应该得到一个不同的异常,这些都很好。这只是 HttpClient 让您知道连接失败,这是因为您的服务器已禁用。您得到的异常应该是 System.Net.WebException、Java.Net.ConnectException、Java.Net.ConnectException 或 Java.IO.IOException,具体取决于服务器的状态。
【解决方案2】:

我遇到了同样的问题(它在 UWP 中运行良好,但在 Android 上出现此错误)。 请查看此链接问题,了解为我解决了什么问题:HttpClient.SendAsync throws ObjectDisposedException on Xamarin.Forms Android but not on UWP

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-24
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2023-03-30
    • 2012-11-26
    • 1970-01-01
    • 2012-06-03
    相关资源
    最近更新 更多