[英]RestSharp returns IRestResponse.StatusCode == 0 to Polly onRetry
我圍繞 RestSharp 客戶端編寫了一個相當簡單的包裝器,使用 Polly 向它添加了一些重試邏輯。 我設置了 Fiddler 來測試它並模擬了一些“不良”響應。
我的問題是在onRetry委托,對result.Result.StatusCode
位似乎有時記錄為0,而不是實際的糟糕狀態代碼(在我的一些testings 502)。
但是,通過我的單元測試,它似乎工作得很好。 也許這里的比賽條件?
知道為什么會這樣嗎?
提前致謝。
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Polly;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Net;
namespace FundsAFE.Graphite
{
public class RequestExecutor
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private IRestClient client;
private IRestRequest request;
private Policy<IRestResponse> retryPolicy;
public IRestResponse LastErrorResponse { get; set; }
private static readonly List<HttpStatusCode> invalidStatusCodes = new List<HttpStatusCode> {
HttpStatusCode.BadGateway,
HttpStatusCode.Unauthorized,
HttpStatusCode.InternalServerError,
HttpStatusCode.RequestTimeout,
HttpStatusCode.BadRequest,
HttpStatusCode.Forbidden,
HttpStatusCode.GatewayTimeout
};
public RequestExecutor(IRestClient client, IRestRequest request)
{
this.client = client;
this.request = request;
}
public IRestResponse Execute(int retryCount, int delay)
{
retryPolicy = Policy
.HandleResult<IRestResponse>(resp => invalidStatusCodes.Contains(resp.StatusCode) || !IsValidJson(resp))
.WaitAndRetry(retryCount, i => TimeSpan.FromMilliseconds(delay), (result, timeSpan, currentRetryCount, context) =>
{
//Status code here is sometimes 0???
logger.Error($"Request failed with {result.Result.StatusCode}. Waiting {timeSpan} before next retry. Retry attempt {currentRetryCount}");
LastErrorResponse = result.Result;
});
var policyResponse = retryPolicy.ExecuteAndCapture(() =>
{
var url = client.BuildUri(request);
logger.Debug(url.ToString());
var response = client.Execute(request);
return response;
});
if(policyResponse.Result != null)
{
return policyResponse.Result;
} else
{
return LastErrorResponse;
}
}
public static bool IsValidJson(IRestResponse response)
{
if (response.Content.Length == 0)
{
//Empty response treated as invalid
return false;
}
try
{
var parsed = JObject.Parse(response.Content);
}
catch (JsonReaderException e)
{
//Will catch any mallformed json
return false;
}
return true;
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FundsAFE.Graphite;
using Moq;
using RestSharp;
using System.Net;
using FluentAssertions;
using System;
using FluentAssertions.Extensions;
namespace FundsAFE.Test.Moq
{
[TestClass]
public class MoqUnitTestRequest
{
public Mock<IRestClient> CreateMockClientWithStatusCodeAndContent(HttpStatusCode code, string content)
{
Mock<IRestClient> mockClient = new Mock<IRestClient>();
mockClient.Setup(c => c.Execute(It.IsAny<IRestRequest>())).Returns(
new RestResponse
{
Content = content,
StatusCode = code
}
);
mockClient.Setup(c => c.BuildUri(It.IsAny<IRestRequest>())).Returns(
new Uri("http://fake.fake")
);
return mockClient;
}
[DataTestMethod]
[DataRow(HttpStatusCode.BadGateway)]
[DataRow(HttpStatusCode.Unauthorized)]
[DataRow(HttpStatusCode.InternalServerError)]
[DataRow(HttpStatusCode.RequestTimeout)]
[DataRow(HttpStatusCode.BadRequest)]
[DataRow(HttpStatusCode.Forbidden)]
[DataRow(HttpStatusCode.GatewayTimeout)]
public void TestBadStatusCodesAndRetry(HttpStatusCode httpStatusCode) {
//Arrange
Mock<IRestRequest> mockRequest = new Mock<IRestRequest>();
Mock<IRestClient> mockClient = CreateMockClientWithStatusCodeAndContent(httpStatusCode, "fakecontent");
RequestExecutor requestExecutor = new RequestExecutor(mockClient.Object, mockRequest.Object);
int retries = 10;
int delay = 50;
int totalWaitTime = (retries * delay) - 10; //10ms error margin
//Act and Verify
var response = requestExecutor.Execute(retryCount: retries, delay: 101);
mockClient.Verify(x => x.Execute(It.IsAny<IRestRequest>()), Times.Exactly(retries + 1)); //1st failed attempt + 10 retries = 11
//Assert
requestExecutor.ExecutionTimeOf(re => re.Execute(retries, delay)).Should().BeGreaterOrEqualTo(totalWaitTime.Milliseconds());
response.Should().NotBeNull();
response.StatusCode.Should().Be(httpStatusCode);
requestExecutor.LastErrorResponse.StatusCode.Should().Be(httpStatusCode);
}
[DataTestMethod]
//Empty content
[DataRow("")]
//Missing closing quote
[DataRow("{\"fruit\": \"Apple,\"size\": \"Large\",\"color\": \"Red\"}")]
//Missing angle bracket
[DataRow("\"q1\": {\"question\": \"Which one is correct team name in NBA?\",\"options\": \"New York Bulls\",\"Los Angeles Kings\",\"Golden State Warriros\",\"Huston Rocket\"],\"answer\": \"Huston Rocket\"}")]
//Missing curly bracket
[DataRow("\"sport\": {\"q1\": {\"question\": \"Which one is correct team name in NBA?\",\"options\": \"New York Bulls\",\"Los Angeles Kings\",\"Golden State Warriros\",\"Huston Rocket\"],\"answer\": \"Huston Rocket\"}")]
public void TestBadContentRetries(string content)
{
//Arrange
Mock<IRestRequest> mockRequest = new Mock<IRestRequest>();
Mock<IRestClient> mockClient = CreateMockClientWithStatusCodeAndContent(HttpStatusCode.OK, content);
RequestExecutor requestExecutor = new RequestExecutor(mockClient.Object, mockRequest.Object);
int retries = 10;
int delay = 50;
int totalWaitTime = (retries * delay) - 10; //10ms error margin
//Act and Verify
var response = requestExecutor.Execute(retryCount: retries, delay: delay);
mockClient.Verify(x => x.Execute(It.IsAny<IRestRequest>()), Times.Exactly(retries + 1)); //1st failed attempt + 10 retries = 11
//Assert
requestExecutor.ExecutionTimeOf(re => re.Execute(retries, delay)).Should().BeGreaterOrEqualTo(totalWaitTime.Milliseconds());
response.Should().NotBeNull();
}
}
}
好像有情況下(例如它看起來從RESTsharp源代碼一些例外情況),其中RESTsharp很可能會回到你的IRestResponse
與resp.StatusCode == 0
。
onRetry
的日志代碼可能應該檢查onRetry
上更廣泛的狀態屬性IRestResponse
,而不僅僅是IRestResponse.StatusCode
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.