mirror of https://github.com/abpframework/abp.git
Browse Source
- Treat QUERY as a safe method like GET: excluded from audit logging and non-transactional UOW - Add HTTP verb constants and Is* helpers to HttpMethodHelperpull/25797/head
21 changed files with 336 additions and 32 deletions
@ -0,0 +1,32 @@ |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Uow; |
|||
|
|||
public class AspNetCoreUnitOfWorkTransactionBehaviourProvider_Tests |
|||
{ |
|||
private static AspNetCoreUnitOfWorkTransactionBehaviourProvider CreateProvider(string method) |
|||
{ |
|||
var httpContext = new DefaultHttpContext(); |
|||
httpContext.Request.Method = method; |
|||
|
|||
return new AspNetCoreUnitOfWorkTransactionBehaviourProvider( |
|||
new HttpContextAccessor { HttpContext = httpContext }, |
|||
Microsoft.Extensions.Options.Options.Create(new AspNetCoreUnitOfWorkTransactionBehaviourProviderOptions())); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("GET", false)] |
|||
[InlineData("QUERY", false)] |
|||
[InlineData("query", false)] |
|||
[InlineData("HEAD", true)] |
|||
[InlineData("POST", true)] |
|||
[InlineData("PUT", true)] |
|||
[InlineData("DELETE", true)] |
|||
public void IsTransactional_Should_Treat_Get_And_Query_As_Non_Transactional(string method, bool expected) |
|||
{ |
|||
CreateProvider(method).IsTransactional.ShouldBe(expected); |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using NSubstitute; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Auditing; |
|||
|
|||
public class AuditingInterceptor_HttpMethod_Tests : AbpAuditingTestBase |
|||
{ |
|||
protected IAuditingStore AuditingStore; |
|||
|
|||
private string? _httpMethod; |
|||
|
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
{ |
|||
AuditingStore = Substitute.For<IAuditingStore>(); |
|||
services.Replace(ServiceDescriptor.Singleton(AuditingStore)); |
|||
|
|||
services.Configure<AbpAuditingOptions>(options => |
|||
{ |
|||
options.IsEnabledForGetRequests = false; |
|||
options.Contributors.Add(new TestHttpMethodAuditContributor(() => _httpMethod)); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Not_Write_AuditLog_For_Query_Http_Method_Without_Explicit_Scope() |
|||
{ |
|||
_httpMethod = "QUERY"; |
|||
|
|||
var auditedObject = GetRequiredService<Auditing_Tests.MyAuditedObject1>(); |
|||
await auditedObject.DoItAsync(new Auditing_Tests.InputObject { Value1 = "x", Value2 = 1 }); |
|||
|
|||
await AuditingStore.DidNotReceive().SaveAsync(Arg.Any<AuditLogInfo>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Write_AuditLog_For_Post_Http_Method_Without_Explicit_Scope() |
|||
{ |
|||
_httpMethod = "POST"; |
|||
|
|||
var auditedObject = GetRequiredService<Auditing_Tests.MyAuditedObject1>(); |
|||
await auditedObject.DoItAsync(new Auditing_Tests.InputObject { Value1 = "x", Value2 = 1 }); |
|||
|
|||
await AuditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>()); |
|||
} |
|||
|
|||
public class TestHttpMethodAuditContributor : AuditLogContributor |
|||
{ |
|||
private readonly Func<string?> _httpMethodFactory; |
|||
|
|||
public TestHttpMethodAuditContributor(Func<string?> httpMethodFactory) |
|||
{ |
|||
_httpMethodFactory = httpMethodFactory; |
|||
} |
|||
|
|||
public override void PreContribute(AuditLogContributionContext context) |
|||
{ |
|||
context.AuditInfo.HttpMethod = _httpMethodFactory(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Http; |
|||
|
|||
public class HttpMethodHelper_Tests |
|||
{ |
|||
private static readonly Dictionary<string, Func<string?, bool>> Predicates = new() |
|||
{ |
|||
[HttpMethodHelper.Get] = HttpMethodHelper.IsGet, |
|||
[HttpMethodHelper.Post] = HttpMethodHelper.IsPost, |
|||
[HttpMethodHelper.Put] = HttpMethodHelper.IsPut, |
|||
[HttpMethodHelper.Delete] = HttpMethodHelper.IsDelete, |
|||
[HttpMethodHelper.Patch] = HttpMethodHelper.IsPatch, |
|||
[HttpMethodHelper.Head] = HttpMethodHelper.IsHead, |
|||
[HttpMethodHelper.Options] = HttpMethodHelper.IsOptions, |
|||
[HttpMethodHelper.Trace] = HttpMethodHelper.IsTrace, |
|||
[HttpMethodHelper.Query] = HttpMethodHelper.IsQuery |
|||
}; |
|||
|
|||
[Theory] |
|||
[InlineData(HttpMethodHelper.Get)] |
|||
[InlineData(HttpMethodHelper.Post)] |
|||
[InlineData(HttpMethodHelper.Put)] |
|||
[InlineData(HttpMethodHelper.Delete)] |
|||
[InlineData(HttpMethodHelper.Patch)] |
|||
[InlineData(HttpMethodHelper.Head)] |
|||
[InlineData(HttpMethodHelper.Options)] |
|||
[InlineData(HttpMethodHelper.Trace)] |
|||
[InlineData(HttpMethodHelper.Query)] |
|||
public void Is_Predicates_Should_Match_Only_Their_Own_Verb_Ignoring_Case(string verb) |
|||
{ |
|||
foreach (var (name, predicate) in Predicates) |
|||
{ |
|||
var shouldMatch = name == verb; |
|||
predicate(verb).ShouldBe(shouldMatch); |
|||
predicate(verb.ToLowerInvariant()).ShouldBe(shouldMatch); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Is_Predicates_Should_Return_False_For_Null() |
|||
{ |
|||
foreach (var predicate in Predicates.Values) |
|||
{ |
|||
predicate(null).ShouldBeFalse(); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("QUERY", true)] |
|||
[InlineData("query", true)] |
|||
[InlineData("Query", true)] |
|||
[InlineData("GET", false)] |
|||
[InlineData("POST", false)] |
|||
[InlineData("", false)] |
|||
[InlineData(null, false)] |
|||
public void IsQuery_Should_Match_Query_Method_Ignoring_Case(string? httpMethod, bool expected) |
|||
{ |
|||
HttpMethodHelper.IsQuery(httpMethod).ShouldBe(expected); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ConvertToHttpMethod_Should_Support_Query() |
|||
{ |
|||
HttpMethodHelper.ConvertToHttpMethod("QUERY").Method.ShouldBe("QUERY"); |
|||
HttpMethodHelper.ConvertToHttpMethod("query").Method.ShouldBe("QUERY"); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("GET")] |
|||
[InlineData("POST")] |
|||
[InlineData("PUT")] |
|||
[InlineData("DELETE")] |
|||
[InlineData("PATCH")] |
|||
[InlineData("QUERY")] |
|||
public void ConvertToHttpMethod_Should_Not_Throw_For_Known_Methods(string httpMethod) |
|||
{ |
|||
Should.NotThrow(() => HttpMethodHelper.ConvertToHttpMethod(httpMethod)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ConvertToHttpMethod_Should_Throw_For_Unknown_Method() |
|||
{ |
|||
Should.Throw<AbpException>(() => HttpMethodHelper.ConvertToHttpMethod("UNKNOWN")); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("GetFooAsync", "GET")] |
|||
[InlineData("GetListAsync", "GET")] |
|||
[InlineData("CreateFooAsync", "POST")] |
|||
[InlineData("UpdateFooAsync", "PUT")] |
|||
[InlineData("DeleteFooAsync", "DELETE")] |
|||
[InlineData("PatchFooAsync", "PATCH")] |
|||
[InlineData("DoSomethingAsync", "POST")] |
|||
// QUERY is intentionally NOT a naming convention: an action must opt in explicitly
|
|||
// with [AcceptVerbs("QUERY")]. A method named Query* still maps to the default verb.
|
|||
[InlineData("QueryFooAsync", "POST")] |
|||
public void GetConventionalVerbForMethodName_Should_Not_Map_Query_By_Name(string methodName, string expectedVerb) |
|||
{ |
|||
HttpMethodHelper.GetConventionalVerbForMethodName(methodName).ShouldBe(expectedVerb); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue