Browse Source

Support secondary Ids and allow to normalize action in url.

pull/96/head
Halil İbrahim Kalkan 9 years ago
parent
commit
9a7507ce31
  1. 27
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs
  2. 32
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs
  3. 41
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs
  4. 18
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DefaultUrlActionNameNormalizer.cs
  5. 7
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IUrlActionNameNormalizer.cs
  6. 27
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlActionNameNormalizerContext.cs
  7. 1
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AppModule.cs
  8. 17
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/PhoneBookUrlActionNameNormalizer.cs
  9. 11
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PersonAppService_Tests.cs

27
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs

@ -261,7 +261,7 @@ namespace Volo.Abp.AspNetCore.Mvc
return new AttributeRouteModel(new RouteAttribute(url)); return new AttributeRouteModel(new RouteAttribute(url));
} }
private static string CalculateUrl(string moduleName, string controllerName, ActionModel action, string httpMethod) protected virtual string CalculateUrl(string moduleName, string controllerName, ActionModel action, string httpMethod)
{ {
var url = $"api/{moduleName}/{controllerName}"; var url = $"api/{moduleName}/{controllerName}";
@ -272,15 +272,34 @@ namespace Volo.Abp.AspNetCore.Mvc
} }
//Add action name if needed //Add action name if needed
var actionName = HttpMethodConventionHelper.RemoveHttpMethodPrefix(action.ActionName, httpMethod); var actionNameInUrl = NormalizeUrlActionName(moduleName, controllerName, action, httpMethod);
if (!actionName.IsNullOrEmpty()) if (!actionNameInUrl.IsNullOrEmpty())
{ {
url += $"/{actionName}"; url += $"/{actionNameInUrl}";
//Add secondary Id
var secondaryIds = action.Parameters.Where(p => p.ParameterName.EndsWith("Id", StringComparison.Ordinal)).ToList();
if (secondaryIds.Count == 1)
{
url += $"/{{{secondaryIds[0].ParameterName}}}";
}
} }
return url; return url;
} }
protected virtual string NormalizeUrlActionName(string moduleName, string controllerName, ActionModel action, string httpMethod)
{
var context = new UrlActionNameNormalizerContext(moduleName, controllerName, action, httpMethod, action.ActionName);
foreach (var normalizer in _options.AppServiceControllers.UrlActionNameNormalizers)
{
normalizer.Normalize(context);
}
return context.ActionNameInUrl;
}
protected virtual void RemoveEmptySelectors(IList<SelectorModel> selectors) protected virtual void RemoveEmptySelectors(IList<SelectorModel> selectors)
{ {
selectors selectors

32
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs

@ -1,9 +1,4 @@
using Microsoft.AspNetCore.Http; namespace Volo.Abp.AspNetCore.Mvc
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Volo.Abp.AspNetCore.Mvc
{ {
public class AbpAspNetCoreMvcOptions public class AbpAspNetCoreMvcOptions
{ {
@ -14,29 +9,4 @@ namespace Volo.Abp.AspNetCore.Mvc
AppServiceControllers = new AppServiceControllerOptions(); AppServiceControllers = new AppServiceControllerOptions();
} }
} }
public class AppServiceControllerOptions
{
public ControllerAssemblySettingList ControllerAssemblySettings { get; }
public List<Type> FormBodyBindingIgnoredTypes { get; }
public AppServiceControllerOptions()
{
ControllerAssemblySettings = new ControllerAssemblySettingList();
FormBodyBindingIgnoredTypes = new List<Type>
{
typeof(IFormFile)
};
}
public AbpControllerAssemblySettingBuilder CreateFor(
Assembly assembly,
string moduleName = AbpControllerAssemblySetting.DefaultServiceModuleName,
bool useConventionalHttpVerbs = true)
{
var setting = new AbpControllerAssemblySetting(moduleName, assembly, useConventionalHttpVerbs);
ControllerAssemblySettings.Add(setting);
return new AbpControllerAssemblySettingBuilder(setting);
}
}
} }

41
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.Http;
namespace Volo.Abp.AspNetCore.Mvc
{
public class AppServiceControllerOptions
{
public ControllerAssemblySettingList ControllerAssemblySettings { get; }
public List<Type> FormBodyBindingIgnoredTypes { get; }
public List<IUrlActionNameNormalizer> UrlActionNameNormalizers { get; }
public AppServiceControllerOptions()
{
ControllerAssemblySettings = new ControllerAssemblySettingList();
FormBodyBindingIgnoredTypes = new List<Type>
{
typeof(IFormFile)
};
UrlActionNameNormalizers = new List<IUrlActionNameNormalizer>
{
new DefaultUrlActionNameNormalizer()
};
}
public AbpControllerAssemblySettingBuilder CreateFor(
Assembly assembly,
string moduleName = AbpControllerAssemblySetting.DefaultServiceModuleName,
bool useConventionalHttpVerbs = true)
{
var setting = new AbpControllerAssemblySetting(moduleName, assembly, useConventionalHttpVerbs);
ControllerAssemblySettings.Add(setting);
return new AbpControllerAssemblySettingBuilder(setting);
}
}
}

18
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/DefaultUrlActionNameNormalizer.cs

