Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Adding PageActionDescriptorProvider
Browse files Browse the repository at this point in the history
Fixes #5353
  • Loading branch information
pranavkm committed Nov 17, 2016
1 parent 3b4d8e2 commit 10091f1
Show file tree
Hide file tree
Showing 17 changed files with 911 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,33 @@ public static AttributeRouteModel CombineAttributeRouteModel(
};
}

/// <summary>
/// Combines the prefix and route template for an attribute route.
/// </summary>
/// <param name="prefix">The prefix.</param>
/// <param name="template">The route template.</param>
/// <returns>The combined pattern.</returns>
public static string CombineTemplates(string prefix, string template)
{
var result = CombineCore(prefix, template);
return CleanTemplate(result);
}

/// <summary>
/// Determines if a template pattern can be used to override a prefix.
/// </summary>
/// <param name="template">The template.</param>
/// <returns><c>true</c> if this is an overriding template, <c>false</c> otherwise.</returns>
/// <remarks>
/// Route templates starting with "~/" or "/" can be used to override the prefix.
/// </remarks>
public static bool IsOverridePattern(string template)
{
return template != null &&
(template.StartsWith("~/", StringComparison.Ordinal) ||
template.StartsWith("/", StringComparison.Ordinal));
}

private static string ChooseName(
AttributeRouteModel left,
AttributeRouteModel right)
Expand All @@ -113,12 +140,6 @@ private static string ChooseName(
}
}

internal static string CombineTemplates(string left, string right)
{
var result = CombineCore(left, right);
return CleanTemplate(result);
}

private static string CombineCore(string left, string right)
{
if (left == null && right == null)
Expand All @@ -143,13 +164,6 @@ private static string CombineCore(string left, string right)
return left + "/" + right;
}

private static bool IsOverridePattern(string template)
{
return template != null &&
(template.StartsWith("~/", StringComparison.Ordinal) ||
template.StartsWith("/", StringComparison.Ordinal));
}

