diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs index f83a397707..da234bb474 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs @@ -226,8 +226,8 @@ namespace Volo.Abp.AspNetCore.Mvc protected virtual string SelectHttpMethod(ActionModel action, AbpControllerAssemblySetting configuration) { return configuration?.UseConventionalHttpVerbs == true - ? HttpVerbHelper.GetConventionalVerbForMethodName(action.ActionName) - : HttpVerbHelper.DefaultHttpVerb; + ? HttpMethodConventionHelper.GetConventionalVerbForMethodName(action.ActionName) + : HttpMethodConventionHelper.DefaultHttpVerb; } protected virtual void NormalizeSelectorRoutes(string moduleName, string controllerName, ActionModel action) @@ -256,11 +256,29 @@ namespace Volo.Abp.AspNetCore.Mvc protected virtual AttributeRouteModel CreateAbpServiceAttributeRouteModel(string moduleName, string controllerName, ActionModel action, string httpMethod) { - var url = $"api/{moduleName}/{controllerName}/{action.ActionName}"; + var url = CalculateUrl(moduleName, controllerName, action, httpMethod); + return new AttributeRouteModel(new RouteAttribute(url)); + } + private static string CalculateUrl(string moduleName, string controllerName, ActionModel action, string httpMethod) + { + var url = $"api/{moduleName}/{controllerName}"; - return new AttributeRouteModel(new RouteAttribute(url)); + //Add {id} path if needed + if (action.Parameters.Any(p => p.ParameterName == "id")) + { + url += "/{id}"; + } + + //Add action name if needed + var actionName = HttpMethodConventionHelper.RemoveHttpMethodPrefix(action.ActionName, httpMethod); + if (!actionName.IsNullOrEmpty()) + { + url += $"/{actionName}"; + } + + return url; } protected virtual void RemoveEmptySelectors(IList selectors) diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs index 6e4e8419d0..2aaf538f1c 100644 --- a/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs +++ b/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs @@ -80,7 +80,7 @@ namespace Volo.Abp.Application.Services return MapToEntityDto(entity); } - public virtual async Task> GetAll(TGetAllInput input) + public virtual async Task> GetList(TGetAllInput input) { CheckGetAllPermission(); diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs index 6fe5b19b28..a42f2d872a 100644 --- a/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs +++ b/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs @@ -39,7 +39,7 @@ namespace Volo.Abp.Application.Services { Task Get(TPrimaryKey id); - Task> GetAll(TGetAllInput input); + Task> GetList(TGetAllInput input); Task Create(TCreateInput input); diff --git a/src/Volo.Abp/Volo/Abp/Http/HttpMethodConventionHelper.cs b/src/Volo.Abp/Volo/Abp/Http/HttpMethodConventionHelper.cs new file mode 100644 index 0000000000..16d62de1f4 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Http/HttpMethodConventionHelper.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; + +namespace Volo.Abp.Http +{ + public static class HttpMethodConventionHelper + { + public const string DefaultHttpVerb = "POST"; + + public static Dictionary ConventionalPrefixes { get; set; } = new Dictionary + { + {"GET", new[] {"GetList", "GetAll", "Get"}}, + {"PUT", new[] {"Put", "Update"}}, + {"DELETE", new[] {"Delete", "Remove"}}, + {"POST", new[] {"Create", "Add", "Insert", "Post"}}, + {"PATCH", new[] {"Patch"}} + }; + + public static string GetConventionalVerbForMethodName(string methodName) + { + foreach (var conventionalPrefix in ConventionalPrefixes) + { + if (conventionalPrefix.Value.Any(prefix => methodName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) + { + return conventionalPrefix.Key; + } + } + + return DefaultHttpVerb; + } + + public static string RemoveHttpMethodPrefix([NotNull] string methodName, [NotNull] string httpMethod) + { + Check.NotNull(methodName, nameof(methodName)); + Check.NotNull(httpMethod, nameof(httpMethod)); + + var prefixes = ConventionalPrefixes.GetOrDefault(httpMethod); + if (prefixes.IsNullOrEmpty()) + { + return methodName; + } + + return methodName.RemovePreFix(prefixes); + } + } +} diff --git a/src/Volo.Abp/Volo/Abp/Http/HttpVerbHelper.cs b/src/Volo.Abp/Volo/Abp/Http/HttpVerbHelper.cs deleted file mode 100644 index 0652d76b59..0000000000 --- a/src/Volo.Abp/Volo/Abp/Http/HttpVerbHelper.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; - -namespace Volo.Abp.Http -{ - public class HttpVerbHelper //TODO: Internal? - { - public const string DefaultHttpVerb = "POST"; - - public static string GetConventionalVerbForMethodName(string methodName) - { - if (methodName.StartsWith("Get", StringComparison.OrdinalIgnoreCase)) - { - return "GET"; - } - - if (methodName.StartsWith("Put", StringComparison.OrdinalIgnoreCase) || - methodName.StartsWith("Update", StringComparison.OrdinalIgnoreCase)) - { - return "PUT"; - } - - if (methodName.StartsWith("Delete", StringComparison.OrdinalIgnoreCase) || - methodName.StartsWith("Remove", StringComparison.OrdinalIgnoreCase)) - { - return "DELETE"; - } - - if (methodName.StartsWith("Patch", StringComparison.OrdinalIgnoreCase)) - { - return "PATCH"; - } - - if (methodName.StartsWith("Post", StringComparison.OrdinalIgnoreCase) || - methodName.StartsWith("Create", StringComparison.OrdinalIgnoreCase) || - methodName.StartsWith("Insert", StringComparison.OrdinalIgnoreCase)) - { - return "POST"; - } - - //Default - return DefaultHttpVerb; - } - } -} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PersonAppService_Tests.cs b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PersonAppService_Tests.cs index 8e48504fdd..d5989a7b21 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PersonAppService_Tests.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PersonAppService_Tests.cs @@ -34,8 +34,8 @@ namespace Volo.Abp.AspNetCore.Mvc [Fact] public async Task GetAll_Test() { - //Ideally should be [GET] /api/app/person - var result = await GetResponseAsObjectAsync>("/api/app/person/GetAll"); + //Ideally should be [GET] /api/app/person OK! + var result = await GetResponseAsObjectAsync>("/api/app/person"); result.Items.Count.ShouldBeGreaterThan(0); } @@ -44,8 +44,8 @@ namespace Volo.Abp.AspNetCore.Mvc { var firstPerson = _personRepository.GetList().First(); - //Ideally should be [GET] /api/app/person/{id} - var result = await GetResponseAsObjectAsync("/api/app/person/Get?id=" + firstPerson.Id); + //Ideally should be [GET] /api/app/person/{id} OK! + var result = await GetResponseAsObjectAsync($"/api/app/person/{firstPerson.Id}"); result.Name.ShouldBe(firstPerson.Name); } @@ -54,8 +54,8 @@ namespace Volo.Abp.AspNetCore.Mvc { var firstPerson = _personRepository.GetList().First(); - //Ideally should be [DELETE] /api/app/person/{id} - await Client.DeleteAsync("/api/app/person/Delete?id=" + firstPerson.Id); + //Ideally should be [DELETE] /api/app/person/{id} OK! + await Client.DeleteAsync($"/api/app/person/{firstPerson.Id}"); (await _personRepository.FindAsync(firstPerson.Id)).ShouldBeNull(); } @@ -67,9 +67,9 @@ namespace Volo.Abp.AspNetCore.Mvc var postData = _jsonSerializer.Serialize(new PersonDto {Name = "John", Age = 33}); - //Ideally should be [POST] /api/app/person + //Ideally should be [POST] /api/app/person OK! var response = await Client.PostAsync( - "/api/app/person/Create", + "/api/app/person", new StringContent(postData, Encoding.UTF8, "application/json") ); @@ -100,9 +100,9 @@ namespace Volo.Abp.AspNetCore.Mvc //Act - //Ideally should be [PUT] /api/app/person/{id} + //Ideally should be [PUT] /api/app/person/{id} OK! var response = await Client.PutAsync( - "/api/app/person/Update?id=" + updateDto.Id, + $"/api/app/person/{updateDto.Id}", new StringContent(putData, Encoding.UTF8, "application/json") ); @@ -136,7 +136,7 @@ namespace Volo.Abp.AspNetCore.Mvc //Ideally should be [POST] /api/people/{id}/phones var response = await Client.PostAsync( - "/api/app/person/AddPhone?id=" + personToAddNewPhone.Id, + $"/api/app/person/{personToAddNewPhone.Id}/Phone", new StringContent(postData, Encoding.UTF8, "application/json") ); @@ -160,7 +160,7 @@ namespace Volo.Abp.AspNetCore.Mvc var douglas = _personRepository.GetList().First(p => p.Name == "Douglas"); //Ideally should be [GET] /api/person/{id}/phones?type=office - var result = await GetResponseAsObjectAsync>("/api/app/person/GetPhones?id=" + douglas.Id); + var result = await GetResponseAsObjectAsync>($"/api/app/person/{douglas.Id}/Phones"); result.Items.Count.ShouldBe(douglas.Phones.Count); } @@ -171,7 +171,7 @@ namespace Volo.Abp.AspNetCore.Mvc var firstPhone = douglas.Phones.First(); //Ideally should be [DELETE] /api/app/person/{id} - await Client.DeleteAsync("/api/app/person/DeletePhone?id=" + douglas.Id + "&phoneId=" + firstPhone.Id); + await Client.DeleteAsync($"/api/app/person/{douglas.Id}/Phone?phoneId=" + firstPhone.Id); douglas = _personRepository.GetList().First(p => p.Name == "Douglas"); douglas.Phones.Any(p => p.Id == firstPhone.Id).ShouldBeFalse(); diff --git a/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs b/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs index bd3d3f61dd..ef268dbc18 100644 --- a/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs +++ b/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs @@ -18,7 +18,7 @@ namespace Volo.Abp.TestApp.Application [Fact] public async Task GetAll() { - var people = await _personAppService.GetAll(new PagedAndSortedResultRequestDto()); + var people = await _personAppService.GetList(new PagedAndSortedResultRequestDto()); people.Items.Count.ShouldBeGreaterThan(0); } }