@ -0,0 +1,18 @@
using System;
using Volo.Abp.Http;
namespace Volo.Abp.AspNetCore.Mvc
{
public class DefaultUrlActionNameNormalizer : IUrlActionNameNormalizer
{
public void Normalize(UrlActionNameNormalizerContext context)
{
if (context.ActionNameInUrl.IsNullOrEmpty())
{
return;
}
context.ActionNameInUrl = HttpMethodConventionHelper.RemoveHttpMethodPrefix(context.ActionNameInUrl, context.HttpMethod);
}
}
}

7
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IUrlActionNameNormalizer.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.Mvc
{
public interface IUrlActionNameNormalizer
{
void Normalize(UrlActionNameNormalizerContext context);
}
}

27
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlActionNameNormalizerContext.cs

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Volo.Abp.AspNetCore.Mvc
{
public class UrlActionNameNormalizerContext
{
public string ModuleName { get; }
public string ControllerName { get; }
public ActionModel Action { get; }
public string HttpMethod { get; }
public string ActionNameInUrl { get; set; }
public UrlActionNameNormalizerContext(string moduleName, string controllerName, ActionModel action, string httpMethod, string actionNameInUrl)
{
ModuleName = moduleName;
ControllerName = controllerName;
Action = action;
HttpMethod = httpMethod;
ActionNameInUrl = actionNameInUrl;
}
}
}

1
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AppModule.cs

@ -25,6 +25,7 @@ namespace Volo.Abp.AspNetCore.App
services.Configure<AbpAspNetCoreMvcOptions>(options => services.Configure<AbpAspNetCoreMvcOptions>(options =>
{ {
options.AppServiceControllers.CreateFor(typeof(TestAppModule).Assembly); options.AppServiceControllers.CreateFor(typeof(TestAppModule).Assembly);
options.AppServiceControllers.UrlActionNameNormalizers.Add(new PhoneBookUrlActionNameNormalizer());
}); });
services.AddAssemblyOf<AppModule>(); services.AddAssemblyOf<AppModule>();

17
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/PhoneBookUrlActionNameNormalizer.cs

@ -0,0 +1,17 @@
using System;
using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Abp.AspNetCore.App
{
public class PhoneBookUrlActionNameNormalizer : IUrlActionNameNormalizer
{
public void Normalize(UrlActionNameNormalizerContext context)
{
if (string.Equals(context.ActionNameInUrl, "phone", StringComparison.OrdinalIgnoreCase))
{
context.ActionNameInUrl = "phones";
return;
}
}
}
}

11
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/PersonAppService_Tests.cs

@ -129,9 +129,8 @@ namespace Volo.Abp.AspNetCore.Mvc
var postData = _jsonSerializer.Serialize(new PhoneDto { Type = PhoneType.Mobile, Number = phoneNumberToAdd }); var postData = _jsonSerializer.Serialize(new PhoneDto { Type = PhoneType.Mobile, Number = phoneNumberToAdd });
//Ideally should be [POST] /api/people/{id}/phones
var response = await Client.PostAsync( var response = await Client.PostAsync(
$"/api/app/people/{personToAddNewPhone.Id}/phone", $"/api/app/people/{personToAddNewPhone.Id}/phones",
new StringContent(postData, Encoding.UTF8, "application/json") new StringContent(postData, Encoding.UTF8, "application/json")
); );
@ -154,8 +153,7 @@ namespace Volo.Abp.AspNetCore.Mvc
{ {
var douglas = _personRepository.GetList().First(p => p.Name == "Douglas"); var douglas = _personRepository.GetList().First(p => p.Name == "Douglas");
//Ideally should be [GET] /api/people/{id}/phones?type=office var result = await GetResponseAsObjectAsync<ListResultDto<PhoneDto>>($"/api/app/people/{douglas.Id}/phones");
var result = await GetResponseAsObjectAsync<ListResultDto<PhoneDto>>($"/api/app/people/{douglas.Id}/Phones");
result.Items.Count.ShouldBe(douglas.Phones.Count); result.Items.Count.ShouldBe(douglas.Phones.Count);
} }
@ -165,11 +163,10 @@ namespace Volo.Abp.AspNetCore.Mvc
var douglas = _personRepository.GetList().First(p => p.Name == "Douglas"); var douglas = _personRepository.GetList().First(p => p.Name == "Douglas");
var firstPhone = douglas.Phones.First(); var firstPhone = douglas.Phones.First();
//Ideally should be [DELETE] /api/app/people/{id}/phones/{phoneId} await Client.DeleteAsync($"/api/app/people/{douglas.Id}/phones/{firstPhone.Id}");
await Client.DeleteAsync($"/api/app/people/{douglas.Id}/Phone?phoneId=" + firstPhone.Id);
douglas = _personRepository.GetList().First(p => p.Name == "Douglas"); douglas = _personRepository.GetList().First(p => p.Name == "Douglas");
douglas.Phones.Any(p => p.Id == firstPhone.Id).ShouldBeFalse(); douglas.Phones.Any(p => p.Id == firstPhone.Id).ShouldBeFalse();
} }
} }
} }
Loading…
Cancel
Save