Skip to content

Commit d35cd6a

Browse files
committed
Dont mangle ActionNameAttribute
1 parent 1808236 commit d35cd6a

File tree

5 files changed

+51
-123
lines changed

5 files changed

+51
-123
lines changed

src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/DefaultApplicationModelProvider.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ internal class DefaultApplicationModelProvider : IApplicationModelProvider
2222
private readonly IModelMetadataProvider _modelMetadataProvider;
2323
private readonly Func<ActionContext, bool> _supportsAllRequests;
2424
private readonly Func<ActionContext, bool> _supportsNonGetRequests;
25-
private readonly List<IActionModelConvention> _actionModelConventions;
2625

2726
public DefaultApplicationModelProvider(
2827
IOptions<MvcOptions> mvcOptionsAccessor,
@@ -33,12 +32,6 @@ public DefaultApplicationModelProvider(
3332

3433
_supportsAllRequests = _ => true;
3534
_supportsNonGetRequests = context => !string.Equals(context.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase);
36-
37-
_actionModelConventions = new List<IActionModelConvention>();
38-
if (_mvcOptions.SuppressAsyncSuffixInActionNames)
39-
{
40-
_actionModelConventions.Add(new SuppressAsyncSuffixInActionNameConvention());
41-
}
4235
}
4336

4437
/// <inheritdoc />
@@ -99,11 +92,6 @@ public void OnProvidersExecuting(ApplicationModelProviderContext context)
9992
actionModel.Parameters.Add(parameterModel);
10093
}
10194
}
102-
103-
foreach (var convention in _actionModelConventions)
104-
{
105-
convention.Apply(actionModel);
106-
}
10795
}
10896
}
10997
}
@@ -308,7 +296,7 @@ internal ActionModel CreateActionModel(
308296
}
309297
else
310298
{
311-
actionModel.ActionName = methodInfo.Name;
299+
actionModel.ActionName = CanonicalizeActionName(methodInfo.Name);
312300
}
313301

314302
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
@@ -383,6 +371,19 @@ internal ActionModel CreateActionModel(
383371
return actionModel;
384372
}
385373

374+
private string CanonicalizeActionName(string actionName)
375+
{
376+
const string Suffix = "Async";
377+
378+
if (_mvcOptions.SuppressAsyncSuffixInActionNames &&
379+
actionName.EndsWith(Suffix, StringComparison.Ordinal))
380+
{
381+
actionName = actionName.Substring(0, actionName.Length - Suffix.Length);
382+
}
383+
384+
return actionName;
385+
}
386+
386387
/// <summary>
387388
/// Returns <c>true</c> if the <paramref name="methodInfo"/> is an action. Otherwise <c>false</c>.
388389
/// </summary>

src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/SuppressAsyncSuffixInActionNameConvention.cs

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
using System;
55
using System.Collections;
66
using System.Collections.Generic;
7+
using Microsoft.AspNetCore.Mvc.Abstractions;
78
using Microsoft.AspNetCore.Mvc.ApplicationModels;
9+
using Microsoft.AspNetCore.Mvc.Controllers;
810
using Microsoft.AspNetCore.Mvc.Filters;
911
using Microsoft.AspNetCore.Mvc.Formatters;
1012
using Microsoft.AspNetCore.Mvc.Infrastructure;
@@ -214,13 +216,17 @@ public int? MaxValidationDepth
214216
/// controller action names.
215217
/// <para>
216218
/// When <see langword="true"/>, MVC will strip action names of the Async suffix.
217-
/// Action names are used for routing as well as view lookup. For example, the action
218-
/// action <c>ProductsController.ListProductsAsync</c> will be routeable at <c>/Products/ListProducts</c>
219+
/// <see cref="ControllerActionDescriptor.ActionName"/> is used to construct the route to the action as
220+
/// well as in view lookup. For example, the action <c>ProductsController.ListProductsAsync</c> will
221+
/// be routeable at <c>/Products/ListProducts</c>
219222
/// with views looked up at <c>/Views/Products/ListProducts.cshtml</c>.
220223
/// </para>
224+
/// <para>
225+
/// This option does not affect values specified using using <see cref="ActionNameAttribute"/>.
226+
/// </para>
221227
/// </summary>
222228
/// <value>
223-
/// The default value is <c>true</c>.
229+
/// The default value is <see langword="true"/>.
224230
/// </value>
225231
public bool SuppressAsyncSuffixInActionNames { get; set; } = true;
226232