private static bool IsEmptyLeftSegment(string template)
{
return template == null ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Abstractions;
Expand All @@ -14,7 +13,6 @@
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing.Tree;

namespace Microsoft.AspNetCore.Mvc.Internal
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
/// <summary>
/// Allows customization of the of the <see cref="PageModel"/>.
/// </summary>
public interface IPageModelConvention
{
/// <summary>
/// Called to apply the convention to the <see cref="PageModel"/>.
/// </summary>
/// <param name="model">The <see cref="PageModel"/>.</param>
void Apply(PageModel model);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
public class PageModel
{
public PageModel(string relativePath, string viewEnginePath)
{
if (relativePath == null)
{
throw new ArgumentNullException(nameof(relativePath));
}

if (viewEnginePath == null)
{
throw new ArgumentNullException(nameof(viewEnginePath));
}

RelativePath = relativePath;
ViewEnginePath = viewEnginePath;

Filters = new List<IFilterMetadata>();
Properties = new Dictionary<object, object>();
Selectors = new List<SelectorModel>();
}

public PageModel(PageModel other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}

RelativePath = other.RelativePath;
ViewEnginePath = other.ViewEnginePath;

Filters = new List<IFilterMetadata>(other.Filters);
Properties = new Dictionary<object, object>(other.Properties);

Selectors = new List<SelectorModel>(other.Selectors.Select(m => new SelectorModel(m)));
}

public string RelativePath { get; }

public string ViewEnginePath { get; }

public IList<IFilterMetadata> Filters { get; }

public IDictionary<object, object> Properties { get; }

public IList<SelectorModel> Selectors { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultRazorProject : RazorProject
{
private const string RazorFileExtension = ".cshtml";
private readonly IFileProvider _provider;

public DefaultRazorProject(IFileProvider provider)
{
_provider = provider;
}

public override IEnumerable<RazorProjectItem> EnumerateItems(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}

if (path.Length == 0 || path[0] != '/')
{
throw new ArgumentException(Resources.RazorProject_PathMustStartWithForwardSlash);
}

return EnumerateFiles(_provider.GetDirectoryContents(path), path, "");
}

private IEnumerable<RazorProjectItem> EnumerateFiles(IDirectoryContents directory, string basePath, string prefix)
{
if (directory.Exists)
{
foreach (var file in directory)
{
if (file.IsDirectory)
{
var children = EnumerateFiles(_provider.GetDirectoryContents(file.PhysicalPath), basePath, prefix + "/" + file.Name);
foreach (var child in children)
{
yield return child;
}
}
else if (string.Equals(RazorFileExtension, Path.GetExtension(file.Name), StringComparison.OrdinalIgnoreCase))
{
yield return new DefaultRazorProjectItem(file, basePath, prefix + "/" + file.Name);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultRazorProjectItem : RazorProjectItem
{
private readonly IFileInfo _fileInfo;

public DefaultRazorProjectItem(IFileInfo fileInfo, string basePath, string path)
{
_fileInfo = fileInfo;
BasePath = basePath;
Path = path;
}

public override string BasePath { get; }

public override string Path { get; }

public override string PhysicalPath => _fileInfo.PhysicalPath;

public override Stream Read()
{
return _fileInfo.CreateReadStream();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class PageActionDescriptorProvider : IActionDescriptorProvider
{
private static readonly string IndexFileName = "Index.cshtml";
private readonly RazorProject _project;
private readonly MvcOptions _mvcOptions;
private readonly RazorPagesOptions _pagesOptions;

public PageActionDescriptorProvider(
RazorProject project,
IOptions<MvcOptions> mvcOptionsAccessor,
IOptions<RazorPagesOptions> pagesOptionsAccessor)
{
_project = project;
_mvcOptions = mvcOptionsAccessor.Value;
_pagesOptions = pagesOptionsAccessor.Value;
}

public int Order { get; set; }

public void OnProvidersExecuting(ActionDescriptorProviderContext context)
{
foreach (var item in _project.EnumerateItems("/"))
{
if (item.Filename.StartsWith("_"))
{
// Pages like _PageImports should not be routable.
continue;
}

string template;
if (!PageDirectiveFeature.TryGetRouteTemplate(item, out template))
{
// .cshtml pages without @page are not RazorPages.
continue;
}

if (AttributeRouteModel.IsOverridePattern(template))
{
throw new InvalidOperationException(string.Format(
Resources.PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable,
item.Path));
}

AddActionDescriptors(context.Results, item, template);
}
}

public void OnProvidersExecuted(ActionDescriptorProviderContext context)
{
}

private void AddActionDescriptors(IList<ActionDescriptor> actions, RazorProjectItem item, string template)
{
var model = new PageModel(item.CombinedPath, item.PathWithoutExtension);
var routePrefix = item.BasePath == "/" ? item.PathWithoutExtension : item.BasePath + item.PathWithoutExtension;
model.Selectors.Add(CreateSelectorModel(routePrefix, template));

if (string.Equals(IndexFileName, item.Filename, StringComparison.OrdinalIgnoreCase))
{
model.Selectors.Add(CreateSelectorModel(item.BasePath, template));
}

for (var i = 0; i < _pagesOptions.Conventions.Count; i++)
{
_pagesOptions.Conventions[i].Apply(model);
}

var filters = new List<FilterDescriptor>(_mvcOptions.Filters.Count + model.Filters.Count);
for (var i = 0; i < _mvcOptions.Filters.Count; i++)
{
filters.Add(new FilterDescriptor(_mvcOptions.Filters[i], FilterScope.Global));
}

for (var i = 0; i < model.Filters.Count; i++)
{
filters.Add(new FilterDescriptor(model.Filters[i], FilterScope.Action));
}

foreach (var selector in model.Selectors)
{
actions.Add(new PageActionDescriptor()
{
AttributeRouteInfo = new AttributeRouteInfo()
{
Name = selector.AttributeRouteModel.Name,
Order = selector.AttributeRouteModel.Order ?? 0,
Template = selector.AttributeRouteModel.Template,
},
DisplayName = $"Page: {item.Path}",
FilterDescriptors = filters,
Properties = new Dictionary<object, object>(model.Properties),
RelativePath = item.CombinedPath,
RouteValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "page", item.PathWithoutExtension },
},
ViewEnginePath = item.Path,
});
}
}

private static SelectorModel CreateSelectorModel(string prefix, string template)
{
return new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Template = AttributeRouteModel.CombineTemplates(prefix, template),
}
};
}
}
}
Loading

0 comments on commit 10091f1

Please sign in to comment.