Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.

Commit aaffa93

Browse files
committed
Add a sample exhibiting custom Razor Page handler conventions
Fixes aspnet/Mvc#6065
1 parent 431036a commit aaffa93

File tree

11 files changed

+317
-36
lines changed

11 files changed

+317
-36
lines changed

Entropy.sln

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Microsoft Visual Studio Solution File, Format Version 12.00
22
# Visual Studio 15
3-
VisualStudioVersion = 15.0.26824.3000
3+
VisualStudioVersion = 15.0.26730.12
44
MinimumVisualStudioVersion = 10.0.40219.1
55
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Builder", "Builder", "{097807B3-FE74-4DCD-B76B-2FCE1A272BE5}"
66
EndProject
@@ -128,6 +128,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entropy.FunctionalTests", "
128128
EndProject
129129
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.ControllerForMultipleAreasSample", "samples\Mvc.ControllerForMultipleAreasSample\Mvc.ControllerForMultipleAreasSample.csproj", "{8A61FA46-3FD4-42BE-80C6-E5931387590C}"
130130
EndProject
131+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.CustomRazorPageHandlers", "samples\Mvc.CustomRazorPageHandlers\Mvc.CustomRazorPageHandlers.csproj", "{C673D327-412E-45E6-A12F-A8D37551793F}"
132+
EndProject
131133
Global
132134
GlobalSection(SolutionConfigurationPlatforms) = preSolution
133135
Debug|Any CPU = Debug|Any CPU
@@ -702,6 +704,18 @@ Global
702704
{8A61FA46-3FD4-42BE-80C6-E5931387590C}.Release|x64.Build.0 = Release|Any CPU
703705
{8A61FA46-3FD4-42BE-80C6-E5931387590C}.Release|x86.ActiveCfg = Release|Any CPU
704706
{8A61FA46-3FD4-42BE-80C6-E5931387590C}.Release|x86.Build.0 = Release|Any CPU
707+
{C673D327-412E-45E6-A12F-A8D37551793F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
708+
{C673D327-412E-45E6-A12F-A8D37551793F}.Debug|Any CPU.Build.0 = Debug|Any CPU
709+
{C673D327-412E-45E6-A12F-A8D37551793F}.Debug|x64.ActiveCfg = Debug|Any CPU
710+
{C673D327-412E-45E6-A12F-A8D37551793F}.Debug|x64.Build.0 = Debug|Any CPU
711+
{C673D327-412E-45E6-A12F-A8D37551793F}.Debug|x86.ActiveCfg = Debug|Any CPU
712+
{C673D327-412E-45E6-A12F-A8D37551793F}.Debug|x86.Build.0 = Debug|Any CPU
713+
{C673D327-412E-45E6-A12F-A8D37551793F}.Release|Any CPU.ActiveCfg = Release|Any CPU
714+
{C673D327-412E-45E6-A12F-A8D37551793F}.Release|Any CPU.Build.0 = Release|Any CPU
715+
{C673D327-412E-45E6-A12F-A8D37551793F}.Release|x64.ActiveCfg = Release|Any CPU
716+
{C673D327-412E-45E6-A12F-A8D37551793F}.Release|x64.Build.0 = Release|Any CPU
717+
{C673D327-412E-45E6-A12F-A8D37551793F}.Release|x86.ActiveCfg = Release|Any CPU
718+
{C673D327-412E-45E6-A12F-A8D37551793F}.Release|x86.Build.0 = Release|Any CPU
705719
EndGlobalSection
706720
GlobalSection(SolutionProperties) = preSolution
707721
HideSolutionNode = FALSE
@@ -765,6 +779,7 @@ Global
765779
{FDD8875D-00D6-4E8C-9E21-D90C0A2C81B7} = {CD3A2B49-E781-4766-B775-6FF3672AE308}
766780
{3D0B3DD1-6779-4E8F-BFBC-809497F7B30F} = {C03BF7E1-ECDF-48D0-85CD-A454AA28D80C}
767781
{8A61FA46-3FD4-42BE-80C6-E5931387590C} = {5BCA2B8A-37DA-4A88-A221-3D5B4796B07F}
782+
{C673D327-412E-45E6-A12F-A8D37551793F} = {8B204FB8-31A6-4559-AE7F-0412F504983C}
768783
EndGlobalSection
769784
GlobalSection(ExtensibilityGlobals) = postSolution
770785
SolutionGuid = {5DF584CA-249D-432C-BBA9-4498414C8E97}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System;
2+
using System.Reflection;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.AspNetCore.Mvc.ApplicationModels;
5+
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
6+
using Microsoft.Extensions.Options;
7+
8+
namespace Mvc.CustomRazorPageHandlers
9+
{
10+
public class CustomHandlerPageApplicationModelProvider : DefaultPageApplicationModelProvider
11+
{
12+
protected override PageHandlerModel CreateHandlerModel(MethodInfo method)
13+
{
14+
if (method == null)
15+
{
16+
throw new ArgumentNullException(nameof(method));
17+
}
18+
19+
if (!IsHandler(method))
20+
{
21+
return null;
22+
}
23+
24+
if (!TryParseHandlerMethod(method.Name, out var httpMethod, out var handlerName))
25+
{
26+
return null;
27+
}
28+
29+
var handlerModel = new PageHandlerModel(
30+
method,
31+
method.GetCustomAttributes(inherit: true))
32+
{
33+
Name = method.Name,
34+
HandlerName = handlerName,
35+
HttpMethod = httpMethod,
36+
};
37+
38+
var methodParameters = handlerModel.MethodInfo.GetParameters();
39+
40+
for (var i = 0; i < methodParameters.Length; i++)
41+
{
42+
var parameter = methodParameters[i];
43+
var parameterModel = CreateParameterModel(parameter);
44+
parameterModel.Handler = handlerModel;
45+
46+
handlerModel.Parameters.Add(parameterModel);
47+
}
48+
49+
return handlerModel;
50+
}
51+
52+
static bool TryParseHandlerMethod(string methodName, out string httpMethod, out string handler)
53+
{
54+
httpMethod = null;
55+
handler = null;
56+
57+
// Now we parse the method name according to our conventions to determine the required HTTP method
58+
// and optional 'handler name'.
59+
//
60+
// Valid names look like:
61+
// - Get
62+
// - GetCustomer
63+
// - PostCustomer
64+
// - DeleteCustomer
65+
66+
var length = methodName.Length;
67+
if (methodName.EndsWith("Async", StringComparison.Ordinal))
68+
{
69+
length -= "Async".Length;
70+
}
71+
72+
if (length == 0)
73+
{
74+
// Method is named "Async". Bail.
75+
return false;
76+
}
77+
78+
var handlerNameStart = 1;
79+
for (; handlerNameStart < length; handlerNameStart++)
80+
{
81+
if (char.IsUpper(methodName[handlerNameStart]))
82+
{
83+
break;
84+
}
85+
}
86+
87+
httpMethod = methodName.Substring(0, handlerNameStart);
88+
if (string.Equals(httpMethod, "GET", StringComparison.OrdinalIgnoreCase) ||
89+
string.Equals(httpMethod, "POST", StringComparison.OrdinalIgnoreCase))
90+
{
91+
// Do nothing. The remaining portion is the handler method.
92+
// e.g. PostCustomerAsync
93+
// HttpMethod = "POST", Handler = "Customer"
94+
handler = handlerNameStart == length ? null : methodName.Substring(handlerNameStart, length - handlerNameStart);
95+
}
96+
else if (
97+
string.Equals(httpMethod, "DELETE", StringComparison.OrdinalIgnoreCase) ||
98+
string.Equals(httpMethod, "PUT", StringComparison.OrdinalIgnoreCase))
99+
{
100+
// e.g. DeleteUser
101+
// HttpMethod = "POST", Handler = "DeleteUser"
102+
httpMethod = "POST";
103+
handler = methodName;
104+
}
105+
else
106+
{
107+
return false;
108+
}
109+
110+
return true;
111+
}
112+
}
113+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<Import Project="..\..\build\dependencies.props" />
4+
5+
<PropertyGroup>
6+
<IsPackable>false</IsPackable>
7+
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreVersion)" />
12+
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(AspNetCoreVersion)" />
13+
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCoreVersion)" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@page "{handler?}"
2+
@functions{
3+
public string Message { get; private set; }
4+
5+
public void Get()
6+
{
7+
Message = "Hello World";
8+
}
9+
10+
public void Post(string message)
11+
{
12+
Message = $"{nameof(Post)} handler received \"{message}\"";
13+
}
14+
15+
public void Delete()
16+
{
17+
Message = $"{nameof(Delete)} handler invoked";
18+
}
19+
20+
public void PutMessage(string message)
21+
{
22+
Message = $"{message} from {nameof(PutMessage)}";
23+
}
24+
}
25+
26+
<h2>Hello @Message!</h2>
27+
28+
<form method="post">
29+
<label>Say <input type="text" name="message" value="@Message" /></label>
30+
31+
<fieldset style="border: 0px; margin: 0px; padding: 4px 0px">
32+
<button type="submit" formaction="Delete">Delete</button>
33+
<button type="submit" formaction="PutMessage">PutMessage</button>
34+
<button type="submit">Post</button>
35+
</fieldset>
36+
</form>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Mvc.CustomRazorPageHandlers
2+
===
3+
4+
This application exhibits creating an IPageApplicationModelProvider that allows customizing the naming format for handlers on a Razor Page \ PageModel.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.IO;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Hosting;
7+
using Microsoft.AspNetCore.Mvc.ApplicationModels;
8+
using Microsoft.Extensions.DependencyInjection;
9+
10+
namespace Mvc.CustomRazorPageHandlers
11+
{
12+
public class Startup
13+
{
14+
// Set up application services
15+
public void ConfigureServices(IServiceCollection services)
16+
{
17+
services.AddMvc();
18+
services.AddSingleton<IPageApplicationModelProvider, CustomHandlerPageApplicationModelProvider>();
19+
}
20+
21+
public void Configure(IApplicationBuilder app)
22+
{
23+
app.UseMvc();
24+
}
25+
26+
public static void Main(string[] args)
27+
{
28+
var host = new WebHostBuilder()
29+
.UseContentRoot(Directory.GetCurrentDirectory())
30+
.UseIISIntegration()
31+
.UseKestrel()
32+
.UseStartup<Startup>()
33+
.Build();
34+
35+
host.Run();
36+
}
37+
}
38+
}

