Merge branch 'merge/release/2.2-to-master' of https://github.com/dotnet-maestro-bot/Mvc into dotnet-maestro-bot-merge/release/2.2-to-master
This commit is contained in:
commit
f7d9271d71
17
Mvc.sln
17
Mvc.sln
|
|
@ -178,7 +178,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicViews", "benchmarkapps
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Api.Analyzers.Test", "test\Mvc.Api.Analyzers.Test\Mvc.Api.Analyzers.Test.csproj", "{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mvc.Api.Analyzers.Test", "test\Mvc.Api.Analyzers.Test\Mvc.Api.Analyzers.Test.csproj", "{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Api.Analyzers", "src\Microsoft.AspNetCore.Mvc.Api.Analyzers\Microsoft.AspNetCore.Mvc.Api.Analyzers.csproj", "{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Api.Analyzers", "src\Microsoft.AspNetCore.Mvc.Api.Analyzers\Microsoft.AspNetCore.Mvc.Api.Analyzers.csproj", "{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorRendering", "benchmarkapps\RazorRendering\RazorRendering.csproj", "{D7C6A696-F232-4288-BCCD-367407E4A934}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
@ -952,6 +954,18 @@ Global
|
||||||
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|x86.ActiveCfg = Release|Any CPU
|
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|x86.Build.0 = Release|Any CPU
|
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -1025,6 +1039,7 @@ Global
|
||||||
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
|
{E89EB74D-C1CE-456F-B42D-CCF1575E0CFB} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
|
||||||
{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{DD7B9F20-354C-4D9E-8C8A-8AE6E7595A87} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
{3B550487-10E4-4E6D-9CEF-B1B4CA1253DA} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||||
|
{D7C6A696-F232-4288-BCCD-367407E4A934} = {2859F266-673A-45A2-9E3C-7B39C6DDD38E}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {63D344F6-F86D-40E6-85B9-0AABBE338C4A}
|
SolutionGuid = {63D344F6-F86D-40E6-85B9-0AABBE338C4A}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
|
|
||||||
|
namespace Data
|
||||||
|
{
|
||||||
|
public class DataA
|
||||||
|
{
|
||||||
|
public DataA(int id, HtmlString icon, HtmlString html, string name, int seconds, int max, float perHour)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Icon = icon;
|
||||||
|
Html = html;
|
||||||
|
Name = name;
|
||||||
|
Seconds = seconds;
|
||||||
|
Max = max;
|
||||||
|
PerHour = perHour;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; }
|
||||||
|
public HtmlString Icon { get; }
|
||||||
|
public HtmlString Html { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public int Seconds { get; }
|
||||||
|
public int Max { get; }
|
||||||
|
public float PerHour { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
|
|
||||||
|
namespace Data
|
||||||
|
{
|
||||||
|
public class DataB
|
||||||
|
{
|
||||||
|
public DataB(int id, HtmlString icon, string name, int value, DateTimeOffset startDate, DateTimeOffset completeDate)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Icon = icon;
|
||||||
|
Name = name;
|
||||||
|
Value = value;
|
||||||
|
StartDate = startDate;
|
||||||
|
CompleteDate = completeDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; }
|
||||||
|
public HtmlString Icon { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public int Value { get; }
|
||||||
|
public DateTimeOffset StartDate { get; }
|
||||||
|
public DateTimeOffset CompleteDate { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
@page
|
||||||
|
@model Pages.PageA
|
||||||
|
@using static System.Convert
|
||||||
|
|
||||||
|
@section Subcategories {
|
||||||
|
<partial name="_Subcategories" />
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Tabs {
|
||||||
|
<span class="dialog-tab@(Model.Condition ? " selected" : "")"><span class="link" data-url="/Category/PageA">Sub cat A</span></span>
|
||||||
|
<span class="dialog-tab"><span class="link" data-url="/Category/PageB/@Model.Value1/@Model.Value2">Sub cat B</span></span>
|
||||||
|
<span class="dialog-tab"><span class="link" data-url="/Category/PageC/@((int)Model.Value3)">Sub cat C</span></span>
|
||||||
|
<span class="dialog-tab@(!Model.Condition ? " selected" : "")"><span class="link" data-url="/Category/PageD/@((int)Model.Value3)">Sub cat D</span></span>
|
||||||
|
}
|
||||||
|
|
||||||
|
@switch (Model.Value)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
<h1>@Model.Name Type A</h1>
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
<h1>@Model.Name Type B</h1>
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
<h1>@Model.Name Type C</h1>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<h3 style="clear:both;">Something Something</h3>
|
||||||
|
<div>
|
||||||
|
<table class="generic small oddeven middle center">
|
||||||
|
<tr>
|
||||||
|
<th style="width: 24px;"></th>
|
||||||
|
<th>Something</th>
|
||||||
|
<th style="width: 270px;">Something Something</th>
|
||||||
|
@if (Model.Value3 != 0)
|
||||||
|
{
|
||||||
|
<th style="width: 70px;" class="dull right">Something</th>
|
||||||
|
}
|
||||||
|
<th style="width: 130px;"></th>
|
||||||
|
</tr>
|
||||||
|
@{
|
||||||
|
foreach (var data in Model.Data1)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td class="left">@data.Icon</td>
|
||||||
|
<td class="left">@data.Name</td>
|
||||||
|
<td style="text-align:center">@data.Html</td>
|
||||||
|
@if (Model.Value3 != 0)
|
||||||
|
{
|
||||||
|
<td class="dull right">
|
||||||
|
@(new TimeSpan(0, 0, (int)data.Seconds)) <br />
|
||||||
|
(@data.PerHour.ToString("N2") p/h)
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
<td>
|
||||||
|
<form asp-page-handler="Make">
|
||||||
|
<input type="hidden" name="Id" value="@((int)Model.Value3)" />
|
||||||
|
<input type="hidden" name="ResType" value="@data.Id" />
|
||||||
|
<input type="text" name="Quantity" style="width: 35px; font-size: 11px; text-align: left;" placeholder="@data.Max.ToString(System.Globalization.NumberFormatInfo.InvariantInfo)" />
|
||||||
|
<a href="#" class="max">max</a>
|
||||||
|
<button class="small">Make</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 style="clear:both;">Something something something</h3>
|
||||||
|
<div>
|
||||||
|
@{
|
||||||
|
if (Model.Data2.Count > 0)
|
||||||
|
{
|
||||||
|
<table class="oddeven" style="font-size: 11px;">
|
||||||
|
<tr>
|
||||||
|
<th style="width:20px"></th>
|
||||||
|
<th class="left">SomethingA</th>
|
||||||
|
<th style="width: 80px;">SomethingB</th>
|
||||||
|
<th style="width: 60px">SomethingC</th>
|
||||||
|
<th>SomethingD</th>
|
||||||
|
<th style="width: 60px">SomethingE</th>
|
||||||
|
<th style="width: 90px;">SomethingF</th>
|
||||||
|
<th style="width: 56px;"></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@foreach (var data in Model.Data2)
|
||||||
|
{
|
||||||
|
var StartDate = data.StartDate;
|
||||||
|
var CompleteDate = data.CompleteDate;
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@data.Icon</td>
|
||||||
|
<td class="left">@data.Name</td>
|
||||||
|
<td>@data.Value</td>
|
||||||
|
<td class="center">@StartDate.ToString("dd MMM HH:mm:ss")</td>
|
||||||
|
@{
|
||||||
|
float percentage = 100f;
|
||||||
|
|
||||||
|
var totalTime = CompleteDate.Subtract(StartDate).TotalMilliseconds;
|
||||||
|
if (totalTime > 1000)
|
||||||
|
{
|
||||||
|
percentage = 100f * (float)DateTimeOffset.UtcNow.Subtract(StartDate).TotalMilliseconds / (float)totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
percentage = MathF.Min(100f, MathF.Max(0f, percentage));
|
||||||
|
|
||||||
|
var startDate = ToInt64(StartDate.Subtract(DateTime.UnixEpoch).Ticks / (double)10000);
|
||||||
|
var endDate = ToInt64(CompleteDate.Subtract(DateTime.UnixEpoch).Ticks / (double)10000);
|
||||||
|
}
|
||||||
|
<td>
|
||||||
|
<div class="progBar">
|
||||||
|
<div class="progProgress" data-start="@startDate" data-end="@endDate" style="width: @(percentage.ToString("N0"))%;"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="center">@CompleteDate.ToString("dd MMM HH:mm:ss")</td>
|
||||||
|
<td>
|
||||||
|
<span class="progTime" data-start="@startDate" data-end="@endDate">@(CompleteDate.Subtract(DateTime.UtcNow).ToString())</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form method="post" asp-page-handler="Cancel">
|
||||||
|
<input type="hidden" name="id" value="@data.Id" />
|
||||||
|
<button class="small">Cancel</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>Something @Model.Name something something something something.</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
using Data;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Pages
|
||||||
|
{
|
||||||
|
public class PageA : Page
|
||||||
|
{
|
||||||
|
public int Value { get; } = 0;
|
||||||
|
public int Value1 { get; } = 0;
|
||||||
|
public int Value2 { get; } = 0;
|
||||||
|
public int Value3 { get; } = 1;
|
||||||
|
public bool Condition { get; } = true;
|
||||||
|
|
||||||
|
public string Name { get; } = "A Name";
|
||||||
|
|
||||||
|
public List<DataA> Data1 { get; }
|
||||||
|
public List<DataB> Data2 { get; }
|
||||||
|
|
||||||
|
public PageA(List<DataA> dataA, List<DataB> dataB, ILogger<PageA> logger) : base(logger)
|
||||||
|
{
|
||||||
|
Data1 = dataA;
|
||||||
|
Data2 = dataB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnGetAsync()
|
||||||
|
{
|
||||||
|
PageTitle = "PageA Title";
|
||||||
|
PageIcon = "sicon dialogue_pagea";
|
||||||
|
await Task.Delay(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
@model Pages.Page
|
||||||
|
|
||||||
|
<div class="dialog-subcat">
|
||||||
|
<div data-url="/Category/PageA" title="PageA Title" class="sicon dialogue_pagea"></div>
|
||||||
|
<div data-url="/Category/PageB" title="PageB Title" class="sicon dialogue_pageb"></div>
|
||||||
|
<div data-url="/Category/PageC" title="PageC Title" class="sicon dialogue_pagec"></div>
|
||||||
|
<div data-url="/Category/PageD" title="PageD Title" class="sicon dialogue_paged"></div>
|
||||||
|
<div data-url="/Category/PageE" title="PageE Title" class="sicon dialogue_pagee"></div>
|
||||||
|
<div data-url="/Category/PageF" title="PageF Title" class="sicon dialogue_pagef"></div>
|
||||||
|
<div data-url="/Category/PageG" title="PageG Title" class="sicon dialogue_pageg"></div>
|
||||||
|
</div>
|
||||||
|
<div class="licon micon dialogue_category"></div>
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Pages
|
||||||
|
{
|
||||||
|
public class Page : PageModel
|
||||||
|
{
|
||||||
|
public ILogger Logger { get; }
|
||||||
|
|
||||||
|
public string PageIcon { get; protected set; }
|
||||||
|
public string PageTitle { get; protected set; }
|
||||||
|
public string PageUrl { get; protected set; }
|
||||||
|
|
||||||
|
public Page(ILogger logger)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void AddErrorMessage(string message)
|
||||||
|
{
|
||||||
|
Response.Headers.Add("X-Error-Message", UrlEncoder.Default.Encode(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
@model Pages.Page
|
||||||
|
|
||||||
|
@RenderSection("Subcategories")
|
||||||
|
<div class="dialog-title-icon @Model.PageIcon"></div>
|
||||||
|
<div class="dialog-title">@Model.PageTitle</div>
|
||||||
|
@{
|
||||||
|
var hasTabs = IsSectionDefined("Tabs");
|
||||||
|
}
|
||||||
|
<div class="dialog-body@(hasTabs ? "-tabs" : "")">
|
||||||
|
@if (hasTabs)
|
||||||
|
{
|
||||||
|
<div class="dialog-tabs">
|
||||||
|
@RenderSection("Tabs", required: false)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="dialog-parch-bottom@(hasTabs ? " tabs" : "")">
|
||||||
|
<div id="contentScroll" tabindex="20" class="dialog-parch-scroll@(hasTabs ? " tabs" : "")">
|
||||||
|
<div class="dialog-parch-left">
|
||||||
|
<div class="dialog-parch-right">
|
||||||
|
<div id="contentWindow" class="dialog-parch@(hasTabs ? " tabs" : "")">
|
||||||
|
@RenderBody()
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-parch-shadow@(hasTabs ? " tabs" : "")"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-close-icon icon_close"></div>
|
||||||
|
<div class="dialog-bottom-left-edge"></div>
|
||||||
|
<div class="dialog-bottom-edge"></div>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- These references are used when running locally -->
|
||||||
|
<ItemGroup Condition="'$(BenchmarksTargetFramework)' == ''">
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||||
|
|
||||||
|
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
These references are used when running on the Benchmarks Server.
|
||||||
|
-->
|
||||||
|
<ItemGroup Condition="'$(BenchmarksTargetFramework)' != ''">
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.App" Version="$(MicrosoftNETCoreApp22PackageVersion)" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Properties\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Url: /Category/PageA
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Data;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<List<DataA>>(_ => DataA);
|
||||||
|
services.AddScoped<List<DataB>>(_ => DataB);
|
||||||
|
services.AddMvc()
|
||||||
|
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DataA> DataA = GenerateDataA();
|
||||||
|
|
||||||
|
private static List<DataA> GenerateDataA()
|
||||||
|
{
|
||||||
|
var dataA = new List<DataA>();
|
||||||
|
|
||||||
|
foreach (var i in Enumerable.Range(0, 100))
|
||||||
|
{
|
||||||
|
dataA.Add(new DataA(i, new HtmlString(i.ToString()), new HtmlString(i.ToString()), i.ToString(), i, i, 60f / i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataA;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DataB> DataB = GenerateDataB();
|
||||||
|
|
||||||
|
private static List<DataB> GenerateDataB()
|
||||||
|
{
|
||||||
|
var utc = DateTimeOffset.UtcNow;
|
||||||
|
var dataB = new List<DataB>();
|
||||||
|
foreach (var i in Enumerable.Range(0, 100))
|
||||||
|
{
|
||||||
|
dataB.Add(new DataB(i, new HtmlString(i.ToString()), i.ToString(), i, utc, utc));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var host = CreateWebHostBuilder(args)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
host.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||||
|
{
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.AddCommandLine(args)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return new WebHostBuilder()
|
||||||
|
.UseKestrel()
|
||||||
|
.UseUrls("http://+:5000")
|
||||||
|
.UseConfiguration(configuration)
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
|
||||||
|
|
||||||
var state = routeContext.RouteData.PushState(MockRouter.Instance, routeValues, null);
|
var state = routeContext.RouteData.PushState(MockRouter.Instance, routeValues, null);
|
||||||
|
|
||||||
var actual = NaiveSelectCandiates(_actions, routeContext.RouteData.Values);
|
var actual = NaiveSelectCandidates(_actions, routeContext.RouteData.Values);
|
||||||
Verify(expected, actual);
|
Verify(expected, actual);
|
||||||
|
|
||||||
state.Restore();
|
state.Restore();
|
||||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
|
||||||
}
|
}
|
||||||
|
|
||||||
// A naive implementation we can use to generate match data for inputs, and for a baseline.
|
// A naive implementation we can use to generate match data for inputs, and for a baseline.
|
||||||
private static IReadOnlyList<ActionDescriptor> NaiveSelectCandiates(ActionDescriptor[] actions, RouteValueDictionary routeValues)
|
private static IReadOnlyList<ActionDescriptor> NaiveSelectCandidates(ActionDescriptor[] actions, RouteValueDictionary routeValues)
|
||||||
{
|
{
|
||||||
var results = new List<ActionDescriptor>();
|
var results = new List<ActionDescriptor>();
|
||||||
for (var i = 0; i < actions.Length; i++)
|
for (var i = 0; i < actions.Length; i++)
|
||||||
|
|
@ -175,7 +175,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
|
||||||
{
|
{
|
||||||
var action = actions[i];
|
var action = actions[i];
|
||||||
var routeValues = new RouteValueDictionary(action.RouteValues);
|
var routeValues = new RouteValueDictionary(action.RouteValues);
|
||||||
var matches = NaiveSelectCandiates(actions, routeValues);
|
var matches = NaiveSelectCandidates(actions, routeValues);
|
||||||
if (matches.Count == 0)
|
if (matches.Count == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("This should have at least one match.");
|
throw new InvalidOperationException("This should have at least one match.");
|
||||||
|
|
@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.Performance
|
||||||
// Make one of the route values not match.
|
// Make one of the route values not match.
|
||||||
routeValues[routeValues.First().Key] = ((string)routeValues.First().Value) + "fkdkfdkkf";
|
routeValues[routeValues.First().Key] = ((string)routeValues.First().Value) + "fkdkfdkkf";
|
||||||
|
|
||||||
var matches = NaiveSelectCandiates(actions, routeValues);
|
var matches = NaiveSelectCandidates(actions, routeValues);
|
||||||
if (matches.Count != 0)
|
if (matches.Count != 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("This should have 0 matches.");
|
throw new InvalidOperationException("This should have 0 matches.");
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@
|
||||||
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreResponseCachingPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
<MicrosoftAspNetCoreResponseCachingPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>3.0.0-a-alpha1-link-generator-master-16955</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreRoutingPackageVersion>
|
<MicrosoftAspNetCoreRoutingPackageVersion>3.0.0-a-alpha1-link-generator-master-16955</MicrosoftAspNetCoreRoutingPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
<MicrosoftAspNetCoreServerKestrelPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||||
<MicrosoftAspNetCoreSessionPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreSessionPackageVersion>
|
<MicrosoftAspNetCoreSessionPackageVersion>3.0.0-alpha1-10400</MicrosoftAspNetCoreSessionPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<IActionConstraintMetadata> ActionConstraints { get; set; }
|
public IList<IActionConstraintMetadata> ActionConstraints { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the endpoint metadata for this action.
|
||||||
|
/// </summary>
|
||||||
public IList<object> EndpointMetadata { get; set; }
|
public IList<object> EndpointMetadata { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,11 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// <param name="actionContext">The <see cref="ActionContext"/> to copy.</param>
|
/// <param name="actionContext">The <see cref="ActionContext"/> to copy.</param>
|
||||||
public ActionContext(ActionContext actionContext)
|
public ActionContext(ActionContext actionContext)
|
||||||
: this(
|
: this(
|
||||||
actionContext.HttpContext,
|
actionContext?.HttpContext,
|
||||||
actionContext.RouteData,
|
actionContext?.RouteData,
|
||||||
actionContext.ActionDescriptor,
|
actionContext?.ActionDescriptor,
|
||||||
actionContext.ModelState)
|
actionContext?.ModelState)
|
||||||
{
|
{
|
||||||
if (actionContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(actionContext));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Intializes a new instance of <see cref="ActionResult{TValue}"/> using the specified <see cref="ActionResult"/>.
|
/// Initializes a new instance of <see cref="ActionResult{TValue}"/> using the specified <see cref="ActionResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="result">The <see cref="ActionResult"/>.</param>
|
/// <param name="result">The <see cref="ActionResult"/>.</param>
|
||||||
public ActionResult(ActionResult result)
|
public ActionResult(ActionResult result)
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
else if (methods.Length > 1)
|
else if (methods.Length > 1)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(Resources.FormatApiConventionMethod_AmbigiousMethodName(methodName, conventionType), nameof(methodName));
|
throw new ArgumentException(Resources.FormatApiConventionMethod_AmbiguousMethodName(methodName, conventionType), nameof(methodName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return methods[0];
|
return methods[0];
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
/// Gets the list of <see cref="ApplicationPart"/> instances.
|
/// Gets the list of <see cref="ApplicationPart"/> instances.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Instances in this collection are stored in precedence order. An <see cref="ApplicationPart"/> that appears
|
/// Instances in this collection are stored in precedence order. An <see cref="ApplicationPart"/> that appears
|
||||||
/// earlier in the list has a higher precendence.
|
/// earlier in the list has a higher precedence.
|
||||||
/// An <see cref="IApplicationFeatureProvider"/> may choose to use this an interface as a way to resolve conflicts when
|
/// An <see cref="IApplicationFeatureProvider"/> may choose to use this an interface as a way to resolve conflicts when
|
||||||
/// multiple <see cref="ApplicationPart"/> instances resolve equivalent feature values.
|
/// multiple <see cref="ApplicationPart"/> instances resolve equivalent feature values.
|
||||||
/// </para>
|
/// </para>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// By default, this maps to <see cref="ProblemDetails.Title"/> and should not change
|
/// By default, this maps to <see cref="ProblemDetails.Title"/> and should not change
|
||||||
/// between multiple occurences of the same error.
|
/// between multiple occurrences of the same error.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// </example>
|
/// </example>
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Setting compatiblity version to a specific version will change the default values of various
|
/// Setting compatibility version to a specific version will change the default values of various
|
||||||
/// settings to match a particular minor release of ASP.NET Core MVC.
|
/// settings to match a particular minor release of ASP.NET Core MVC.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
// If there are multiple IConsumeActionConstraints which are defined at the class and
|
// If there are multiple IConsumeActionConstraints which are defined at the class and
|
||||||
// at the action level, the one closest to the action overrides the others. To ensure this
|
// at the action level, the one closest to the action overrides the others. To ensure this
|
||||||
// we take advantage of the fact that ConsumesAttribute is both an IActionFilter and an
|
// we take advantage of the fact that ConsumesAttribute is both an IActionFilter and an
|
||||||
// IConsumeActionConstraint. Since filterdescriptor collection is ordered (the last filter is the one
|
// IConsumeActionConstraint. Since FilterDescriptor collection is ordered (the last filter is the one
|
||||||
// closest to the action), we apply this constraint only if there is no IConsumeActionConstraint after this.
|
// closest to the action), we apply this constraint only if there is no IConsumeActionConstraint after this.
|
||||||
return actionDescriptor.FilterDescriptors.Last(
|
return actionDescriptor.FilterDescriptors.Last(
|
||||||
filter => filter.Filter is IConsumesActionConstraint).Filter == this;
|
filter => filter.Filter is IConsumesActionConstraint).Filter == this;
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
///
|
///
|
||||||
/// <code>
|
/// <code>
|
||||||
/// [HttpGet]
|
/// [HttpGet]
|
||||||
/// public ProductModel GetProduct([FromServices] IProductModelRequestService productModelReqest)
|
/// public ProductModel GetProduct([FromServices] IProductModelRequestService productModelRequest)
|
||||||
/// {
|
/// {
|
||||||
/// return productModelReqest.Value;
|
/// return productModelRequest.Value;
|
||||||
/// }
|
/// }
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate DefaultValues onces so subclasses don't have to cache.
|
// Evaluate DefaultValues once so subclasses don't have to cache.
|
||||||
var defaultValues = DefaultValues;
|
var defaultValues = DefaultValues;
|
||||||
|
|
||||||
foreach (var @switch in options)
|
foreach (var @switch in options)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
@ -134,7 +133,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
// Consumers who poll will observe a new action descriptor collection at step 2 - they will see
|
// Consumers who poll will observe a new action descriptor collection at step 2 - they will see
|
||||||
// the new collection and ignore the change token.
|
// the new collection and ignore the change token.
|
||||||
//
|
//
|
||||||
// Consumers who listen to the change token will requery at step 4 - they will see the new collection
|
// Consumers who listen to the change token will re-query at step 4 - they will see the new collection
|
||||||
// and new change token.
|
// and new change token.
|
||||||
//
|
//
|
||||||
// Anyone who acquires the collection and change token between steps 2 and 3 will be notified of
|
// Anyone who acquires the collection and change token between steps 2 and 3 will be notified of
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
///</para>
|
///</para>
|
||||||
///<para>
|
///<para>
|
||||||
/// To be reactively notified of changes, downcast to <see cref="ActionDescriptorCollectionProvider"/> and
|
/// To be reactively notified of changes, downcast to <see cref="ActionDescriptorCollectionProvider"/> and
|
||||||
/// subcribe to the change token returned from <see cref="ActionDescriptorCollectionProvider.GetChangeToken"/>
|
/// subscribe to the change token returned from <see cref="ActionDescriptorCollectionProvider.GetChangeToken"/>
|
||||||
/// using <see cref="ChangeToken.OnChange(System.Func{IChangeToken}, System.Action)"/>.
|
/// using <see cref="ChangeToken.OnChange(System.Func{IChangeToken}, System.Action)"/>.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TResult">The type of <see cref="IActionResult"/>.</typeparam>
|
/// <typeparam name="TResult">The type of <see cref="IActionResult"/>.</typeparam>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Implementions of <see cref="IActionResultExecutor{TResult}"/> are typically called by the
|
/// Implementations of <see cref="IActionResultExecutor{TResult}"/> are typically called by the
|
||||||
/// <see cref="IActionResult.ExecuteResultAsync(ActionContext)"/> method of the corresponding action result type.
|
/// <see cref="IActionResult.ExecuteResultAsync(ActionContext)"/> method of the corresponding action result type.
|
||||||
/// Implementations should be registered as singleton services.
|
/// Implementations should be registered as singleton services.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public interface IActionResultExecutor<in TResult> where TResult : IActionResult
|
public interface IActionResultExecutor<in TResult> where TResult : IActionResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asynchronously excecutes the action result, by modifying the <see cref="HttpResponse"/>.
|
/// Asynchronously executes the action result, by modifying the <see cref="HttpResponse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The <see cref="ActionContext"/> associated with the current request."/></param>
|
/// <param name="context">The <see cref="ActionContext"/> associated with the current request."/></param>
|
||||||
/// <param name="result">The action result to execute.</param>
|
/// <param name="result">The action result to execute.</param>
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
=> typeof(Task<IActionResult>).IsAssignableFrom(executor.MethodReturnType);
|
=> typeof(Task<IActionResult>).IsAssignableFrom(executor.MethodReturnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Task<PhysicalfileResult> DownloadFile(..)
|
// Task<PhysicalFileResult> DownloadFile(..)
|
||||||
// ValueTask<ViewResult> GetViewsAsync(..)
|
// ValueTask<ViewResult> GetViewsAsync(..)
|
||||||
private class TaskOfActionResultExecutor : ActionMethodExecutor
|
private class TaskOfActionResultExecutor : ActionMethodExecutor
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
// We also want to add the same (as in reference equality) list of actions to the ordinal entries.
|
// We also want to add the same (as in reference equality) list of actions to the ordinal entries.
|
||||||
// We'll keep updating `entries` to include all of the actions in the same equivalence class -
|
// We'll keep updating `entries` to include all of the actions in the same equivalence class -
|
||||||
// meaning, all conventionally routed actions for which the route values are equalignoring case.
|
// meaning, all conventionally routed actions for which the route values are equal ignoring case.
|
||||||
//
|
//
|
||||||
// `entries` will appear in `OrdinalIgnoreCaseEntries` exactly once and in `OrdinalEntries` once
|
// `entries` will appear in `OrdinalIgnoreCaseEntries` exactly once and in `OrdinalEntries` once
|
||||||
// for each variation of casing that we've seen.
|
// for each variation of casing that we've seen.
|
||||||
|
|
|
||||||
|
|
@ -138,10 +138,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
ActionModel action)
|
ActionModel action)
|
||||||
{
|
{
|
||||||
var defaultControllerConstraints = Enumerable.Empty<IActionConstraintMetadata>();
|
var defaultControllerConstraints = Enumerable.Empty<IActionConstraintMetadata>();
|
||||||
|
var defaultControllerEndpointMetadata = Enumerable.Empty<object>();
|
||||||
if (controller.Selectors.Count > 0)
|
if (controller.Selectors.Count > 0)
|
||||||
{
|
{
|
||||||
defaultControllerConstraints = controller.Selectors[0].ActionConstraints
|
defaultControllerConstraints = controller.Selectors[0].ActionConstraints
|
||||||
.Where(constraint => !(constraint is IRouteTemplateProvider));
|
.Where(constraint => !(constraint is IRouteTemplateProvider));
|
||||||
|
defaultControllerEndpointMetadata = controller.Selectors[0].EndpointMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionDescriptors = new List<ControllerActionDescriptor>();
|
var actionDescriptors = new List<ControllerActionDescriptor>();
|
||||||
|
|
@ -164,8 +166,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
AddActionConstraints(actionDescriptor, actionSelector, controllerConstraints);
|
AddActionConstraints(actionDescriptor, actionSelector, controllerConstraints);
|
||||||
|
|
||||||
// REVIEW: Need to get metadata from controller
|
// Metadata for the action is more significant so order it before the controller metadata
|
||||||
actionDescriptor.EndpointMetadata = actionSelector.EndpointMetadata.ToList();
|
var actionDescriptorMetadata = actionSelector.EndpointMetadata.ToList();
|
||||||
|
actionDescriptorMetadata.AddRange(defaultControllerEndpointMetadata);
|
||||||
|
|
||||||
|
actionDescriptor.EndpointMetadata = actionDescriptorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
return actionDescriptors;
|
return actionDescriptors;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
public RequestDelegate GetPipeline(Type configurationType)
|
public RequestDelegate GetPipeline(Type configurationType)
|
||||||
{
|
{
|
||||||
// Build the pipeline only once. This is similar to how middlewares registered in Startup are constructed.
|
// Build the pipeline only once. This is similar to how middleware registered in Startup are constructed.
|
||||||
|
|
||||||
var requestDelegate = _pipelinesCache.GetOrAdd(
|
var requestDelegate = _pipelinesCache.GetOrAdd(
|
||||||
configurationType,
|
configurationType,
|
||||||
|
|
@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideally we want the experience of a middleware pipeline to behave the same as if it was registered
|
// Ideally we want the experience of a middleware pipeline to behave the same as if it was registered
|
||||||
// in Startup. In this scenario, an Exception thrown in a middelware later in the pipeline gets
|
// in Startup. In this scenario, an Exception thrown in a middleware later in the pipeline gets
|
||||||
// propagated back to earlier middleware. So, check if a later resource filter threw an Exception and
|
// propagated back to earlier middleware. So, check if a later resource filter threw an Exception and
|
||||||
// propagate that back to the middleware pipeline.
|
// propagate that back to the middleware pipeline.
|
||||||
resourceExecutedContext.ExceptionDispatchInfo?.Throw();
|
resourceExecutedContext.ExceptionDispatchInfo?.Throw();
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// - /Home/Index/{id?}
|
// - /Home/Index/{id?}
|
||||||
// - /Home
|
// - /Home
|
||||||
// - /
|
// - /
|
||||||
if (UseDefaultValuePlusRemainingSegementsOptional(i, action, endpointInfo, newPathSegments))
|
if (UseDefaultValuePlusRemainingSegmentsOptional(i, action, endpointInfo, newPathSegments))
|
||||||
{
|
{
|
||||||
var subPathSegments = newPathSegments.Take(i);
|
var subPathSegments = newPathSegments.Take(i);
|
||||||
|
|
||||||
|
|
@ -270,7 +270,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseDefaultValuePlusRemainingSegementsOptional(
|
private bool UseDefaultValuePlusRemainingSegmentsOptional(
|
||||||
int segmentIndex,
|
int segmentIndex,
|
||||||
ActionDescriptor action,
|
ActionDescriptor action,
|
||||||
MvcEndpointInfo endpointInfo,
|
MvcEndpointInfo endpointInfo,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since
|
// If the model type is IEnumerable<> then we need to know if we can assign a List<> to it, since
|
||||||
// that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyColection<> and
|
// that's what we would create. (The cases handled here are IEnumerable<>, IReadOnlyCollection<> and
|
||||||
// IReadOnlyList<>).
|
// IReadOnlyList<>).
|
||||||
var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
|
var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
|
||||||
if (enumerableType != null)
|
if (enumerableType != null)
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
//
|
//
|
||||||
// If a property does not have a binding source, then it's fair game for any value provider.
|
// If a property does not have a binding source, then it's fair game for any value provider.
|
||||||
//
|
//
|
||||||
// If any property meets the above conditions and has a value from valueproviders, then we'll
|
// If any property meets the above conditions and has a value from ValueProviders, then we'll
|
||||||
// create the model and try to bind it. OR if ALL properties of the model have a greedy source,
|
// create the model and try to bind it. OR if ALL properties of the model have a greedy source,
|
||||||
// then we go ahead and create it.
|
// then we go ahead and create it.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
// enum FlagsEnum { Value1 = 1, Value2 = 2, Value4 = 4 }
|
// enum FlagsEnum { Value1 = 1, Value2 = 2, Value4 = 4 }
|
||||||
//
|
//
|
||||||
// Valid Scenarios:
|
// Valid Scenarios:
|
||||||
// 1. valueproviderresult="Value2,Value4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
|
// 1. valueProviderResult="Value2,Value4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
|
||||||
// 2. valueproviderresult="2,4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
|
// 2. valueProviderResult="2,4", model=Value2 | Value4, underlying=6, converted=Value2, Value4
|
||||||
//
|
//
|
||||||
// Invalid Scenarios:
|
// Invalid Scenarios:
|
||||||
// 1. valueproviderresult="2,10", model=12, underlying=12, converted=12
|
// 1. valueProviderResult="2,10", model=12, underlying=12, converted=12
|
||||||
//
|
//
|
||||||
var underlying = Convert.ChangeType(
|
var underlying = Convert.ChangeType(
|
||||||
model,
|
model,
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
var headerName = bindingContext.FieldName;
|
var headerName = bindingContext.FieldName;
|
||||||
|
|
||||||
// Do not set ModelBindingResult to Failed on not finding the value in the header as we want the inner
|
// Do not set ModelBindingResult to Failed on not finding the value in the header as we want the inner
|
||||||
// modelbinder to do that. This would give a chance to the inner binder to add more useful information.
|
// ModelBinder to do that. This would give a chance to the inner binder to add more useful information.
|
||||||
// For example, SimpleTypeModelBinder adds a model error when binding to let's say an integer and the
|
// For example, SimpleTypeModelBinder adds a model error when binding to let's say an integer and the
|
||||||
// model is null.
|
// model is null.
|
||||||
var request = bindingContext.HttpContext.Request;
|
var request = bindingContext.HttpContext.Request;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
// Normalizes keys, in a keyvaluepair collection, from jQuery format to a format that MVC understands.
|
// Normalizes keys, in a KeyValuePair collection, from jQuery format to a format that MVC understands.
|
||||||
internal static class JQueryKeyValuePairNormalizer
|
internal static class JQueryKeyValuePairNormalizer
|
||||||
{
|
{
|
||||||
public static IDictionary<string, StringValues> GetValues(
|
public static IDictionary<string, StringValues> GetValues(
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
|
||||||
var cacheEntry = GetCacheEntry(modelType);
|
var cacheEntry = GetCacheEntry(modelType);
|
||||||
|
|
||||||
// We're relying on a safe race-condition for Properties - take care only
|
// We're relying on a safe race-condition for Properties - take care only
|
||||||
// to set the value onces the properties are fully-initialized.
|
// to set the value once the properties are fully-initialized.
|
||||||
if (cacheEntry.Details.Properties == null)
|
if (cacheEntry.Details.Properties == null)
|
||||||
{
|
{
|
||||||
var key = ModelMetadataIdentity.ForType(modelType);
|
var key = ModelMetadataIdentity.ForType(modelType);
|
||||||
|
|
|
||||||
|
|
@ -1509,18 +1509,18 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConvention_UnsupportedAttributesOnConvention"), p0, p1, p2);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConvention_UnsupportedAttributesOnConvention"), p0, p1, p2);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method name '{0}' is ambigous for convention type '{1}'. More than one method found with the name '{0}'.
|
/// Method name '{0}' is ambiguous for convention type '{1}'. More than one method found with the name '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ApiConventionMethod_AmbigiousMethodName
|
internal static string ApiConventionMethod_AmbiguousMethodName
|
||||||
{
|
{
|
||||||
get => GetString("ApiConventionMethod_AmbigiousMethodName");
|
get => GetString("ApiConventionMethod_AmbiguousMethodName");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method name '{0}' is ambigous for convention type '{1}'. More than one method found with the name '{0}'.
|
/// Method name '{0}' is ambiguous for convention type '{1}'. More than one method found with the name '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatApiConventionMethod_AmbigiousMethodName(object p0, object p1)
|
internal static string FormatApiConventionMethod_AmbiguousMethodName(object p0, object p1)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConventionMethod_AmbigiousMethodName"), p0, p1);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConventionMethod_AmbiguousMethodName"), p0, p1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A method named '{0}' was not found on convention type '{1}'.
|
/// A method named '{0}' was not found on convention type '{1}'.
|
||||||
|
|
|
||||||
|
|
@ -451,8 +451,8 @@
|
||||||
<data name="ApiConvention_UnsupportedAttributesOnConvention" xml:space="preserve">
|
<data name="ApiConvention_UnsupportedAttributesOnConvention" xml:space="preserve">
|
||||||
<value>Method {0} is decorated with the following attributes that are not allowed on an API convention method:{1}The following attributes are allowed on API convention methods: {2}.</value>
|
<value>Method {0} is decorated with the following attributes that are not allowed on an API convention method:{1}The following attributes are allowed on API convention methods: {2}.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ApiConventionMethod_AmbigiousMethodName" xml:space="preserve">
|
<data name="ApiConventionMethod_AmbiguousMethodName" xml:space="preserve">
|
||||||
<value>Method name '{0}' is ambigous for convention type '{1}'. More than one method found with the name '{0}'.</value>
|
<value>Method name '{0}' is ambiguous for convention type '{1}'. More than one method found with the name '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ApiConventionMethod_NoMethodFound" xml:space="preserve">
|
<data name="ApiConventionMethod_NoMethodFound" xml:space="preserve">
|
||||||
<value>A method named '{0}' was not found on convention type '{1}'.</value>
|
<value>A method named '{0}' was not found on convention type '{1}'.</value>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
@ -51,46 +52,41 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
throw new ArgumentNullException(nameof(urlActionContext));
|
throw new ArgumentNullException(nameof(urlActionContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
var valuesDictionary = GetValuesDictionary(urlActionContext.Values);
|
var values = GetValuesDictionary(urlActionContext.Values);
|
||||||
|
|
||||||
if (urlActionContext.Action == null)
|
if (urlActionContext.Action == null)
|
||||||
{
|
{
|
||||||
if (!valuesDictionary.ContainsKey("action") &&
|
if (!values.ContainsKey("action") &&
|
||||||
AmbientValues.TryGetValue("action", out var action))
|
AmbientValues.TryGetValue("action", out var action))
|
||||||
{
|
{
|
||||||
valuesDictionary["action"] = action;
|
values["action"] = action;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
valuesDictionary["action"] = urlActionContext.Action;
|
values["action"] = urlActionContext.Action;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlActionContext.Controller == null)
|
if (urlActionContext.Controller == null)
|
||||||
{
|
{
|
||||||
if (!valuesDictionary.ContainsKey("controller") &&
|
if (!values.ContainsKey("controller") &&
|
||||||
AmbientValues.TryGetValue("controller", out var controller))
|
AmbientValues.TryGetValue("controller", out var controller))
|
||||||
{
|
{
|
||||||
valuesDictionary["controller"] = controller;
|
values["controller"] = controller;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
valuesDictionary["controller"] = urlActionContext.Controller;
|
values["controller"] = urlActionContext.Controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
|
|
||||||
|
var path = _linkGenerator.GetPathByRouteValues(
|
||||||
ActionContext.HttpContext,
|
ActionContext.HttpContext,
|
||||||
valuesDictionary,
|
routeName: null,
|
||||||
out var link);
|
values,
|
||||||
if (!successfullyGeneratedLink)
|
new FragmentString(urlActionContext.Fragment == null ? null : "#" + urlActionContext.Fragment));
|
||||||
{
|
return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, path);
|
||||||
//TODO: log here
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, link, urlActionContext.Fragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -101,20 +97,12 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
throw new ArgumentNullException(nameof(routeContext));
|
throw new ArgumentNullException(nameof(routeContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values);
|
var path = _linkGenerator.GetPathByRouteValues(
|
||||||
|
|
||||||
var successfullyGeneratedLink = _linkGenerator.TryGetLink(
|
|
||||||
ActionContext.HttpContext,
|
ActionContext.HttpContext,
|
||||||
routeContext.RouteName,
|
routeContext.RouteName,
|
||||||
valuesDictionary,
|
routeContext.Values,
|
||||||
out var link);
|
new FragmentString(routeContext.Fragment == null ? null : "#" + routeContext.Fragment));
|
||||||
|
return GenerateUrl(routeContext.Protocol, routeContext.Host, path);
|
||||||
if (!successfullyGeneratedLink)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenerateUrl(routeContext.Protocol, routeContext.Host, link, routeContext.Fragment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +201,68 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a URI from the provided components.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="protocol">The URI scheme/protocol.</param>
|
||||||
|
/// <param name="host">The URI host.</param>
|
||||||
|
/// <param name="path">The URI path and remaining portions (path, query, and fragment).</param>
|
||||||
|
/// <returns>
|
||||||
|
/// An absolute URI if the <paramref name="protocol"/> or <paramref name="host"/> is specified, otherwise generates a
|
||||||
|
/// URI with an absolute path.
|
||||||
|
/// </returns>
|
||||||
|
protected string GenerateUrl(string protocol, string host, string path)
|
||||||
|
{
|
||||||
|
// This method is similar to GenerateUrl, but it's used for EndpointRouting. It ignores pathbase and fragment
|
||||||
|
// because those have already been incorporated.
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perf: In most of the common cases, GenerateUrl is called with a null protocol, host and fragment.
|
||||||
|
// In such cases, we might not need to build any URL as the url generated is mostly same as the virtual path available in pathData.
|
||||||
|
// For such common cases, this FastGenerateUrl method saves a string allocation per GenerateUrl call.
|
||||||
|
string url;
|
||||||
|
if (TryFastGenerateUrl(protocol, host, path, fragment: null, out url))
|
||||||
|
{
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = GetStringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
|
||||||
|
{
|
||||||
|
AppendPathAndFragment(builder, pathBase: null, path, fragment: null);
|
||||||
|
|
||||||
|
// We're returning a partial URL (just path + query + fragment), but we still want it to be rooted.
|
||||||
|
if (builder.Length == 0 || builder[0] != '/')
|
||||||
|
{
|
||||||
|
builder.Insert(0, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
|
||||||
|
builder.Append(protocol);
|
||||||
|
|
||||||
|
builder.Append("://");
|
||||||
|
|
||||||
|
host = string.IsNullOrEmpty(host) ? ActionContext.HttpContext.Request.Host.Value : host;
|
||||||
|
builder.Append(host);
|
||||||
|
AppendPathAndFragment(builder, pathBase: null, path, fragment: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Clear the StringBuilder so that it can reused for the next call.
|
||||||
|
builder.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// for unit testing
|
// for unit testing
|
||||||
internal static void AppendPathAndFragment(StringBuilder builder, PathString pathBase, string virtualPath, string fragment)
|
internal static void AppendPathAndFragment(StringBuilder builder, PathString pathBase, string virtualPath, string fragment)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -462,7 +462,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
if (!routeValues.ContainsKey("handler") &&
|
if (!routeValues.ContainsKey("handler") &&
|
||||||
ambientValues.TryGetValue("handler", out var handler))
|
ambientValues.TryGetValue("handler", out var handler))
|
||||||
{
|
{
|
||||||
// Clear out formaction unless it's explicitly specified in the routeValues.
|
// Clear out form action unless it's explicitly specified in the routeValues.
|
||||||
routeValues["handler"] = null;
|
routeValues["handler"] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public class ValidationProblemDetails : ProblemDetails
|
public class ValidationProblemDetails : ProblemDetails
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Intializes a new instance of <see cref="ValidationProblemDetails"/>.
|
/// Initializes a new instance of <see cref="ValidationProblemDetails"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ValidationProblemDetails()
|
public ValidationProblemDetails()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a flag to determine whether error messsages from JSON deserialization by the
|
/// Gets or sets a flag to determine whether error messages from JSON deserialization by the
|
||||||
/// <see cref="JsonInputFormatter"/> will be added to the <see cref="ModelStateDictionary"/>. The default
|
/// <see cref="JsonInputFormatter"/> will be added to the <see cref="ModelStateDictionary"/>. The default
|
||||||
/// value is <c>false</c>, meaning that a generic error message will be used instead.
|
/// value is <c>false</c>, meaning that a generic error message will be used instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||||
|
|
||||||
if (duplicates != null)
|
if (duplicates != null)
|
||||||
{
|
{
|
||||||
var viewsDiffereningInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.Identifier));
|
var viewsDifferingInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.Identifier));
|
||||||
|
|
||||||
var message = string.Join(
|
var message = string.Join(
|
||||||
Environment.NewLine,
|
Environment.NewLine,
|
||||||
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
||||||
viewsDiffereningInCase);
|
viewsDifferingInCase);
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
// Ensure parts do not specify views with differing cases. This is not supported
|
// Ensure parts do not specify views with differing cases. This is not supported
|
||||||
// at runtime and we should flag at as such for precompiled views.
|
// at runtime and we should flag at as such for precompiled views.
|
||||||
var viewsDiffereningInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.RelativePath));
|
var viewsDifferingInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.RelativePath));
|
||||||
|
|
||||||
var message = string.Join(
|
var message = string.Join(
|
||||||
Environment.NewLine,
|
Environment.NewLine,
|
||||||
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
||||||
viewsDiffereningInCase);
|
viewsDifferingInCase);
|
||||||
throw new InvalidOperationException(message);
|
throw new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
// ViewFeature items have precedence semantics - when two views have the same path \ identifier,
|
// ViewFeature items have precedence semantics - when two views have the same path \ identifier,
|
||||||
// the one that appears earlier in the list wins. Therefore the ordering of
|
// the one that appears earlier in the list wins. Therefore the ordering of
|
||||||
// RazorCompiledItemFeatureProvider and ViewsFeatureProvider is pertinent - any view compiled
|
// RazorCompiledItemFeatureProvider and ViewsFeatureProvider is pertinent - any view compiled
|
||||||
// using the Sdk will be prefered to views compiled using MvcPrecompilation.
|
// using the Sdk will be preferred to views compiled using MvcPrecompilation.
|
||||||
if (!builder.PartManager.FeatureProviders.OfType<RazorCompiledItemFeatureProvider>().Any())
|
if (!builder.PartManager.FeatureProviders.OfType<RazorCompiledItemFeatureProvider>().Any())
|
||||||
{
|
{
|
||||||
builder.PartManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider());
|
builder.PartManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider());
|
||||||
|
|
@ -159,6 +159,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
|
ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
|
||||||
|
|
||||||
|
services.TryAddEnumerable(
|
||||||
|
ServiceDescriptor.Transient<IPostConfigureOptions<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>());
|
||||||
|
|
||||||
services.TryAddSingleton<
|
services.TryAddSingleton<
|
||||||
IRazorViewEngineFileProviderAccessor,
|
IRazorViewEngineFileProviderAccessor,
|
||||||
DefaultRazorViewEngineFileProviderAccessor>();
|
DefaultRazorViewEngineFileProviderAccessor>();
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
// from either the set of known precompiled views, or by being compiled.
|
// from either the set of known precompiled views, or by being compiled.
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
|
|
||||||
// We need to validate that the all of the precompiled views are unique by path (case-insenstive).
|
// We need to validate that the all of the precompiled views are unique by path (case-insensitive).
|
||||||
// We do this because there's no good way to canonicalize paths on windows, and it will create
|
// We do this because there's no good way to canonicalize paths on windows, and it will create
|
||||||
// problems when deploying to linux. Rather than deal with these issues, we just don't support
|
// problems when deploying to linux. Rather than deal with these issues, we just don't support
|
||||||
// views that differ only by case.
|
// views that differ only by case.
|
||||||
|
|
@ -116,6 +116,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AllowRecompilingViewsOnFileChange { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<CompiledViewDescriptor> CompileAsync(string relativePath)
|
public Task<CompiledViewDescriptor> CompileAsync(string relativePath)
|
||||||
{
|
{
|
||||||
|
|
@ -254,16 +256,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
// Used to validate and recompile
|
// Used to validate and recompile
|
||||||
NormalizedPath = normalizedPath,
|
NormalizedPath = normalizedPath,
|
||||||
ExpirationTokens = new List<IChangeToken>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var checksums = precompiledView.Item.GetChecksumMetadata();
|
ExpirationTokens = GetExpirationTokens(precompiledView),
|
||||||
for (var i = 0; i < checksums.Count; i++)
|
};
|
||||||
{
|
|
||||||
// We rely on Razor to provide the right set of checksums. Trust the compiler, it has to do a good job,
|
|
||||||
// so it probably will.
|
|
||||||
item.ExpirationTokens.Add(_fileProvider.Watch(checksums[i].Identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We also need to create a new descriptor, because the original one doesn't have expiration tokens on
|
// We also need to create a new descriptor, because the original one doesn't have expiration tokens on
|
||||||
// it. These will be used by the view location cache, which is like an L1 cache for views (this class is
|
// it. These will be used by the view location cache, which is like an L1 cache for views (this class is
|
||||||
|
|
@ -282,10 +277,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
private ViewCompilerWorkItem CreateRuntimeCompilationWorkItem(string normalizedPath)
|
private ViewCompilerWorkItem CreateRuntimeCompilationWorkItem(string normalizedPath)
|
||||||
{
|
{
|
||||||
var expirationTokens = new List<IChangeToken>()
|
IList<IChangeToken> expirationTokens = Array.Empty<IChangeToken>();
|
||||||
|
|
||||||
|
if (AllowRecompilingViewsOnFileChange)
|
||||||
{
|
{
|
||||||
_fileProvider.Watch(normalizedPath),
|
var changeToken = _fileProvider.Watch(normalizedPath);
|
||||||
};
|
expirationTokens = new List<IChangeToken> { changeToken };
|
||||||
|
}
|
||||||
|
|
||||||
var projectItem = _projectEngine.FileSystem.GetItem(normalizedPath);
|
var projectItem = _projectEngine.FileSystem.GetItem(normalizedPath);
|
||||||
if (!projectItem.Exists)
|
if (!projectItem.Exists)
|
||||||
|
|
@ -293,7 +291,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
_logger.ViewCompilerCouldNotFindFileAtPath(normalizedPath);
|
_logger.ViewCompilerCouldNotFindFileAtPath(normalizedPath);
|
||||||
|
|
||||||
// If the file doesn't exist, we can't do compilation right now - we still want to cache
|
// If the file doesn't exist, we can't do compilation right now - we still want to cache
|
||||||
// the fact that we tried. This will allow us to retrigger compilation if the view file
|
// the fact that we tried. This will allow us to re-trigger compilation if the view file
|
||||||
// is added.
|
// is added.
|
||||||
return new ViewCompilerWorkItem()
|
return new ViewCompilerWorkItem()
|
||||||
{
|
{
|
||||||
|
|
@ -313,9 +311,46 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
_logger.ViewCompilerFoundFileToCompile(normalizedPath);
|
_logger.ViewCompilerFoundFileToCompile(normalizedPath);
|
||||||
|
|
||||||
|
GetChangeTokensFromImports(expirationTokens, projectItem);
|
||||||
|
|
||||||
|
return new ViewCompilerWorkItem()
|
||||||
|
{
|
||||||
|
SupportsCompilation = true,
|
||||||
|
|
||||||
|
NormalizedPath = normalizedPath,
|
||||||
|
ExpirationTokens = expirationTokens,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<IChangeToken> GetExpirationTokens(CompiledViewDescriptor precompiledView)
|
||||||
|
{
|
||||||
|
if (!AllowRecompilingViewsOnFileChange)
|
||||||
|
{
|
||||||
|
return Array.Empty<IChangeToken>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var checksums = precompiledView.Item.GetChecksumMetadata();
|
||||||
|
var expirationTokens = new List<IChangeToken>(checksums.Count);
|
||||||
|
|
||||||
|
for (var i = 0; i < checksums.Count; i++)
|
||||||
|
{
|
||||||
|
// We rely on Razor to provide the right set of checksums. Trust the compiler, it has to do a good job,
|
||||||
|
// so it probably will.
|
||||||
|
expirationTokens.Add(_fileProvider.Watch(checksums[i].Identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
return expirationTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetChangeTokensFromImports(IList<IChangeToken> expirationTokens, RazorProjectItem projectItem)
|
||||||
|
{
|
||||||
|
if (!AllowRecompilingViewsOnFileChange)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// OK this means we can do compilation. For now let's just identify the other files we need to watch
|
// OK this means we can do compilation. For now let's just identify the other files we need to watch
|
||||||
// so we can create the cache entry. Compilation will happen after we release the lock.
|
// so we can create the cache entry. Compilation will happen after we release the lock.
|
||||||
|
|
||||||
var importFeature = _projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
var importFeature = _projectEngine.ProjectFeatures.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||||
|
|
||||||
// There should always be an import feature unless someone has misconfigured their RazorProjectEngine.
|
// There should always be an import feature unless someone has misconfigured their RazorProjectEngine.
|
||||||
|
|
@ -330,14 +365,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
{
|
{
|
||||||
expirationTokens.Add(_fileProvider.Watch(physicalImport.FilePath));
|
expirationTokens.Add(_fileProvider.Watch(physicalImport.FilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ViewCompilerWorkItem()
|
|
||||||
{
|
|
||||||
SupportsCompilation = true,
|
|
||||||
|
|
||||||
NormalizedPath = normalizedPath,
|
|
||||||
ExpirationTokens = expirationTokens,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual CompiledViewDescriptor CompileAndEmit(string relativePath)
|
protected virtual CompiledViewDescriptor CompileAndEmit(string relativePath)
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
feature.ViewDescriptors,
|
feature.ViewDescriptors,
|
||||||
_compilationMemoryCacheProvider.CompilationMemoryCache,
|
_compilationMemoryCacheProvider.CompilationMemoryCache,
|
||||||
_logger);
|
_logger)
|
||||||
|
{
|
||||||
|
AllowRecompilingViewsOnFileChange = _viewEngineOptions.AllowRecompilingViewsOnFileChange,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@ using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.RazorPages.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
@ -13,10 +15,21 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides programmatic configuration for the <see cref="RazorViewEngine"/>.
|
/// Provides programmatic configuration for the <see cref="RazorViewEngine"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RazorViewEngineOptions
|
public class RazorViewEngineOptions : IEnumerable<ICompatibilitySwitch>
|
||||||
{
|
{
|
||||||
|
private readonly ICompatibilitySwitch[] _switches;
|
||||||
|
private readonly CompatibilitySwitch<bool> _allowRecompilingViewsOnFileChange;
|
||||||
private Action<RoslynCompilationContext> _compilationCallback = c => { };
|
private Action<RoslynCompilationContext> _compilationCallback = c => { };
|
||||||
|
|
||||||
|
public RazorViewEngineOptions()
|
||||||
|
{
|
||||||
|
_allowRecompilingViewsOnFileChange = new CompatibilitySwitch<bool>(nameof(AllowRecompilingViewsOnFileChange));
|
||||||
|
_switches = new[]
|
||||||
|
{
|
||||||
|
_allowRecompilingViewsOnFileChange,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a <see cref="IList{IViewLocationExpander}"/> used by the <see cref="RazorViewEngine"/>.
|
/// Gets a <see cref="IList{IViewLocationExpander}"/> used by the <see cref="RazorViewEngine"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -181,5 +194,52 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
_compilationCallback = value;
|
_compilationCallback = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that determines if Razor files (Razor Views and Razor Pages) are recompiled and updated
|
||||||
|
/// if files change on disk.
|
||||||
|
/// <para>
|
||||||
|
/// When <see langword="true"/>, MVC will use <see cref="IFileProvider.Watch(string)"/> to watch for changes to
|
||||||
|
/// Razor files in configured <see cref="IFileProvider"/> instances.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The default value is <see langword="true"/> if the version is <see cref = "CompatibilityVersion.Version_2_1" />
|
||||||
|
/// or earlier. If the version is later and <see cref= "IHostingEnvironment.EnvironmentName" /> is <c>Development</c>,
|
||||||
|
/// the default value is <see langword="true"/>. Otherwise, the default value is <see langword="false" />.
|
||||||
|
/// </value>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
||||||
|
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
|
||||||
|
/// guidance and examples of setting the application's compatibility version.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Configuring the desired value of the compatibility switch by calling this property's setter will take
|
||||||
|
/// precedence over the value implied by the application's <see cref="CompatibilityVersion"/>.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_1"/> or
|
||||||
|
/// lower then this setting will have the value <see langword="true"/> unless explicitly configured.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_2"/> or
|
||||||
|
/// higher then this setting will have the value <see langword="false"/> unless
|
||||||
|
/// <see cref="IHostingEnvironment.EnvironmentName"/> is <c>Development</c> or the value is explicitly configured.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public bool AllowRecompilingViewsOnFileChange
|
||||||
|
{
|
||||||
|
// Note: When compatibility switches are removed in 3.0, this property should be retained as a regular boolean property.
|
||||||
|
get => _allowRecompilingViewsOnFileChange.Value;
|
||||||
|
set => _allowRecompilingViewsOnFileChange.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,45 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
internal class RazorViewEngineOptionsSetup :
|
||||||
/// Sets up default options for <see cref="RazorViewEngineOptions"/>.
|
ConfigureCompatibilityOptions<RazorViewEngineOptions>,
|
||||||
/// </summary>
|
IConfigureOptions<RazorViewEngineOptions>
|
||||||
public class RazorViewEngineOptionsSetup : IConfigureOptions<RazorViewEngineOptions>
|
|
||||||
{
|
{
|
||||||
private readonly IHostingEnvironment _hostingEnvironment;
|
private readonly IHostingEnvironment _hostingEnvironment;
|
||||||
|
|
||||||
/// <summary>
|
public RazorViewEngineOptionsSetup(
|
||||||
/// Initializes a new instance of <see cref="RazorViewEngineOptions"/>.
|
IHostingEnvironment hostingEnvironment,
|
||||||
/// </summary>
|
ILoggerFactory loggerFactory,
|
||||||
/// <param name="hostingEnvironment"><see cref="IHostingEnvironment"/> for the application.</param>
|
IOptions<MvcCompatibilityOptions> compatibilityOptions)
|
||||||
public RazorViewEngineOptionsSetup(IHostingEnvironment hostingEnvironment)
|
: base(loggerFactory, compatibilityOptions)
|
||||||
{
|
{
|
||||||
_hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
|
_hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override IReadOnlyDictionary<string, object> DefaultValues
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var values = new Dictionary<string, object>();
|
||||||
|
if (Version < CompatibilityVersion.Version_2_2)
|
||||||
|
{
|
||||||
|
// Default to true in 2.1 or earlier. In 2.2, we have to conditionally enable this
|
||||||
|
// and consequently this switch has no default value.
|
||||||
|
values[nameof(RazorViewEngineOptions.AllowRecompilingViewsOnFileChange)] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Configure(RazorViewEngineOptions options)
|
public void Configure(RazorViewEngineOptions options)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
|
|
@ -41,6 +59,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
options.AreaViewLocationFormats.Add("/Areas/{2}/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
|
options.AreaViewLocationFormats.Add("/Areas/{2}/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
|
||||||
options.AreaViewLocationFormats.Add("/Areas/{2}/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
|
options.AreaViewLocationFormats.Add("/Areas/{2}/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
|
||||||
options.AreaViewLocationFormats.Add("/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
|
options.AreaViewLocationFormats.Add("/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
|
||||||
|
|
||||||
|
if (_hostingEnvironment.IsDevelopment())
|
||||||
|
{
|
||||||
|
options.AllowRecompilingViewsOnFileChange = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
@ -18,11 +19,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
private readonly IFileProvider _fileProvider;
|
private readonly IFileProvider _fileProvider;
|
||||||
private readonly string[] _searchPatterns;
|
private readonly string[] _searchPatterns;
|
||||||
private readonly string[] _additionalFilesToTrack;
|
private readonly string[] _additionalFilesToTrack;
|
||||||
|
private readonly bool _watchForChanges;
|
||||||
|
|
||||||
public PageActionDescriptorChangeProvider(
|
public PageActionDescriptorChangeProvider(
|
||||||
RazorTemplateEngine templateEngine,
|
RazorTemplateEngine templateEngine,
|
||||||
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
|
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
|
||||||
IOptions<RazorPagesOptions> razorPagesOptions)
|
IOptions<RazorPagesOptions> razorPagesOptions,
|
||||||
|
IOptions<RazorViewEngineOptions> razorViewEngineOptions)
|
||||||
{
|
{
|
||||||
if (templateEngine == null)
|
if (templateEngine == null)
|
||||||
{
|
{
|
||||||
|
|
@ -39,6 +42,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
throw new ArgumentNullException(nameof(razorPagesOptions));
|
throw new ArgumentNullException(nameof(razorPagesOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_watchForChanges = razorViewEngineOptions.Value.AllowRecompilingViewsOnFileChange;
|
||||||
|
if (!_watchForChanges)
|
||||||
|
{
|
||||||
|
// No need to do any additional work if we aren't going to be watching for file changes.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_fileProvider = fileProviderAccessor.FileProvider;
|
_fileProvider = fileProviderAccessor.FileProvider;
|
||||||
|
|
||||||
var rootDirectory = razorPagesOptions.Value.RootDirectory;
|
var rootDirectory = razorPagesOptions.Value.RootDirectory;
|
||||||
|
|
@ -84,6 +94,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
public IChangeToken GetChangeToken()
|
public IChangeToken GetChangeToken()
|
||||||
{
|
{
|
||||||
|
if (!_watchForChanges)
|
||||||
|
{
|
||||||
|
return NullChangeToken.Singleton;
|
||||||
|
}
|
||||||
|
|
||||||
var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + _searchPatterns.Length];
|
var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + _searchPatterns.Length];
|
||||||
for (var i = 0; i < _additionalFilesToTrack.Length; i++)
|
for (var i = 0; i < _additionalFilesToTrack.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
public void OnProvidersExecuting(PageRouteModelProviderContext context)
|
public void OnProvidersExecuting(PageRouteModelProviderContext context)
|
||||||
{
|
{
|
||||||
// When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectoryy = /Areas;
|
// When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectory = /Areas;
|
||||||
// we need to ensure that the page is only route-able via the area route. By adding area routes first,
|
// we need to ensure that the page is only route-able via the area route. By adding area routes first,
|
||||||
// we'll ensure non area routes get skipped when it encounters an IsAlreadyRegistered check.
|
// we'll ensure non area routes get skipped when it encounters an IsAlreadyRegistered check.
|
||||||
|
|
||||||
|
|
@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
private static bool IsRouteable(RazorProjectItem item)
|
private static bool IsRouteable(RazorProjectItem item)
|
||||||
{
|
{
|
||||||
// Pages like _ViewImports should not be routable.
|
// Pages like _ViewImports should not be routeable.
|
||||||
return !item.FileName.StartsWith("_", StringComparison.OrdinalIgnoreCase);
|
return !item.FileName.StartsWith("_", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
|
||||||
public class CacheTagKey : IEquatable<CacheTagKey>
|
public class CacheTagKey : IEquatable<CacheTagKey>
|
||||||
{
|
{
|
||||||
private static readonly char[] AttributeSeparator = new[] { ',' };
|
private static readonly char[] AttributeSeparator = new[] { ',' };
|
||||||
private static readonly Func<IRequestCookieCollection, string, string> CookieAcccessor = (c, key) => c[key];
|
private static readonly Func<IRequestCookieCollection, string, string> CookieAccessor = (c, key) => c[key];
|
||||||
private static readonly Func<IHeaderDictionary, string, string> HeaderAccessor = (c, key) => c[key];
|
private static readonly Func<IHeaderDictionary, string, string> HeaderAccessor = (c, key) => c[key];
|
||||||
private static readonly Func<IQueryCollection, string, string> QueryAccessor = (c, key) => c[key];
|
private static readonly Func<IQueryCollection, string, string> QueryAccessor = (c, key) => c[key];
|
||||||
private static readonly Func<RouteValueDictionary, string, string> RouteValueAccessor = (c, key) => c[key]?.ToString();
|
private static readonly Func<RouteValueDictionary, string, string> RouteValueAccessor = (c, key) => c[key]?.ToString();
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
|
||||||
_expiresOn = tagHelper.ExpiresOn;
|
_expiresOn = tagHelper.ExpiresOn;
|
||||||
_expiresSliding = tagHelper.ExpiresSliding;
|
_expiresSliding = tagHelper.ExpiresSliding;
|
||||||
_varyBy = tagHelper.VaryBy;
|
_varyBy = tagHelper.VaryBy;
|
||||||
_cookies = ExtractCollection(tagHelper.VaryByCookie, request.Cookies, CookieAcccessor);
|
_cookies = ExtractCollection(tagHelper.VaryByCookie, request.Cookies, CookieAccessor);
|
||||||
_headers = ExtractCollection(tagHelper.VaryByHeader, request.Headers, HeaderAccessor);
|
_headers = ExtractCollection(tagHelper.VaryByHeader, request.Headers, HeaderAccessor);
|
||||||
_queries = ExtractCollection(tagHelper.VaryByQuery, request.Query, QueryAccessor);
|
_queries = ExtractCollection(tagHelper.VaryByQuery, request.Query, QueryAccessor);
|
||||||
_routeValues = ExtractCollection(tagHelper.VaryByRoute, tagHelper.ViewContext.RouteData.Values, RouteValueAccessor);
|
_routeValues = ExtractCollection(tagHelper.VaryByRoute, tagHelper.ViewContext.RouteData.Values, RouteValueAccessor);
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
// can't be put inside a using block.
|
// can't be put inside a using block.
|
||||||
entry.Dispose();
|
entry.Dispose();
|
||||||
|
|
||||||
// Set the result on the TCS once we've commited the entry to the cache since commiting to the cache
|
// Set the result on the TCS once we've committed the entry to the cache since commiting to the cache
|
||||||
// may throw.
|
// may throw.
|
||||||
tcs.SetResult(content);
|
tcs.SetResult(content);
|
||||||
return content;
|
return content;
|
||||||
|
|
|
||||||
|
|
@ -167,9 +167,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
/// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks>
|
/// <remarks>Does nothing if user provides an <c>FormAction</c> attribute.</remarks>
|
||||||
/// <exception cref="InvalidOperationException">
|
/// <exception cref="InvalidOperationException">
|
||||||
/// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
|
/// Thrown if <c>FormAction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
|
||||||
/// <see cref="Fragment"/> or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
|
/// <see cref="Fragment"/> or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
|
||||||
/// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
|
/// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
|
||||||
/// are non-<c>null</c>
|
/// are non-<c>null</c>
|
||||||
|
|
@ -186,7 +186,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
throw new ArgumentNullException(nameof(output));
|
throw new ArgumentNullException(nameof(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If "formaction" is already set, it means the user is attempting to use a normal button or input element.
|
// If "FormAction" is already set, it means the user is attempting to use a normal button or input element.
|
||||||
if (output.Attributes.ContainsName(FormAction))
|
if (output.Attributes.ContainsName(FormAction))
|
||||||
{
|
{
|
||||||
if (Action != null ||
|
if (Action != null ||
|
||||||
|
|
@ -198,7 +198,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
Route != null ||
|
Route != null ||
|
||||||
(_routeValues != null && _routeValues.Count > 0))
|
(_routeValues != null && _routeValues.Count > 0))
|
||||||
{
|
{
|
||||||
// User specified a formaction and one of the bound attributes; can't override that formaction
|
// User specified a FormAction and one of the bound attributes; can't override that FormAction
|
||||||
// attribute.
|
// attribute.
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
Resources.FormatFormActionTagHelper_CannotOverrideFormAction(
|
Resources.FormatFormActionTagHelper_CannotOverrideFormAction(
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
public class RenderAtEndOfFormTagHelper : TagHelper
|
public class RenderAtEndOfFormTagHelper : TagHelper
|
||||||
{
|
{
|
||||||
// This TagHelper's order must be greater than the FormTagHelper's. I.e it must be executed after
|
// This TagHelper's order must be greater than the FormTagHelper's. I.e it must be executed after
|
||||||
// FormTaghelper does.
|
// FormTagHelper does.
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int Order => -900;
|
public override int Order => -900;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
var copiedAttribute = false;
|
var copiedAttribute = false;
|
||||||
|
|
||||||
// We iterate context.AllAttributes backwards since we prioritize TagHelperOutput values occurring
|
// We iterate context.AllAttributes backwards since we prioritize TagHelperOutput values occurring
|
||||||
// before the current context.AllAttribtes[i].
|
// before the current context.AllAttributes[i].
|
||||||
for (var i = context.AllAttributes.Count - 1; i >= 0; i--)
|
for (var i = context.AllAttributes.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
// We look for the original attribute so we can restore the exact attribute name the user typed in
|
// We look for the original attribute so we can restore the exact attribute name the user typed in
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing.Handlers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of <see cref="RedirectHandler"/>.
|
/// Creates a new instance of <see cref="RedirectHandler"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="maxRedirects">The maximun number of redirect responses to follow. It must be
|
/// <param name="maxRedirects">The maximum number of redirect responses to follow. It must be
|
||||||
/// equal or greater than 0.</param>
|
/// equal or greater than 0.</param>
|
||||||
public RedirectHandler(int maxRedirects)
|
public RedirectHandler(int maxRedirects)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
|
||||||
/// <see cref="WebApplicationFactoryContentRootAttribute"/> on the assembly containing the functional tests with
|
/// <see cref="WebApplicationFactoryContentRootAttribute"/> on the assembly containing the functional tests with
|
||||||
/// a key equal to the <typeparamref name="TEntryPoint"/> assembly <see cref="Assembly.FullName"/>.
|
/// a key equal to the <typeparamref name="TEntryPoint"/> assembly <see cref="Assembly.FullName"/>.
|
||||||
/// In case an attribute with the right key can't be found, <see cref="WebApplicationFactory{TEntryPoint}"/>
|
/// In case an attribute with the right key can't be found, <see cref="WebApplicationFactory{TEntryPoint}"/>
|
||||||
/// will fall back to searching for a solution file (*.sln) and then appending <typeparamref name="TEntryPoint"/> asembly name
|
/// will fall back to searching for a solution file (*.sln) and then appending <typeparamref name="TEntryPoint"/> assembly name
|
||||||
/// to the solution directory. The application root directory will be used to discover views and content files.
|
/// to the solution directory. The application root directory will be used to discover views and content files.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// Gets or sets <see cref="ViewDataDictionary"/> used by <see cref="ViewResult"/> and <see cref="ViewBag"/>.
|
/// Gets or sets <see cref="ViewDataDictionary"/> used by <see cref="ViewResult"/> and <see cref="ViewBag"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// By default, this property is intiailized when <see cref="Controllers.IControllerActivator"/> activates
|
/// By default, this property is initialized when <see cref="Controllers.IControllerActivator"/> activates
|
||||||
/// controllers.
|
/// controllers.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// This property can be accessed after the controller has been activated, for example, in a controller action
|
/// This property can be accessed after the controller has been activated, for example, in a controller action
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -28,14 +29,30 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
// Don't do anything. We'll call FlushAsync.
|
// Don't do anything. We'll call FlushAsync.
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task FlushAsync()
|
public override Task FlushAsync() => FlushAsyncCore();
|
||||||
|
|
||||||
|
// private non-virtual for internal calling.
|
||||||
|
// It first does a fast check to see if async is necessary, we inline this check.
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private Task FlushAsyncCore()
|
||||||
{
|
{
|
||||||
var length = _charBuffer.Length;
|
var length = _charBuffer.Length;
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
|
// If nothing sync buffered return CompletedTask,
|
||||||
|
// so we can fast-path skip async state-machine creation
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return FlushAsyncAwaited();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FlushAsyncAwaited()
|
||||||
|
{
|
||||||
|
var length = _charBuffer.Length;
|
||||||
|
Debug.Assert(length > 0);
|
||||||
|
|
||||||
var pages = _charBuffer.Pages;
|
var pages = _charBuffer.Pages;
|
||||||
var count = pages.Count;
|
var count = pages.Count;
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
|
|
@ -89,21 +106,54 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
_charBuffer.Append(value);
|
_charBuffer.Append(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task WriteAsync(char value)
|
public override Task WriteAsync(char value)
|
||||||
{
|
{
|
||||||
await FlushAsync();
|
var flushTask = FlushAsyncCore();
|
||||||
|
|
||||||
|
// FlushAsyncCore will return CompletedTask if nothing sync buffered
|
||||||
|
// Fast-path and skip async state-machine if only a single async operation
|
||||||
|
return ReferenceEquals(flushTask, Task.CompletedTask) ?
|
||||||
|
_inner.WriteAsync(value) :
|
||||||
|
WriteAsyncAwaited(flushTask, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task WriteAsyncAwaited(Task flushTask, char value)
|
||||||
|
{
|
||||||
|
await flushTask;
|
||||||
await _inner.WriteAsync(value);
|
await _inner.WriteAsync(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task WriteAsync(char[] buffer, int index, int count)
|
public override Task WriteAsync(char[] buffer, int index, int count)
|
||||||
{
|
{
|
||||||
await FlushAsync();
|
var flushTask = FlushAsyncCore();
|
||||||
|
|
||||||
|
// FlushAsyncCore will return CompletedTask if nothing sync buffered
|
||||||
|
// Fast-path and skip async state-machine if only a single async operation
|
||||||
|
return ReferenceEquals(flushTask, Task.CompletedTask) ?
|
||||||
|
_inner.WriteAsync(buffer, index, count) :
|
||||||
|
WriteAsyncAwaited(flushTask, buffer, index, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task WriteAsyncAwaited(Task flushTask, char[] buffer, int index, int count)
|
||||||
|
{
|
||||||
|
await flushTask;
|
||||||
await _inner.WriteAsync(buffer, index, count);
|
await _inner.WriteAsync(buffer, index, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task WriteAsync(string value)
|
public override Task WriteAsync(string value)
|
||||||
{
|
{
|
||||||
await FlushAsync();
|
var flushTask = FlushAsyncCore();
|
||||||
|
|
||||||
|
// FlushAsyncCore will return CompletedTask if nothing sync buffered
|
||||||
|
// Fast-path and skip async state-machine if only a single async operation
|
||||||
|
return ReferenceEquals(flushTask, Task.CompletedTask) ?
|
||||||
|
_inner.WriteAsync(value) :
|
||||||
|
WriteAsyncAwaited(flushTask, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task WriteAsyncAwaited(Task flushTask, string value)
|
||||||
|
{
|
||||||
|
await flushTask;
|
||||||
await _inner.WriteAsync(value);
|
await _inner.WriteAsync(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public IHtmlContentBuilder Append(string unencoded)
|
public IHtmlContentBuilder Append(string unencoded)
|
||||||
{
|
{
|
||||||
|
|
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public IHtmlContentBuilder AppendHtml(IHtmlContent content)
|
public IHtmlContentBuilder AppendHtml(IHtmlContent content)
|
||||||
{
|
{
|
||||||
|
|
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public IHtmlContentBuilder AppendHtml(string encoded)
|
public IHtmlContentBuilder AppendHtml(string encoded)
|
||||||
{
|
{
|
||||||
|
|
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void AppendValue(ViewBufferValue value)
|
private void AppendValue(ViewBufferValue value)
|
||||||
{
|
{
|
||||||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
page.Append(value);
|
page.Append(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private ViewBufferPage GetCurrentPage()
|
private ViewBufferPage GetCurrentPage()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||||
|
|
||||||
public bool IsFull => Count == Capacity;
|
public bool IsFull => Count == Capacity;
|
||||||
|
|
||||||
// Very common trival method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
// Very common trivial method; nudge it to inline https://github.com/aspnet/Mvc/pull/8339
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Append(ViewBufferValue value) => Buffer[Count++] = value;
|
public void Append(ViewBufferValue value) => Buffer[Count++] = value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
Order = int.MinValue + 100;
|
Order = int.MinValue + 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -1565,10 +1565,10 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetApiDescription_ParameterDescription_RedundentMetadata_NotMergedWithParent()
|
public void GetApiDescription_ParameterDescription_RedundantMetadata_NotMergedWithParent()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var action = CreateActionDescriptor(nameof(AcceptsRedundentMetadata));
|
var action = CreateActionDescriptor(nameof(AcceptsRedundantMetadata));
|
||||||
var parameterDescriptor = action.Parameters.Single();
|
var parameterDescriptor = action.Parameters.Single();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -1590,7 +1590,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetApiDescription_ParameterDescription_RedundentMetadata_WithParameterMetadata()
|
public void GetApiDescription_ParameterDescription_RedundantMetadata_WithParameterMetadata()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var action = CreateActionDescriptor(nameof(AcceptsPerson));
|
var action = CreateActionDescriptor(nameof(AcceptsPerson));
|
||||||
|
|
@ -2081,7 +2081,7 @@ namespace Microsoft.AspNetCore.Mvc.Description
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AcceptsRedundentMetadata([FromQuery] RedundentMetadata r)
|
private void AcceptsRedundantMetadata([FromQuery] RedundentMetadata r)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnFormatting_NullUrlHelperContextNoRequestServices_ThrowsArgumentNullExeption()
|
public void OnFormatting_NullUrlHelperContextNoRequestServices_ThrowsArgumentNullException()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
|
var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public void Convert_InfersDeclaredTypeFromActionResultTypeParameter()
|
public void Convert_InfersDeclaredTypeFromActionResultTypeParameter()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var value = new DeriviedItem();
|
var value = new DerivedItem();
|
||||||
var actionResultOfT = new ActionResult<BaseItem>(value);
|
var actionResultOfT = new ActionResult<BaseItem>(value);
|
||||||
var convertToActionResult = (IConvertToActionResult)actionResultOfT;
|
var convertToActionResult = (IConvertToActionResult)actionResultOfT;
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeriviedItem : BaseItem
|
private class DerivedItem : BaseItem
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,14 +85,14 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Constructor_ThrowsIfMethodIsAmbigous()
|
public void Constructor_ThrowsIfMethodIsAmbiguous()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var methodName = typeof(ConventionWithProducesAttribute).FullName + '.' + nameof(ConventionWithProducesAttribute.Get);
|
var methodName = typeof(ConventionWithProducesAttribute).FullName + '.' + nameof(ConventionWithProducesAttribute.Get);
|
||||||
var attribute = typeof(ProducesAttribute);
|
var attribute = typeof(ProducesAttribute);
|
||||||
var type = typeof(TestConventions);
|
var type = typeof(TestConventions);
|
||||||
|
|
||||||
var expected = $"Method name 'Method' is ambigous for convention type '{type}'. More than one method found with the name 'Method'.";
|
var expected = $"Method name 'Method' is ambiguous for convention type '{type}'. More than one method found with the name 'Method'.";
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
ExceptionAssert.ThrowsArgument(
|
ExceptionAssert.ThrowsArgument(
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsNameMatch_WithPrefix_ReturnsFalse_IfNameIsNotProperPrfix()
|
public void IsNameMatch_WithPrefix_ReturnsFalse_IfNameIsNotProperPrefix()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var name = "Postman";
|
var name = "Postman";
|
||||||
|
|
@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsMatch_ReturnsTrue_IfMethodNameAndParametersMatchs()
|
public void IsMatch_ReturnsTrue_IfMethodNameAndParametersMatches()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var method = typeof(TestController).GetMethod(nameof(TestController.Get));
|
var method = typeof(TestController).GetMethod(nameof(TestController.Get));
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
[InlineData("application/json")]
|
[InlineData("application/json")]
|
||||||
[InlineData("application/json;Parameter1=12")]
|
[InlineData("application/json;Parameter1=12")]
|
||||||
[InlineData("text/xml")]
|
[InlineData("text/xml")]
|
||||||
public void ActionConstraint_Accept_MatchesForMachingRequestContentType(string contentType)
|
public void ActionConstraint_Accept_MatchesForMatchingRequestContentType(string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var constraint = new ConsumesAttribute("application/json", "text/xml");
|
var constraint = new ConsumesAttribute("application/json", "text/xml");
|
||||||
|
|
|
||||||
|
|
@ -1027,10 +1027,10 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
||||||
// Arrange
|
// Arrange
|
||||||
var controller = new TestableController();
|
var controller = new TestableController();
|
||||||
var pageName = "/Page-Name";
|
var pageName = "/Page-Name";
|
||||||
var routeVaues = new { key = "value" };
|
var routeValues = new { key = "value" };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = controller.RedirectToPage(pageName, routeVaues);
|
var result = controller.RedirectToPage(pageName, routeValues);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.IsType<RedirectToPageResult>(result);
|
Assert.IsType<RedirectToPageResult>(result);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
Pipeline1.ConfigurePipeline = (ab) =>
|
Pipeline1.ConfigurePipeline = (ab) =>
|
||||||
{
|
{
|
||||||
configureCallCount++;
|
configureCallCount++;
|
||||||
ab.Use((httpCtxt, next) =>
|
ab.Use((httpContext, next) =>
|
||||||
{
|
{
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
|
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
|
||||||
httpContext.Setup(c => c.Request.Query["format"]).Returns("xml");
|
httpContext.Setup(c => c.Request.Query["format"]).Returns("xml");
|
||||||
|
|
||||||
// Routedata contains json
|
// RouteData contains json
|
||||||
var data = new RouteData();
|
var data = new RouteData();
|
||||||
data.Values.Add("format", "json");
|
data.Values.Add("format", "json");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// object value, bool useDeclaredTypeAsString, bool expectedCanwriteResult, bool useNonNullContentType
|
// object value, bool useDeclaredTypeAsString, bool expectedCanWriteResult, bool useNonNullContentType
|
||||||
yield return new object[] { "valid value", true, false, true };
|
yield return new object[] { "valid value", true, false, true };
|
||||||
yield return new object[] { "valid value", false, false, true };
|
yield return new object[] { "valid value", false, false, true };
|
||||||
yield return new object[] { "", false, false, true };
|
yield return new object[] { "", false, false, true };
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Constructor_WithNameAndInitalValue_IsValueSetIsFalse()
|
public void Constructor_WithNameAndInitialValue_IsValueSetIsFalse()
|
||||||
{
|
{
|
||||||
// Arrange & Act
|
// Arrange & Act
|
||||||
var @switch = new CompatibilitySwitch<bool>("TestProperty", initialValue: true);
|
var @switch = new CompatibilitySwitch<bool>("TestProperty", initialValue: true);
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
var @switch = new CompatibilitySwitch<bool>("TestProperty");
|
var @switch = new CompatibilitySwitch<bool>("TestProperty");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
@switch.Value = false; // You don't need to actually change the value, just caling the setting works
|
@switch.Value = false; // You don't need to actually change the value, just calling the setting works
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(@switch.Value);
|
Assert.False(@switch.Value);
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionDescriptors_UpdatesAndResubscripes_WhenChangeTokenTriggers()
|
public void ActionDescriptors_UpdatesAndResubscribes_WhenChangeTokenTriggers()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorProvider>();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
{
|
{
|
||||||
public class ProblemDetalsClientErrorFactoryTest
|
public class ProblemDetailsClientErrorFactoryTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientError_ReturnsProblemDetails_IfNoMappingWasFound()
|
public void GetClientError_ReturnsProblemDetails_IfNoMappingWasFound()
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InferBindingSourceForParameter_ReturnsPath_IfParameterAppearsInAnyRoutes_MulitpleRoutes()
|
public void InferBindingSourceForParameter_ReturnsPath_IfParameterAppearsInAnyRoutes_MultipleRoutes()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionName = nameof(ParameterBindingController.ParameterInMultipleRoutes);
|
var actionName = nameof(ParameterBindingController.ParameterInMultipleRoutes);
|
||||||
|
|
@ -1112,7 +1112,7 @@ Environment.NewLine + "int b";
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DiscoverErrorResponseType_UsesValueFromApiErrorTypeAttribute_SpecifiedOnControllerAsssembly()
|
public void DiscoverErrorResponseType_UsesValueFromApiErrorTypeAttribute_SpecifiedOnControllerAssembly()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected = typeof(InvalidEnumArgumentException);
|
var expected = typeof(InvalidEnumArgumentException);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||||
|
|
@ -252,6 +253,33 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
Assert.Equal(nameof(ConventionallyRoutedController.ConventionalAction), actionConstraint.Value);
|
Assert.Equal(nameof(ConventionallyRoutedController.ConventionalAction), actionConstraint.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_EndpointMetadata_ContainsAttributesFromActionAndController()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var descriptors = GetDescriptors(
|
||||||
|
typeof(AuthorizeController).GetTypeInfo());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, descriptors.Count());
|
||||||
|
|
||||||
|
var anonymousAction = Assert.Single(descriptors, a => a.RouteValues["action"] == "AllowAnonymousAction");
|
||||||
|
|
||||||
|
Assert.NotNull(anonymousAction.EndpointMetadata);
|
||||||
|
|
||||||
|
Assert.Collection(anonymousAction.EndpointMetadata,
|
||||||
|
metadata => Assert.IsType<AllowAnonymousAttribute>(metadata),
|
||||||
|
metadata => Assert.IsType<AuthorizeAttribute>(metadata));
|
||||||
|
|
||||||
|
var authorizeAction = Assert.Single(descriptors, a => a.RouteValues["action"] == "AuthorizeAction");
|
||||||
|
|
||||||
|
Assert.NotNull(authorizeAction.EndpointMetadata);
|
||||||
|
|
||||||
|
Assert.Collection(authorizeAction.EndpointMetadata,
|
||||||
|
metadata => Assert.Equal("ActionPolicy", Assert.IsType<AuthorizeAttribute>(metadata).Policy),
|
||||||
|
metadata => Assert.Equal("ControllerPolicy", Assert.IsType<AuthorizeAttribute>(metadata).Policy));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_ActionWithHttpMethods_AddedToEndpointMetadata()
|
public void GetDescriptors_ActionWithHttpMethods_AddedToEndpointMetadata()
|
||||||
{
|
{
|
||||||
|
|
@ -272,7 +300,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
Assert.False(httpMethodMetadata.AcceptCorsPreflight);
|
Assert.False(httpMethodMetadata.AcceptCorsPreflight);
|
||||||
Assert.Equal("GET", Assert.Single(httpMethodMetadata.HttpMethods));
|
Assert.Equal("GET", Assert.Single(httpMethodMetadata.HttpMethods));
|
||||||
});
|
},
|
||||||
|
metadata => Assert.IsType<RouteAttribute>(metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1175,7 +1204,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("A", typeof(ApiExplorerEnabledConventionalRoutedController))]
|
[InlineData("A", typeof(ApiExplorerEnabledConventionalRoutedController))]
|
||||||
[InlineData("A", typeof(ApiExplorerEnabledActionConventionalRoutedController))]
|
[InlineData("A", typeof(ApiExplorerEnabledActionConventionalRoutedController))]
|
||||||
public void ApiExplorer_ThrowsForContentionalRouting(string actionName, Type type)
|
public void ApiExplorer_ThrowsForConventionalRouting(string actionName, Type type)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var assemblyName = type.GetTypeInfo().Assembly.GetName().Name;
|
var assemblyName = type.GetTypeInfo().Assembly.GetName().Name;
|
||||||
|
|
@ -1865,6 +1894,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
public void AttributeRoutedAction() { }
|
public void AttributeRoutedAction() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize("ControllerPolicy")]
|
||||||
|
private class AuthorizeController
|
||||||
|
{
|
||||||
|
[AllowAnonymous]
|
||||||
|
public void AllowAnonymousAction() { }
|
||||||
|
|
||||||
|
[Authorize("ActionPolicy")]
|
||||||
|
public void AuthorizeAction() { }
|
||||||
|
}
|
||||||
|
|
||||||
private class EmptyController
|
private class EmptyController
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1206,7 +1206,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(nameof(TestController.AsynActionMethodWithTestActionResult))]
|
[InlineData(nameof(TestController.AsyncActionMethodWithTestActionResult))]
|
||||||
[InlineData(nameof(TestController.ActionMethodWithTestActionResult))]
|
[InlineData(nameof(TestController.ActionMethodWithTestActionResult))]
|
||||||
public async Task InvokeAction_ReturnTypeAsIActionResult_ReturnsExpected(string methodName)
|
public async Task InvokeAction_ReturnTypeAsIActionResult_ReturnsExpected(string methodName)
|
||||||
{
|
{
|
||||||
|
|
@ -1654,7 +1654,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
return new TestActionResult { Value = value };
|
return new TestActionResult { Value = value };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TestActionResult> AsynActionMethodWithTestActionResult(int value)
|
public async Task<TestActionResult> AsyncActionMethodWithTestActionResult(int value)
|
||||||
{
|
{
|
||||||
return await Task.FromResult<TestActionResult>(new TestActionResult { Value = value });
|
return await Task.FromResult<TestActionResult>(new TestActionResult { Value = value });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EnumerateElements_TwoEnumerableImplemenations()
|
public void EnumerateElements_TwoEnumerableImplementations()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });
|
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });
|
||||||
|
|
|
||||||
|
|
@ -1316,7 +1316,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
{ model, new ValidationStateEntry() }
|
{ model, new ValidationStateEntry() }
|
||||||
};
|
};
|
||||||
var method = GetType().GetMethod(nameof(Validate_Throws_ForTopLeveleMetadataData), BindingFlags.NonPublic | BindingFlags.Instance);
|
var method = GetType().GetMethod(nameof(Validate_Throws_ForTopLevelMetadataData), BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
var metadata = MetadataProvider.GetMetadataForParameter(method.GetParameters()[0]);
|
var metadata = MetadataProvider.GetMetadataForParameter(method.GetParameters()[0]);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
|
|
@ -1465,7 +1465,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
void DoSomething();
|
void DoSomething();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Validate_Throws_ForTopLeveleMetadataData(DepthObject depthObject) { }
|
private void Validate_Throws_ForTopLevelMetadataData(DepthObject depthObject) { }
|
||||||
|
|
||||||
// Custom validation attribute that returns multiple entries in ValidationResult.MemberNames and those member
|
// Custom validation attribute that returns multiple entries in ValidationResult.MemberNames and those member
|
||||||
// names are indexers. An example scenario is an attribute that confirms all entries in a list are unique.
|
// names are indexers. An example scenario is an attribute that confirms all entries in a list are unique.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
|
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
|
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
|
||||||
var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
|
var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal });
|
new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
|
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
|
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
|
||||||
|
|
@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
|
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
httpMaxRequestBodySize.IsReadOnly = true;
|
httpMaxRequestBodySize.IsReadOnly = true;
|
||||||
|
|
@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
|
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
||||||
|
|
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
Assert.Equal($"The request body size limit has been disabled.", write.State.ToString());
|
Assert.Equal($"The request body size limit has been disabled.", write.State.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
|
private static AuthorizationFilterContext CreateAuthorizationFilterContext(IFilterMetadata[] filters)
|
||||||
{
|
{
|
||||||
return new AuthorizationFilterContext(CreateActionContext(), filters);
|
return new AuthorizationFilterContext(CreateActionContext(), filters);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EnumerateElements_TwoEnumerableImplemenations()
|
public void EnumerateElements_TwoEnumerableImplementations()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });
|
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Task requestDelegate(HttpContext context) => Task.FromResult(true);
|
Task requestDelegate(HttpContext context) => Task.FromResult(true);
|
||||||
var middlwareFilter = new MiddlewareFilter(requestDelegate);
|
var middlewareFilter = new MiddlewareFilter(requestDelegate);
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
var resourceExecutingContext = GetResourceExecutingContext(httpContext);
|
var resourceExecutingContext = GetResourceExecutingContext(httpContext);
|
||||||
var resourceExecutionDelegate = GetResourceExecutionDelegate(httpContext);
|
var resourceExecutionDelegate = GetResourceExecutionDelegate(httpContext);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await middlwareFilter.OnResourceExecutionAsync(resourceExecutingContext, resourceExecutionDelegate);
|
await middlewareFilter.OnResourceExecutionAsync(resourceExecutingContext, resourceExecutionDelegate);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var feature = resourceExecutingContext.HttpContext.Features.Get<IMiddlewareFilterFeature>();
|
var feature = resourceExecutingContext.HttpContext.Features.Get<IMiddlewareFilterFeature>();
|
||||||
|
|
@ -398,7 +398,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
logger,
|
logger,
|
||||||
diagnosticSource,
|
diagnosticSource,
|
||||||
mapper,
|
mapper,
|
||||||
CreatControllerContext(actionContext, valueProviderFactories, maxAllowedErrorsInModelState),
|
CreateControllerContext(actionContext, valueProviderFactories, maxAllowedErrorsInModelState),
|
||||||
CreateCacheEntry((ControllerActionDescriptor)actionContext.ActionDescriptor, controllerFactory),
|
CreateCacheEntry((ControllerActionDescriptor)actionContext.ActionDescriptor, controllerFactory),
|
||||||
filters)
|
filters)
|
||||||
{
|
{
|
||||||
|
|
@ -420,7 +420,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
return ObjectMethodExecutor.Create(actionDescriptor.MethodInfo, actionDescriptor.ControllerTypeInfo);
|
return ObjectMethodExecutor.Create(actionDescriptor.MethodInfo, actionDescriptor.ControllerTypeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ControllerContext CreatControllerContext(
|
private static ControllerContext CreateControllerContext(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||||
int maxAllowedErrorsInModelState)
|
int maxAllowedErrorsInModelState)
|
||||||
|
|
|
||||||
|
|
@ -112,9 +112,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var asyncResultFilter = Mock.Of<IAsyncResultFilter>();
|
var asyncResultFilter = Mock.Of<IAsyncResultFilter>();
|
||||||
var resourceFilter = Mock.Of<IResourceFilter>();
|
var resourceFilter = Mock.Of<IResourceFilter>();
|
||||||
var asyncResourceFilter = Mock.Of<IAsyncResourceFilter>();
|
var asyncResourceFilter = Mock.Of<IAsyncResourceFilter>();
|
||||||
var orderedresourceFilterMock = new Mock<IOrderedResourceFilter>();
|
var orderedResourceFilterMock = new Mock<IOrderedResourceFilter>();
|
||||||
orderedresourceFilterMock.SetupGet(f => f.Order).Returns(-100);
|
orderedResourceFilterMock.SetupGet(f => f.Order).Returns(-100);
|
||||||
var orderedResourceFilter = orderedresourceFilterMock.Object;
|
var orderedResourceFilter = orderedResourceFilterMock.Object;
|
||||||
var filters = new IFilterMetadata[]
|
var filters = new IFilterMetadata[]
|
||||||
{
|
{
|
||||||
actionFilter,
|
actionFilter,
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
|
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
|
||||||
var routeValuesAddressNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
var routeValuesAddressNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
||||||
Assert.NotNull(routeValuesAddressNameMetadata);
|
Assert.NotNull(routeValuesAddressNameMetadata);
|
||||||
Assert.Equal(string.Empty, routeValuesAddressNameMetadata.Name);
|
Assert.Equal(string.Empty, routeValuesAddressNameMetadata.RouteName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -402,7 +402,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
|
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
|
||||||
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
||||||
Assert.NotNull(routeValuesAddressMetadata);
|
Assert.NotNull(routeValuesAddressMetadata);
|
||||||
Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
|
Assert.Equal("namedRoute", routeValuesAddressMetadata.RouteName);
|
||||||
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
|
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
|
||||||
},
|
},
|
||||||
(ep) =>
|
(ep) =>
|
||||||
|
|
@ -410,7 +410,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
|
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
|
||||||
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
|
||||||
Assert.NotNull(routeValuesAddressMetadata);
|
Assert.NotNull(routeValuesAddressMetadata);
|
||||||
Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
|
Assert.Equal("namedRoute", routeValuesAddressMetadata.RouteName);
|
||||||
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
|
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var requestFormLimitsFilter = new RequestFormLimitsFilter(NullLoggerFactory.Instance);
|
var requestFormLimitsFilter = new RequestFormLimitsFilter(NullLoggerFactory.Instance);
|
||||||
requestFormLimitsFilter.FormOptions = new FormOptions();
|
requestFormLimitsFilter.FormOptions = new FormOptions();
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { requestFormLimitsFilter });
|
new IFilterMetadata[] { requestFormLimitsFilter });
|
||||||
// Set to null explicitly as we want to make sure the filter adds one
|
// Set to null explicitly as we want to make sure the filter adds one
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(null);
|
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(null);
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var requestFormLimitsFilter = new RequestFormLimitsFilter(NullLoggerFactory.Instance);
|
var requestFormLimitsFilter = new RequestFormLimitsFilter(NullLoggerFactory.Instance);
|
||||||
requestFormLimitsFilter.FormOptions = new FormOptions();
|
requestFormLimitsFilter.FormOptions = new FormOptions();
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { requestFormLimitsFilter });
|
new IFilterMetadata[] { requestFormLimitsFilter });
|
||||||
var oldFormFeature = new FormFeature(authorizationFilterContext.HttpContext.Request);
|
var oldFormFeature = new FormFeature(authorizationFilterContext.HttpContext.Request);
|
||||||
// Set to null explicitly as we want to make sure the filter adds one
|
// Set to null explicitly as we want to make sure the filter adds one
|
||||||
|
|
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
|
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
|
||||||
requestFormLimitsFilter.FormOptions = new FormOptions();
|
requestFormLimitsFilter.FormOptions = new FormOptions();
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { requestFormLimitsFilter });
|
new IFilterMetadata[] { requestFormLimitsFilter });
|
||||||
authorizationFilterContext.HttpContext.Request.Form = new FormCollection(null);
|
authorizationFilterContext.HttpContext.Request.Form = new FormCollection(null);
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
|
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
|
||||||
requestFormLimitsFilter.FormOptions = new FormOptions();
|
requestFormLimitsFilter.FormOptions = new FormOptions();
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { requestFormLimitsFilter });
|
new IFilterMetadata[] { requestFormLimitsFilter });
|
||||||
// Set to null explicitly as we want to make sure the filter adds one
|
// Set to null explicitly as we want to make sure the filter adds one
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(null);
|
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(null);
|
||||||
|
|
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
|
var requestFormLimitsFilter = new RequestFormLimitsFilter(loggerFactory);
|
||||||
requestFormLimitsFilter.FormOptions = new FormOptions();
|
requestFormLimitsFilter.FormOptions = new FormOptions();
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { requestFormLimitsFilter });
|
new IFilterMetadata[] { requestFormLimitsFilter });
|
||||||
// Set to null explicitly as we want to make sure the filter adds one
|
// Set to null explicitly as we want to make sure the filter adds one
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(
|
authorizationFilterContext.HttpContext.Features.Set<IFormFeature>(
|
||||||
|
|
@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
write.State.ToString());
|
write.State.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
|
private static AuthorizationFilterContext CreateAuthorizationFilterContext(IFilterMetadata[] filters)
|
||||||
{
|
{
|
||||||
return new AuthorizationFilterContext(CreateActionContext(), filters);
|
return new AuthorizationFilterContext(CreateActionContext(), filters);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
|
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
|
||||||
requestSizeLimitResourceFilter.Bytes = 12345;
|
requestSizeLimitResourceFilter.Bytes = 12345;
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
requestSizeLimitResourceFilter.Bytes = 12345;
|
requestSizeLimitResourceFilter.Bytes = 12345;
|
||||||
var requestSizeLimitResourceFilterFinal = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
|
var requestSizeLimitResourceFilterFinal = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
|
||||||
requestSizeLimitResourceFilterFinal.Bytes = 0;
|
requestSizeLimitResourceFilterFinal.Bytes = 0;
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(
|
var authorizationFilterContext = CreateAuthorizationFilterContext(
|
||||||
new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal });
|
new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
|
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
|
||||||
requestSizeLimitResourceFilter.Bytes = 12345;
|
requestSizeLimitResourceFilter.Bytes = 12345;
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
|
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
|
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
|
||||||
requestSizeLimitResourceFilter.Bytes = 12345;
|
requestSizeLimitResourceFilter.Bytes = 12345;
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
httpMaxRequestBodySize.IsReadOnly = true;
|
httpMaxRequestBodySize.IsReadOnly = true;
|
||||||
|
|
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
|
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
|
||||||
requestSizeLimitResourceFilter.Bytes = 12345;
|
requestSizeLimitResourceFilter.Bytes = 12345;
|
||||||
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
var authorizationFilterContext = CreateAuthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
|
||||||
|
|
||||||
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
|
||||||
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
|
||||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
Assert.Equal($"The maximum request body size has been set to 12345.", write.State.ToString());
|
Assert.Equal($"The maximum request body size has been set to 12345.", write.State.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
|
private static AuthorizationFilterContext CreateAuthorizationFilterContext(IFilterMetadata[] filters)
|
||||||
{
|
{
|
||||||
return new AuthorizationFilterContext(CreateActionContext(), filters);
|
return new AuthorizationFilterContext(CreateActionContext(), filters);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -467,7 +467,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(DerivedInputFormattersThrowingNonInputFormatterException))]
|
[MemberData(nameof(DerivedInputFormattersThrowingNonInputFormatterException))]
|
||||||
public async Task BindModel_DerivedXmlInputFormatters_ThrowingNonInputFormatingException_AddsErrorToModelState(
|
public async Task BindModel_DerivedXmlInputFormatters_ThrowingNonInputFormattingException_AddsErrorToModelState(
|
||||||
IInputFormatter formatter,
|
IInputFormatter formatter,
|
||||||
string contentType,
|
string contentType,
|
||||||
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
|
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
|
||||||
|
|
|
||||||
|
|
@ -42,14 +42,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
[InlineData(false, typeof(IntEnum))]
|
[InlineData(false, typeof(IntEnum))]
|
||||||
[InlineData(false, typeof(FlagsEnum))]
|
[InlineData(false, typeof(FlagsEnum))]
|
||||||
public async Task BindModel_AddsErrorToModelState_ForEmptyValue_AndNonNullableEnumTypes(
|
public async Task BindModel_AddsErrorToModelState_ForEmptyValue_AndNonNullableEnumTypes(
|
||||||
bool suprressBindingUndefinedValueToEnumType,
|
bool suppressBindingUndefinedValueToEnumType,
|
||||||
Type modelType)
|
Type modelType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var message = "The value '' is invalid.";
|
var message = "The value '' is invalid.";
|
||||||
var binderInfo = GetBinderAndContext(
|
var binderInfo = GetBinderAndContext(
|
||||||
modelType,
|
modelType,
|
||||||
suprressBindingUndefinedValueToEnumType,
|
suppressBindingUndefinedValueToEnumType,
|
||||||
valueProviderValue: "");
|
valueProviderValue: "");
|
||||||
var bindingContext = binderInfo.Item1;
|
var bindingContext = binderInfo.Item1;
|
||||||
var binder = binderInfo.Item2;
|
var binder = binderInfo.Item2;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(typeof(FormCollection))]
|
[InlineData(typeof(FormCollection))]
|
||||||
[InlineData(typeof(DerviedFormCollection))]
|
[InlineData(typeof(DerivedFormCollection))]
|
||||||
public void Create_ThrowsException_ForFormCollectionModelType(Type modelType)
|
public void Create_ThrowsException_ForFormCollectionModelType(Type modelType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -62,9 +62,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DerviedFormCollection : FormCollection
|
private class DerivedFormCollection : FormCollection
|
||||||
{
|
{
|
||||||
public DerviedFormCollection() : base(fields: null, files: null) { }
|
public DerivedFormCollection() : base(fields: null, files: null) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -607,7 +607,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TryUpdataModel_ModelTypeDifferentFromModel_Throws()
|
public async Task TryUpdateModel_ModelTypeDifferentFromModel_Throws()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var metadataProvider = new EmptyModelMetadataProvider();
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
foreach (var property in formOptionsProperties)
|
foreach (var property in formOptionsProperties)
|
||||||
{
|
{
|
||||||
var formLimiAttributeProperty = formLimitsAttributeProperties
|
var formLimitAttributeProperty = formLimitsAttributeProperties
|
||||||
.Where(pi => property.Name == pi.Name && pi.PropertyType == property.PropertyType)
|
.Where(pi => property.Name == pi.Name && pi.PropertyType == property.PropertyType)
|
||||||
.SingleOrDefault();
|
.SingleOrDefault();
|
||||||
Assert.NotNull(formLimiAttributeProperty);
|
Assert.NotNull(formLimitAttributeProperty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
[InlineData(null, false)]
|
[InlineData(null, false)]
|
||||||
[InlineData(true, false)]
|
[InlineData(true, false)]
|
||||||
[InlineData(false, true)]
|
[InlineData(false, true)]
|
||||||
public void OnAuthorization_RedirectsToHttpsEndpoint_WithSpecifiedStatusCodeAndrequireHttpsPermanentOption(bool? permanent, bool requireHttpsPermanent)
|
public void OnAuthorization_RedirectsToHttpsEndpoint_WithSpecifiedStatusCodeAndRequireHttpsPermanentOption(bool? permanent, bool requireHttpsPermanent)
|
||||||
{
|
{
|
||||||
var requestContext = new DefaultHttpContext();
|
var requestContext = new DefaultHttpContext();
|
||||||
requestContext.RequestServices = CreateServices(null, requireHttpsPermanent);
|
requestContext.RequestServices = CreateServices(null, requireHttpsPermanent);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
"testController",
|
"testController",
|
||||||
"testAction");
|
"testAction");
|
||||||
actionDescriptor.RouteValues.Add("randomKey", "testRandom");
|
actionDescriptor.RouteValues.Add("randomKey", "testRandom");
|
||||||
var descriptorCollectionProvider = CreateActionDesciprtorCollectionProvider(actionDescriptor);
|
var descriptorCollectionProvider = CreateActionDescriptorCollectionProvider(actionDescriptor);
|
||||||
|
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddRouting();
|
services.AddRouting();
|
||||||
|
|
@ -214,7 +214,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
"testAction");
|
"testAction");
|
||||||
actionDescriptor.RouteValues.Add("randomKey", "testRandom");
|
actionDescriptor.RouteValues.Add("randomKey", "testRandom");
|
||||||
|
|
||||||
var provider = CreateActionDesciprtorCollectionProvider(actionDescriptor);
|
var provider = CreateActionDescriptorCollectionProvider(actionDescriptor);
|
||||||
|
|
||||||
var constraint = new KnownRouteValueConstraint(provider);
|
var constraint = new KnownRouteValueConstraint(provider);
|
||||||
|
|
||||||
|
|
@ -235,7 +235,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
|
|
||||||
private static HttpContext GetHttpContext(ActionDescriptor actionDescriptor, bool setupRequestServices = true)
|
private static HttpContext GetHttpContext(ActionDescriptor actionDescriptor, bool setupRequestServices = true)
|
||||||
{
|
{
|
||||||
var descriptorCollectionProvider = CreateActionDesciprtorCollectionProvider(actionDescriptor);
|
var descriptorCollectionProvider = CreateActionDescriptorCollectionProvider(actionDescriptor);
|
||||||
|
|
||||||
var context = new Mock<HttpContext>();
|
var context = new Mock<HttpContext>();
|
||||||
if (setupRequestServices)
|
if (setupRequestServices)
|
||||||
|
|
@ -247,7 +247,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
|
||||||
return context.Object;
|
return context.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IActionDescriptorCollectionProvider CreateActionDesciprtorCollectionProvider(ActionDescriptor actionDescriptor)
|
private static IActionDescriptorCollectionProvider CreateActionDescriptorCollectionProvider(ActionDescriptor actionDescriptor)
|
||||||
{
|
{
|
||||||
var actionProvider = new Mock<IActionDescriptorProvider>(MockBehavior.Strict);
|
var actionProvider = new Mock<IActionDescriptorProvider>(MockBehavior.Strict);
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue