Cache more things in HandlerMethodDescriptor
Add tests for DefaultPageHandlerMethodSelector
This commit is contained in:
parent
7b53ba1f6b
commit
4faef7afaf
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
|
|
@ -12,5 +13,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
public MethodInfo Method { get; set; }
|
||||
|
||||
public Func<Page, object, Task<IActionResult>> Executor { get; set; }
|
||||
|
||||
public string HttpMethod { get; set; }
|
||||
|
||||
public StringSegment FormAction { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -3,140 +3,106 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
public class DefaultPageHandlerMethodSelector : IPageHandlerMethodSelector
|
||||
{
|
||||
private const string FormAction = "formaction";
|
||||
|
||||
public HandlerMethodDescriptor Select(PageContext context)
|
||||
{
|
||||
var handlers = new List<HandlerMethodAndMetadata>(context.ActionDescriptor.HandlerMethods.Count);
|
||||
for (var i = 0; i < context.ActionDescriptor.HandlerMethods.Count; i++)
|
||||
var handlers = SelectHandlers(context);
|
||||
if (handlers == null || handlers.Count == 0)
|
||||
{
|
||||
handlers.Add(HandlerMethodAndMetadata.Create(context.ActionDescriptor.HandlerMethods[i]));
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = handlers.Count - 1; i >= 0; i--)
|
||||
List<HandlerMethodDescriptor> ambiguousMatches = null;
|
||||
HandlerMethodDescriptor bestMatch = null;
|
||||
for (var score = 2; score >= 0; score--)
|
||||
{
|
||||
var handler = handlers[i];
|
||||
|
||||
if (handler.HttpMethod != null &&
|
||||
!string.Equals(handler.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||
for (var i = 0; i < handlers.Count; i++)
|
||||
{
|
||||
handlers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
var formaction = Convert.ToString(context.RouteData.Values["formaction"]);
|
||||
|
||||
for (var i = handlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var handler = handlers[i];
|
||||
|
||||
if (handler.Formaction != null &&
|
||||
!string.Equals(handler.Formaction, formaction, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
handlers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
var ambiguousMatches = (List<HandlerMethodDescriptor>)null;
|
||||
var best = (HandlerMethodAndMetadata?)null;
|
||||
for (var i = 2; i >= 0; i--)
|
||||
{
|
||||
for (var j = 0; j < handlers.Count; j++)
|
||||
{
|
||||
var handler = handlers[j];
|
||||
if (handler.GetScore() == i)
|
||||
var handler = handlers[i];
|
||||
if (GetScore(handler) == score)
|
||||
{
|
||||
if (best == null)
|
||||
if (bestMatch == null)
|
||||
{
|
||||
best = handler;
|
||||
bestMatch = handler;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ambiguousMatches == null)
|
||||
{
|
||||
ambiguousMatches = new List<HandlerMethodDescriptor>();
|
||||
ambiguousMatches.Add(best.Value.Handler);
|
||||
ambiguousMatches.Add(bestMatch);
|
||||
}
|
||||
|
||||
ambiguousMatches.Add(handler.Handler);
|
||||
ambiguousMatches.Add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
if (ambiguousMatches != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Selecting a handler is ambiguous! Matches: {string.Join(", ", ambiguousMatches)}");
|
||||
var ambiguousMethods = string.Join(", ", ambiguousMatches.Select(m => m.Method));
|
||||
throw new InvalidOperationException(Resources.FormatAmbiguousHandler(Environment.NewLine, ambiguousMethods));
|
||||
}
|
||||
|
||||
if (best != null)
|
||||
if (bestMatch != null)
|
||||
{
|
||||
return best.Value.Handler;
|
||||
return bestMatch;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Bad prototype substring implementation :)
|
||||
private struct HandlerMethodAndMetadata
|
||||
private static List<HandlerMethodDescriptor> SelectHandlers(PageContext context)
|
||||
{
|
||||
public static HandlerMethodAndMetadata Create(HandlerMethodDescriptor handler)
|
||||
var handlers = context.ActionDescriptor.HandlerMethods;
|
||||
List<HandlerMethodDescriptor> handlersToConsider = null;
|
||||
|
||||
var formAction = Convert.ToString(context.RouteData.Values[FormAction]);
|
||||
for (var i = 0; i < handlers.Count; i++)
|
||||
{
|
||||
var name = handler.Method.Name;
|
||||
|
||||
string httpMethod;
|
||||
if (name.StartsWith("OnGet", StringComparison.Ordinal))
|
||||
var handler = handlers[i];
|
||||
if (handler.HttpMethod != null &&
|
||||
!string.Equals(handler.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
httpMethod = "GET";
|
||||
continue;
|
||||
}
|
||||
else if (name.StartsWith("OnPost", StringComparison.Ordinal))
|
||||
else if (handler.FormAction.HasValue &&
|
||||
!handler.FormAction.Equals(formAction, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
httpMethod = "POST";
|
||||
}
|
||||
else
|
||||
{
|
||||
httpMethod = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
var formactionStart = httpMethod?.Length + 2 ?? 0;
|
||||
var formactionLength = name.EndsWith("Async", StringComparison.Ordinal)
|
||||
? name.Length - formactionStart - "Async".Length
|
||||
: name.Length - formactionStart;
|
||||
if (handlersToConsider == null)
|
||||
{
|
||||
handlersToConsider = new List<HandlerMethodDescriptor>();
|
||||
}
|
||||
|
||||
var formaction = formactionLength == 0 ? null : name.Substring(formactionStart, formactionLength);
|
||||
|
||||
return new HandlerMethodAndMetadata(handler, httpMethod, formaction);
|
||||
handlersToConsider.Add(handler);
|
||||
}
|
||||
|
||||
public HandlerMethodAndMetadata(HandlerMethodDescriptor handler, string httpMethod, string formaction)
|
||||
return handlersToConsider;
|
||||
}
|
||||
|
||||
private static int GetScore(HandlerMethodDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.FormAction != null)
|
||||
{
|
||||
Handler = handler;
|
||||
HttpMethod = httpMethod;
|
||||
Formaction = formaction;
|
||||
return 2;
|
||||
}
|
||||
|
||||
public HandlerMethodDescriptor Handler { get; }
|
||||
|
||||
public string HttpMethod { get; }
|
||||
|
||||
public string Formaction { get; }
|
||||
|
||||
public int GetScore()
|
||||
else if (descriptor.HttpMethod != null)
|
||||
{
|
||||
if (Formaction != null)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (HttpMethod != null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
|||
using Microsoft.AspNetCore.Razor.Evolution;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class PageActionInvokerProvider : IActionInvokerProvider
|
||||
{
|
||||
private static readonly string[] _handlerMethodNames = new string[] { "OnGet", "OnPost" };
|
||||
private const string PageStartFileName = "_PageStart.cshtml";
|
||||
private readonly IPageLoader _loader;
|
||||
private readonly IPageFactoryProvider _pageFactoryProvider;
|
||||
|
|
@ -204,7 +204,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var pageStartItems = _razorProject.FindHierarchicalItems(descriptor.ViewEnginePath, PageStartFileName);
|
||||
foreach (var item in pageStartItems)
|
||||
{
|
||||
if(item.Exists)
|
||||
if (item.Exists)
|
||||
{
|
||||
var factoryResult = _razorPageFactoryProvider.CreateFactory(item.Path);
|
||||
if (factoryResult.Success)
|
||||
|
|
@ -220,21 +220,91 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Internal for testing.
|
||||
internal static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor)
|
||||
{
|
||||
for (var i = 0; i < _handlerMethodNames.Length; i++)
|
||||
var methods = type.GetMethods();
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
var methodName = _handlerMethodNames[i];
|
||||
var method = type.GetMethod(methodName);
|
||||
if (method != null && !method.IsGenericMethod)
|
||||
var method = methods[i];
|
||||
if (!IsValidHandler(method))
|
||||
{
|
||||
actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor()
|
||||
{
|
||||
Method = method,
|
||||
Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
string httpMethod;
|
||||
int formActionStart;
|
||||
|
||||
if (method.Name.StartsWith("OnGet", StringComparison.Ordinal))
|
||||
{
|
||||
httpMethod = "GET";
|
||||
formActionStart = "OnGet".Length;
|
||||
}
|
||||
else if (method.Name.StartsWith("OnPost", StringComparison.Ordinal))
|
||||
{
|
||||
httpMethod = "POST";
|
||||
formActionStart = "OnPost".Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var formActionLength = method.Name.Length - formActionStart;
|
||||
if (method.Name.EndsWith("Async", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
formActionLength -= "Async".Length;
|
||||
}
|
||||
|
||||
var formAction = new StringSegment(method.Name, formActionStart, formActionLength);
|
||||
|
||||
var handlerMethodDescriptor = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = method,
|
||||
Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method),
|
||||
FormAction = formAction,
|
||||
HttpMethod = httpMethod,
|
||||
};
|
||||
|
||||
actionDescriptor.HandlerMethods.Add(handlerMethodDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidHandler(MethodInfo methodInfo)
|
||||
{
|
||||
// The SpecialName bit is set to flag members that are treated in a special way by some compilers
|
||||
// (such as property accessors and operator overloading methods).
|
||||
if (methodInfo.IsSpecialName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
|
||||
if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsConstructor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return methodInfo.IsPublic;
|
||||
}
|
||||
|
||||
internal class InnerCache
|
||||
{
|
||||
public InnerCache(int version)
|
||||
|
|
|
|||
|
|
@ -106,6 +106,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("UnsupportedHandlerMethodType"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:{0}{0}{1}
|
||||
/// </summary>
|
||||
internal static string AmbiguousHandler
|
||||
{
|
||||
get { return GetString("AmbiguousHandler"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:{0}{0}{1}
|
||||
/// </summary>
|
||||
internal static string FormatAmbiguousHandler(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("AmbiguousHandler"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path must be an application relative path that starts with a forward slash '/'.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@
|
|||
<data name="UnsupportedHandlerMethodType" xml:space="preserve">
|
||||
<value>Unsupported handler method return type '{0}'.</value>
|
||||
</data>
|
||||
<data name="AmbiguousHandler" xml:space="preserve">
|
||||
<value>Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:{0}{0}{1}</value>
|
||||
</data>
|
||||
<data name="PathMustBeAnAppRelativePath" xml:space="preserve">
|
||||
<value>Path must be an application relative path that starts with a forward slash '/'.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,406 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class DefaultPageHandlerMethodSelectorTest
|
||||
{
|
||||
[Fact]
|
||||
public void Select_ReturnsNull_WhenNoHandlerMatchesHttpMethod()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "GET"
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST"
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData(),
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "PUT"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act
|
||||
var actual = selector.Select(pageContext);
|
||||
|
||||
// Assert
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_ReturnsOnlyHandler()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "GET"
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData(),
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "GET"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act
|
||||
var actual = selector.Select(pageContext);
|
||||
|
||||
// Assert
|
||||
Assert.Same(descriptor, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET")]
|
||||
[InlineData("POST")]
|
||||
public void Select_ReturnsHandlerWithMatchingHttpRequestMethod(string httpMethod)
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "PUT",
|
||||
};
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = httpMethod,
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData(),
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = httpMethod,
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act
|
||||
var actual = selector.Select(pageContext);
|
||||
|
||||
// Assert
|
||||
Assert.Same(descriptor2, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_ReturnsNullWhenNoHandlerMatchesFormAction()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData
|
||||
{
|
||||
Values =
|
||||
{
|
||||
{ "formaction", "update" }
|
||||
}
|
||||
},
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "POST"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act
|
||||
var actual = selector.Select(pageContext);
|
||||
|
||||
// Assert
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_ReturnsHandlerThatMatchesFormAction()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Delete"),
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData
|
||||
{
|
||||
Values =
|
||||
{
|
||||
{ "formaction", "Add" }
|
||||
}
|
||||
},
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "Post"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act
|
||||
var actual = selector.Select(pageContext);
|
||||
|
||||
// Assert
|
||||
Assert.Same(descriptor1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_ReturnsHandlerWithMatchingHttpMethodWithoutAFormAction()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Subscribe"),
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "POST",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData
|
||||
{
|
||||
Values =
|
||||
{
|
||||
{ "formaction", "Add" }
|
||||
}
|
||||
},
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "Post"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act
|
||||
var actual = selector.Select(pageContext);
|
||||
|
||||
// Assert
|
||||
Assert.Same(descriptor2, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_WithoutFormAction_ThrowsIfMoreThanOneHandlerMatches()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(Post)),
|
||||
HttpMethod = "POST",
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(PostAsync)),
|
||||
HttpMethod = "POST",
|
||||
};
|
||||
|
||||
var descriptor3 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "GET",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
descriptor3,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData(),
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "Post"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => selector.Select(pageContext));
|
||||
var methods = descriptor1.Method + ", " + descriptor2.Method;
|
||||
var message = "Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:" +
|
||||
Environment.NewLine + Environment.NewLine + methods;
|
||||
|
||||
Assert.Equal(message, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_WithFormAction_ThrowsIfMoreThanOneHandlerMatches()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor1 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(Post)),
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
};
|
||||
|
||||
var descriptor2 = new HandlerMethodDescriptor
|
||||
{
|
||||
Method = GetType().GetMethod(nameof(PostAsync)),
|
||||
HttpMethod = "POST",
|
||||
FormAction = new StringSegment("Add"),
|
||||
};
|
||||
|
||||
var descriptor3 = new HandlerMethodDescriptor
|
||||
{
|
||||
HttpMethod = "GET",
|
||||
};
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
HandlerMethods =
|
||||
{
|
||||
descriptor1,
|
||||
descriptor2,
|
||||
descriptor3,
|
||||
},
|
||||
},
|
||||
RouteData = new RouteData
|
||||
{
|
||||
Values =
|
||||
{
|
||||
{ "formaction", "Add" }
|
||||
}
|
||||
},
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Method = "Post"
|
||||
},
|
||||
},
|
||||
};
|
||||
var selector = new DefaultPageHandlerMethodSelector();
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => selector.Select(pageContext));
|
||||
var methods = descriptor1.Method + ", " + descriptor2.Method;
|
||||
var message = "Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:" +
|
||||
Environment.NewLine + Environment.NewLine + methods;
|
||||
|
||||
Assert.Equal(message, ex.Message);
|
||||
}
|
||||
|
||||
public void Post()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostAsync()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
|
|
@ -382,6 +383,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(typeof(InheritsMethods), handler.Method.DeclaringType);
|
||||
},
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnGet", handler.Method.Name);
|
||||
Assert.Equal(typeof(TestSetPageModel), handler.Method.DeclaringType);
|
||||
},
|
||||
(handler) =>
|
||||
{
|
||||
Assert.Equal("OnPost", handler.Method.Name);
|
||||
Assert.Equal(typeof(TestSetPageModel), handler.Method.DeclaringType);
|
||||
|
|
@ -389,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_ProtectedMethodsNotFound()
|
||||
public void PopulateHandlerMethodDescriptors_IgnoresNonPublicMethods()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
|
|
@ -432,6 +438,113 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Empty(actionDescriptor.HandlerMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_IgnoresStaticMethods()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Index.cshtml"
|
||||
};
|
||||
|
||||
var modelTypeInfo = typeof(PageModelWithStaticHandler).GetTypeInfo();
|
||||
var expected = modelTypeInfo.GetMethod(nameof(PageModelWithStaticHandler.OnGet), BindingFlags.Public | BindingFlags.Instance);
|
||||
var actionDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(modelTypeInfo, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
handler => Assert.Same(expected, handler.Method));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_IgnoresAbstractMethods()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Index.cshtml"
|
||||
};
|
||||
|
||||
var modelTypeInfo = typeof(PageModelWithAbstractMethod).GetTypeInfo();
|
||||
var expected = modelTypeInfo.GetMethod(nameof(PageModelWithAbstractMethod.OnGet));
|
||||
var actionDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(modelTypeInfo, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
handler => Assert.Same(expected, handler.Method));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_DiscoversMethodsWithFormActions()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Index.cshtml"
|
||||
};
|
||||
|
||||
var modelTypeInfo = typeof(PageModelWithFormActions).GetTypeInfo();
|
||||
var actionDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
|
||||
// Act
|
||||
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(modelTypeInfo, actionDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actionDescriptor.HandlerMethods.OrderBy(h => h.Method.Name),
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnGet)), handler.Method);
|
||||
Assert.Equal("GET", handler.HttpMethod);
|
||||
Assert.Equal(0, handler.FormAction.Length);
|
||||
Assert.NotNull(handler.Executor);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnPostAdd)), handler.Method);
|
||||
Assert.Equal("POST", handler.HttpMethod);
|
||||
Assert.Equal("Add", handler.FormAction.ToString());
|
||||
Assert.NotNull(handler.Executor);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnPostAddCustomer)), handler.Method);
|
||||
Assert.Equal("POST", handler.HttpMethod);
|
||||
Assert.Equal("AddCustomer", handler.FormAction.ToString());
|
||||
Assert.NotNull(handler.Executor);
|
||||
},
|
||||
handler =>
|
||||
{
|
||||
Assert.Same(modelTypeInfo.GetMethod(nameof(PageModelWithFormActions.OnPostDeleteAsync)), handler.Method);
|
||||
Assert.Equal("POST", handler.HttpMethod);
|
||||
Assert.Equal("Delete", handler.FormAction.ToString());
|
||||
Assert.NotNull(handler.Executor);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateHandlerMethodDescriptors_AllowOnlyOneMethod()
|
||||
{
|
||||
|
|
@ -635,6 +748,57 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private class PageModelWithStaticHandler
|
||||
{
|
||||
public static void OnGet(string name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class PageModelWithAbstractMethod
|
||||
{
|
||||
public abstract void OnPost(string name);
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class PageModelWithFormActions
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPostAdd()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPostAddCustomer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPostDeleteAsync()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void OnPostDelete()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class ProtectedModel
|
||||
{
|
||||
protected void OnGet()
|
||||
|
|
|
|||
Loading…
Reference in New Issue