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