【问题标题】:RestSharp "Error getting response stream (ReadAsync): ReceiveFailure Value cannot be null. Parameter name: src"RestSharp“获取响应流时出错(ReadAsync):ReceiveFailure 值不能为空。参数名称:src”
【发布时间】:2018-09-19 09:31:38
【问题描述】:

大家好,我正在尝试使用 RestSharp 登录我的 xamarin api,如果身份验证有效,API 应该返回状态码 200 OK,如果身份验证失败(密码错误)和其他代码,则返回状态码,具体取决于案例场景,但是当身份验证通过(状态代码 200 ok)时,我在所有其他案例旁得到状态代码 0,下面的源代码是我的实现方式

 //payload am sending to the api
   RequestPayload res = new RequestPayload();
   res.appid = appid;
   res.data = data;
   res.method = "Login";

   //convert to json object
   var MySerializedObject =  JsonConvert.SerializeObject(res);
   string APIUrl = ""http://142.168.20.15:8021/RouteTask";

   //create client
   RestClient client = new RestClient(APIUrl);

   //create request
   RestRequest request = new RestRequest(Method.POST);

   // set request headeer
   request.AddHeader("Content-Type", "application/x-www-form-urlencoded");

   //request.AddJsonBody(MySerializedObject); --i have also tried this

   request.AddParameter("application/json", MySerializedObject, ParameterType.RequestBody);
   request.JsonSerializer.ContentType = "application/json; charset=utf-8";
   request.AddParameter("RequestSource", "Web", "application/json", ParameterType.QueryString);
   client.Timeout = 2000000;
   var response =  client.Execute(request); // where the issue appears
   //RestResponse response =  client.Execute(request); // i have tried this
   //IRestResponse response =  client.Execute(request); // i have tried this
    if (response.IsSuccessful)
        {
         //use response data
        }

在所有场景中,它返回一个 StatusCode: 0, Content-Type: , Content-Length: 0) 和 errorMessage

"获取响应流时出错 (ReadAsync):ReceiveFailure 值 不能为空。参数名称:src"

下面的截图表明api调用失败的时间 验证有效时收到响应

【问题讨论】:

  • 这里有一个类似的问题,可以参考here
  • MonkeySteve,我已经看到了这个问题,但是,解决方案对我不起作用,如果服务器发送任何其他响应,我的问题就会出现 200 OK
  • 我遇到了类似的 401 错误问题。如果您为所有异常打开中断,您可以看到在 RestSharp 中抛出了几个,但它们没有冒泡,并且状态代码最终为零。在我的情况下,返回 401 的响应是纯文本“过期”而不是 json,这让我相信它与解析器有关,但它甚至从未命中 OnBeforeDeserialization 方法,因此似乎排除了解析错误.
  • 从服务器指示身份验证过期的初始 511 开始,随后的 401 指示我有一个错误的密钥(因为我不知道 511 来了并且需要更新)我得到了同样的错误情况。但是我确实点击了 OnBeforeDeserialization 方法,并且已经抛出异常并且所有响应数据都丢失了,包括标题和状态代码......

标签: c# api xamarin.forms restsharp


【解决方案1】:

我终于找到了解决方法。忍受冗长的回应。

标签提到了 Xamarin,这也是我正在使用的 - 特别是 iOS。我认为这实际上可能是 Mono 的一个错误,但我没有采取那么远来确认。

问题在于复制响应缓冲区的默认方式。在 RestSharp 代码中,这是通过 MiscExtensions.cs 中名为 ReadAsBytes 的扩展方法完成的。似乎对于某些响应缓冲区,对 Stream.Read 方法的调用失败。发生这种情况时,异常会导致 RestSharp “快捷方式”处理响应的其余部分,因此状态代码永远不会被填写,因为它发生在调用 ReadAsBytes 之后。

好消息是 RestSharp 确实提供了一种方法,可以用您自己的方法替换对 ReadAsBytes 的调用。这是通过 IRestRequest 对象上的 ResponseWriter 属性完成的。如果它定义了一个函数,它将绕过 ReadAsBytes 调用并调用你给它的函数。问题是,这被定义为一个动作,你没有得到完整响应对象的副本,所以它有点没用。相反,您必须使用 AdvancedResponseWriter 属性。这包括响应对象和响应流。但是您仍然必须设置 ResponseWriter 属性,否则它不会绕过默认处理程序,您仍然会收到错误。

好的,那么你是如何做到这一点的呢?我最终将它实现为 RestClient 的包装器,因此我不必到处实现代码。这是基本设置:

public class MyRestClient : RestClient
{
    public MyRestClient(string baseUrl) : base(baseUrl)
    { }