samples/Mvc.DynamicCorsPolicyProvider/Mvc.DynamicCorsPolicyProvider.sln

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

samples/Mvc.EmbeddedViewSample.Web/EmbeddedResources/Views/Home/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
<li>@Html.ActionLink(linkText: "Echo action", actionName: "EchoActionUrl")</li>
1111
</ul>
1212
</body>
13-
</html>
13+
</html>

test/Entropy.FunctionalTests/Entropy.FunctionalTests.csproj

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,6 @@
1313
<RootNamespace>Entropy.FunctionalTests</RootNamespace>
1414
</PropertyGroup>
1515

16-
<PropertyGroup Condition=" '$(TargetFramework)' != 'netcoreapp2.0' ">
17-
<!-- Work around https://github.com/dotnet/sdk/issues/926. Align with bitness of the web site projects. -->
18-
<PlatformTarget>x86</PlatformTarget>
19-
20-
<!--
21-
Work around https://github.com/Microsoft/vstest/issues/428 aka https://github.com/aspnet/Mvc/issues/5873.
22-
Create the appropriate binding redirects.
23-
-->
24-
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
25-
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
26-
</PropertyGroup>
27-
2816
<ItemGroup>
2917
<EmbeddedResource Include="resources\**\*" />
3018
<Content Include="nginx.conf" CopyToOutputDirectory="PreserveNewest" />
@@ -36,6 +24,7 @@
3624
<ProjectReference Include="..\..\samples\Antiforgery.MvcWithAuthAndAjax\Antiforgery.MvcWithAuthAndAjax.csproj" />
3725
<ProjectReference Include="..\..\samples\Mvc.ActionConstraintSample.Web\Mvc.ActionConstraintSample.Web.csproj" />
3826
<ProjectReference Include="..\..\samples\Mvc.ControllerForMultipleAreasSample\Mvc.ControllerForMultipleAreasSample.csproj" />
27+
<ProjectReference Include="..\..\samples\Mvc.CustomRazorPageHandlers\Mvc.CustomRazorPageHandlers.csproj" />
3928
<ProjectReference Include="..\..\samples\Mvc.CustomRouteSample.Web\Mvc.CustomRouteSample.Web.csproj" />
4029
<ProjectReference Include="..\..\samples\Mvc.CustomRoutingConvention\Mvc.CustomRoutingConvention.csproj" />
4130
<ProjectReference Include="..\..\samples\Mvc.EmbeddedViewSample.Web\Mvc.EmbeddedViewSample.Web.csproj" />

0 commit comments

Comments
 (0)