src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/DefaultApplicationModelProviderTest.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ public void OnProvidersExecuting_RemovesAsyncSuffix_WhenOptionIsSet()
300300
var options = new MvcOptions();
301301
var provider = new TestApplicationModelProvider(options, new EmptyModelMetadataProvider());
302302
var typeInfo = typeof(AsyncActionController).GetTypeInfo();
303+
var methodInfo = typeInfo.GetMethod(nameof(AsyncActionController.GetPersonAsync));
303304

304305
var context = new ApplicationModelProviderContext(new[] { typeInfo });
305306

@@ -308,7 +309,7 @@ public void OnProvidersExecuting_RemovesAsyncSuffix_WhenOptionIsSet()
308309

309310
// Assert
310311
var controllerModel = Assert.Single(context.Result.Controllers);
311-
var action = Assert.Single(controllerModel.Actions);
312+
var action = Assert.Single(controllerModel.Actions, a => a.ActionMethod == methodInfo);
312313
Assert.Equal("GetPerson", action.ActionName);
313314
}
314315

@@ -319,6 +320,7 @@ public void OnProvidersExecuting_DoesNotRemoveAsyncSuffix_WhenOptionIsDisabled()
319320
var options = new MvcOptions { SuppressAsyncSuffixInActionNames = false };
320321
var provider = new TestApplicationModelProvider(options, new EmptyModelMetadataProvider());
321322
var typeInfo = typeof(AsyncActionController).GetTypeInfo();
323+
var methodInfo = typeInfo.GetMethod(nameof(AsyncActionController.GetPersonAsync));
322324

323325
var context = new ApplicationModelProviderContext(new[] { typeInfo });
324326

@@ -327,10 +329,30 @@ public void OnProvidersExecuting_DoesNotRemoveAsyncSuffix_WhenOptionIsDisabled()
327329

328330
// Assert
329331
var controllerModel = Assert.Single(context.Result.Controllers);
330-
var action = Assert.Single(controllerModel.Actions);
332+
var action = Assert.Single(controllerModel.Actions, a => a.ActionMethod == methodInfo);
331333
Assert.Equal(nameof(AsyncActionController.GetPersonAsync), action.ActionName);
332334
}
333335

336+
[Fact]
337+
public void OnProvidersExecuting_DoesNotRemoveAsyncSuffix_WhenActionNameIsSpecifiedUsingActionNameAttribute()
338+
{
339+
// Arrange
340+
var options = new MvcOptions();
341+
var provider = new TestApplicationModelProvider(options, new EmptyModelMetadataProvider());
342+
var typeInfo = typeof(AsyncActionController).GetTypeInfo();
343+
var methodInfo = typeInfo.GetMethod(nameof(AsyncActionController.GetAddressAsync));
344+
345+
var context = new ApplicationModelProviderContext(new[] { typeInfo });
346+
347+
// Act
348+
provider.OnProvidersExecuting(context);
349+
350+
// Assert
351+
var controllerModel = Assert.Single(context.Result.Controllers);
352+
var action = Assert.Single(controllerModel.Actions, a => a.ActionMethod == methodInfo);
353+
Assert.Equal("GetRealAddressAsync", action.ActionName);
354+
}
355+
334356
[Fact]
335357
public void CreateControllerModel_DerivedFromControllerClass_HasFilter()
336358
{
@@ -1814,7 +1836,10 @@ public void Edit() { }
18141836

18151837
private class AsyncActionController : Controller
18161838
{
1817-
public IActionResult GetPersonAsync() => null;
1839+
public Task<IActionResult> GetPersonAsync() => null;
1840+
1841+
[ActionName("GetRealAddressAsync")]
1842+
public Task<IActionResult> GetAddressAsync() => null;
18181843
}
18191844

18201845
private class TestApplicationModelProvider : DefaultApplicationModelProvider

src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModels/SuppressAsyncSuffixInActionNameConventionTest.cs

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)