    public override IRestResponse Execute(IRestRequest request)
    {
        request.ResponseWriter = s => { };
        request.AdvancedResponseWriter = (input, response) => response.RawBytes = ReadAsBytes(input);

        return base.Execute(request);
    }

    private static byte[] ReadAsBytes(Stream input)
    {
        var buffer = new byte[16 * 1024];

        using (var ms = new MemoryStream())
        {
            int read;
            try
            {
                while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                { ms.Write(buffer, 0, read); }

                return ms.ToArray();
            }
            catch (WebException ex)
            { return Encoding.UTF8.GetBytes(ex.Message); }
        };
    }
}

ReadAsBytes 方法实际上只是 RestSharp ReadAsBytes 方法的复制/粘贴,并添加了一个 try/catch。如果失败,它将异常原因返回到响应缓冲区。这可能是也可能不是您想要的,因此请根据需要进行修改。您可能还需要为 Execute 覆盖其他方法,但在我的情况下,这是我们唯一使用的方法,因此已经足够了。

到目前为止,这似乎对我有用。也许如果有人有野心,他们可以一直追踪到 Mono,看看它不喜欢流的什么地方,但我现在没有时间。

祝你好运!

【讨论】:

  • 你好史蒂夫我已经尝试过你的解决方案,它不起作用,而是我收到错误“获取响应流时出错(ReadAsync):ReceiveFailure 值不能为空。参数名称:src”在内容中仍然不是我所期待的。
  • 它仍然会抛出错误,但 catch 将允许它继续并完成填充响应对象,因此您至少可以取回状态码。也发现了这个。 github.com/mono/mono/issues/9511 看来这确实是 Mono 的问题。
  • 我遇到了一些额外的场景,这些场景没有通过 ResponseWriter 并且仍然最终返回零状态代码,所以显然这不是一个完整的解决方案。我还没有时间进行追踪,看看我是否可以找到解决方法。我看到的一个是“连接被对等方关闭”错误。
  • 嗨史蒂夫,我使用了一个替代的 .net http 客户端来实现结果,我在下面的答案中有实现,谢谢你的协助
【解决方案2】:

好的,所以在玩了一会儿 RestSharp 之后,我意识到正如 @steve_In_Co 前面提到的那样,MONO 存在兼容性问题(我们认为这是一个错误),所以我使用 .Net HTTP 库以基本方式进行了处理它对我有用,所以如果有人还在寻找出路,请在下面找到有效的 .net http 实现代码。

//payload am sending to the api 
   RequestPayload res = new RequestPayload();
   res.appid = appid;
   res.data = data;
   res.method = "Login";

   //convert to json object
   var MySerializedObject =  JsonConvert.SerializeObject(res);

   string APIUrl = ""http://142.168.20.15:8021/RouteTask";

   //create basic .net http client
   HttpClient client = new HttpClient();
   client.BaseAddress = new Uri(APIUrl);

   // this was required in the header of my request, 
   // you may not need this, or you may need to adjust parameter
   //("RequestSource","Web") or you own custom headers
   client.DefaultRequestHeaders.Add("RequestSource", "Web");
    // this class is custom, you can leave it out
   connectionService = new ConnectionService();
  //check for internet connection on users device before making the call
  if (connectionService.IsConnected)
    {
       //make the call to the api
        HttpResponseMessage response = await 
        client.PostAsJsonAsync(ApiConstants.APIDefault, res);
        if (response.IsSuccessStatusCode)
            {
                string o = response.Content.ReadAsStringAsync().Result;
                dynamic payload = JsonConvert.DeserializeObject(o);
                string msg = payload["valMessage"];
                resp.a = true;
                resp.msg = payload["responseDescription"];
            }
        else
            {
                string o = response.Content.ReadAsStringAsync().Result;
                dynamic payload = JsonConvert.DeserializeObject(o);
                resp.a = false;
                resp.msg = payload["response"];
            }
    }

【讨论】:

  • 我根本看不出这是如何工作的,正如您在原始问题中所建议的那样,当内容为空(len = 0)并且在您的解决方案中您反序列化空然后继续时会出现问题用它。在我看来,你厌倦了反序列化一个空值并且它适当地失败了。而且您的解决方案甚至比OP还差。请指教
  • 你好 axa,我的兴趣在于响应的内容,即(Response.Content),但如果我的登录请求失败并且我得到一个不同于 200 OK 的状态码,那么 Response.Content 将是null (我不会得到后端程序员打算作为失败原因的原始响应)但是使用普通的 .net httpClient 如果我的请求因任何原因失败,我能够读取适当的响应(也许你打算在史蒂夫中发布这个在 CO 的回答中,因为您的担忧将适用于此)请注意我的解决方案 100% 有效
  • 也许我只是误解了,因为我看到如果没有响应。IsSuccessStuatusCode 它仍然会读取内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多