зеркало из https://github.com/dotnet/extensions.git
[automated] Merge branch 'main' => 'dev' (#5461)
This commit is contained in:
Коммит
39383ee9da
|
@ -20,7 +20,9 @@ internal sealed class RequestMessageSnapshotStrategy : ResilienceStrategy
|
|||
ResilienceContext context,
|
||||
TState state)
|
||||
{
|
||||
if (!context.Properties.TryGetValue(ResilienceKeys.RequestMessage, out var request) || request is null)
|
||||
HttpRequestMessage? request = context.GetRequestMessage();
|
||||
|
||||
if (request is null)
|
||||
{
|
||||
Throw.InvalidOperationException("The HTTP request message was not found in the resilience context.");
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ public static partial class ResilienceHttpClientBuilderExtensions
|
|||
requestMessage.SetResilienceContext(args.ActionContext);
|
||||
|
||||
// replace the request message
|
||||
args.ActionContext.Properties.Set(ResilienceKeys.RequestMessage, requestMessage);
|
||||
args.ActionContext.SetRequestMessage(requestMessage);
|
||||
|
||||
if (args.PrimaryContext.Properties.TryGetValue(ResilienceKeys.RoutingStrategy, out var routingPipeline))
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Http.Resilience.Internal;
|
|||
|
||||
internal static class ResilienceKeys
|
||||
{
|
||||
public static readonly ResiliencePropertyKey<HttpRequestMessage> RequestMessage = new("Resilience.Http.RequestMessage");
|
||||
public static readonly ResiliencePropertyKey<HttpRequestMessage?> RequestMessage = new("Resilience.Http.RequestMessage");
|
||||
|
||||
public static readonly ResiliencePropertyKey<RequestRoutingStrategy> RoutingStrategy = new("Resilience.Http.RequestRoutingStrategy");
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Http.Resilience.Internal;
|
||||
using Microsoft.Shared.DiagnosticIds;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
using Polly;
|
||||
|
||||
namespace Polly;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utility methods for working with <see cref="ResilienceContext"/>.
|
||||
/// </summary>
|
||||
[Experimental(diagnosticId: DiagnosticIds.Experiments.Resilience, UrlFormat = DiagnosticIds.UrlFormat)]
|
||||
public static class HttpResilienceContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the request message from the <see cref="ResilienceContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The resilience context.</param>
|
||||
/// <returns>
|
||||
/// The request message.
|
||||
/// If the request message is not present in the <see cref="ResilienceContext"/> the method returns <see langword="null"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="context"/> is <see langword="null"/>.</exception>
|
||||
public static HttpRequestMessage? GetRequestMessage(this ResilienceContext context)
|
||||
{
|
||||
_ = Throw.IfNull(context);
|
||||
return context.Properties.GetValue(ResilienceKeys.RequestMessage, default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the request message on the <see cref="ResilienceContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The resilience context.</param>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="context"/> is <see langword="null"/>.</exception>
|
||||
public static void SetRequestMessage(this ResilienceContext context, HttpRequestMessage? requestMessage)
|
||||
{
|
||||
_ = Throw.IfNull(context);
|
||||
context.Properties.Set(ResilienceKeys.RequestMessage, requestMessage);
|
||||
}
|
||||
}
|
|
@ -58,7 +58,7 @@ public class ResilienceHandler : DelegatingHandler
|
|||
|
||||
ResilienceContext context = GetOrSetResilienceContext(request, cancellationToken, out bool created);
|
||||
TrySetRequestMetadata(context, request);
|
||||
SetRequestMessage(context, request);
|
||||
context.SetRequestMessage(request);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ public class ResilienceHandler : DelegatingHandler
|
|||
|
||||
ResilienceContext context = GetOrSetResilienceContext(request, cancellationToken, out bool created);
|
||||
TrySetRequestMetadata(context, request);
|
||||
SetRequestMessage(context, request);
|
||||
context.SetRequestMessage(request);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -165,11 +165,8 @@ public class ResilienceHandler : DelegatingHandler
|
|||
}
|
||||
}
|
||||
|
||||
private static void SetRequestMessage(ResilienceContext context, HttpRequestMessage request)
|
||||
=> context.Properties.Set(ResilienceKeys.RequestMessage, request);
|
||||
|
||||
private static HttpRequestMessage GetRequestMessage(ResilienceContext context, HttpRequestMessage request)
|
||||
=> context.Properties.GetValue(ResilienceKeys.RequestMessage, request);
|
||||
=> context.GetRequestMessage() ?? request;
|
||||
|
||||
private static void RestoreResilienceContext(ResilienceContext context, HttpRequestMessage request, bool created)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Http.Resilience.Internal;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
@ -26,7 +27,9 @@ internal sealed class RoutingResilienceStrategy : ResilienceStrategy
|
|||
ResilienceContext context,
|
||||
TState state)
|
||||
{
|
||||
if (!context.Properties.TryGetValue(ResilienceKeys.RequestMessage, out var request))
|
||||
HttpRequestMessage? request = context.GetRequestMessage();
|
||||
|
||||
if (request is null)
|
||||
{
|
||||
Throw.InvalidOperationException("The HTTP request message was not found in the resilience context.");
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public class RequestMessageSnapshotStrategyTests
|
|||
var strategy = Create();
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
using var request = new HttpRequestMessage();
|
||||
context.Properties.Set(ResilienceKeys.RequestMessage, request);
|
||||
context.SetRequestMessage(request);
|
||||
|
||||
using var response = await strategy.ExecuteAsync(
|
||||
context =>
|
||||
|
@ -39,5 +39,15 @@ public class RequestMessageSnapshotStrategyTests
|
|||
strategy.Invoking(s => s.Execute(() => { })).Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExecuteAsync_RequestMessageIsNull_Throws()
|
||||
{
|
||||
var strategy = Create();
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
context.SetRequestMessage(null);
|
||||
|
||||
strategy.Invoking(s => s.Execute(_ => { }, context)).Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
|
||||
private static ResiliencePipeline Create() => new ResiliencePipelineBuilder().AddStrategy(_ => new RequestMessageSnapshotStrategy(), Mock.Of<ResilienceStrategyOptions>()).Build();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Http.Resilience.Internal;
|
||||
using Polly;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.Http.Resilience.Test.Resilience;
|
||||
|
||||
public class HttpResilienceContextExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetRequestMessage_ResilienceContextIsNull_Throws()
|
||||
{
|
||||
ResilienceContext context = null!;
|
||||
Assert.Throws<ArgumentNullException>(context.GetRequestMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRequestMessage_RequestMessageIsMissing_ReturnsNull()
|
||||
{
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
|
||||
Assert.Null(context.GetRequestMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRequestMessage_RequestMessageIsNull_ReturnsNull()
|
||||
{
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
context.Properties.Set(ResilienceKeys.RequestMessage, null);
|
||||
|
||||
Assert.Null(context.GetRequestMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRequestMessage_RequestMessageIsPresent_ReturnsRequestMessage()
|
||||
{
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
using var request = new HttpRequestMessage();
|
||||
context.Properties.Set(ResilienceKeys.RequestMessage, request);
|
||||
|
||||
Assert.Same(request, context.GetRequestMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetRequestMessage_ResilienceContextIsNull_Throws()
|
||||
{
|
||||
ResilienceContext context = null!;
|
||||
using var request = new HttpRequestMessage();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => context.SetRequestMessage(request));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetRequestMessage_RequestMessageIsNull_SetsNullRequestMessage()
|
||||
{
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
context.SetRequestMessage(null);
|
||||
|
||||
Assert.True(context.Properties.TryGetValue(ResilienceKeys.RequestMessage, out HttpRequestMessage? request));
|
||||
Assert.Null(request);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetRequestMessage_RequestMessageIsNotNull_SetsRequestMessage()
|
||||
{
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
using var request = new HttpRequestMessage();
|
||||
context.SetRequestMessage(request);
|
||||
|
||||
Assert.True(context.Properties.TryGetValue(ResilienceKeys.RequestMessage, out HttpRequestMessage? actualRequest));
|
||||
Assert.Same(request, actualRequest);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Http.Diagnostics;
|
||||
using Microsoft.Extensions.Http.Resilience.Internal;
|
||||
using Microsoft.Extensions.Http.Resilience.Test.Helpers;
|
||||
using Polly;
|
||||
using Xunit;
|
||||
|
@ -108,7 +107,7 @@ public class ResilienceHandlerTest
|
|||
handler.InnerHandler = new TestHandlerStub((r, _) =>
|
||||
{
|
||||
r.GetResilienceContext().Should().NotBeNull();
|
||||
r.GetResilienceContext()!.Properties.GetValue(ResilienceKeys.RequestMessage, null!).Should().BeSameAs(r);
|
||||
r.GetResilienceContext()!.GetRequestMessage().Should().BeSameAs(r);
|
||||
|
||||
return Task.FromResult(new HttpResponseMessage { StatusCode = HttpStatusCode.Created });
|
||||
});
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Http.Resilience.Internal;
|
||||
using Microsoft.Extensions.Http.Resilience.Routing.Internal;
|
||||
using Moq;
|
||||
using Polly;
|
||||
|
@ -22,6 +21,16 @@ public class RoutingResilienceStrategyTests
|
|||
strategy.Invoking(s => s.Execute(() => { })).Should().Throw<InvalidOperationException>().WithMessage("The HTTP request message was not found in the resilience context.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequestMessageIsNull_Throws()
|
||||
{
|
||||
var strategy = Create(() => Mock.Of<RequestRoutingStrategy>());
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
context.SetRequestMessage(null);
|
||||
|
||||
strategy.Invoking(s => s.Execute(_ => { }, context)).Should().Throw<InvalidOperationException>().WithMessage("The HTTP request message was not found in the resilience context.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoRoutingProvider_Ok()
|
||||
{
|
||||
|
@ -29,7 +38,7 @@ public class RoutingResilienceStrategyTests
|
|||
|
||||
var strategy = Create(null);
|
||||
var context = ResilienceContextPool.Shared.Get();
|
||||
context.Properties.Set(ResilienceKeys.RequestMessage, request);
|
||||
context.SetRequestMessage(request);
|
||||
|
||||
strategy.Invoking(s => s.Execute(_ => { }, context)).Should().NotThrow();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче