Modify TagHelperDescriptorResolver and dependencies to not throw.
- Changed the TagHelperDescriptorResolver, TagHelperTypeResolver and AddOrRemoveTagHelperSpanVisitor to not throw when they're unable to understand the users directive lookup text. - This involved utilizing the new ParserErrorSink to capture errors found during TagHelperDescriptor resolution. #210
This commit is contained in:
parent
a477bd5cb1
commit
ed9c432889
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot resolve TagHelper containing assembly '{0}'.
|
||||
/// Cannot resolve TagHelper containing assembly '{0}'. Error: {1}
|
||||
/// </summary>
|
||||
internal static string TagHelperTypeResolver_CannotResolveTagHelperAssembly
|
||||
{
|
||||
|
|
@ -39,11 +39,11 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot resolve TagHelper containing assembly '{0}'.
|
||||
/// Cannot resolve TagHelper containing assembly '{0}'. Error: {1}
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(object p0)
|
||||
internal static string FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperTypeResolver_CannotResolveTagHelperAssembly"), p0);
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperTypeResolver_CannotResolveTagHelperAssembly"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -110,6 +110,22 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
return GetString("ArgumentCannotBeNullOrEmpty");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2}
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorResolver_EncounteredUnexpectedError
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorResolver_EncounteredUnexpectedError"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2}
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorResolver_EncounteredUnexpectedError(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorResolver_EncounteredUnexpectedError"), p0, p1, p2);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
"typeName, assemblyName"</value>
|
||||
</data>
|
||||
<data name="TagHelperTypeResolver_CannotResolveTagHelperAssembly" xml:space="preserve">
|
||||
<value>Cannot resolve TagHelper containing assembly '{0}'.</value>
|
||||
<value>Cannot resolve TagHelper containing assembly '{0}'. Error: {1}</value>
|
||||
</data>
|
||||
<data name="TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull" xml:space="preserve">
|
||||
<value>Tag helper directive assembly name cannot be null or empty.</value>
|
||||
|
|
@ -137,4 +137,7 @@
|
|||
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>The value cannot be null or empty.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorResolver_EncounteredUnexpectedError" xml:space="preserve">
|
||||
<value>Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -4,7 +4,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
|
|
@ -36,20 +38,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
foreach (var directiveDescriptor in context.DirectiveDescriptors)
|
||||
{
|
||||
var lookupInfo = GetLookupInfo(directiveDescriptor);
|
||||
|
||||
if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.RemoveTagHelper)
|
||||
try
|
||||
{
|
||||
resolvedDescriptors.RemoveWhere(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
|
||||
var lookupInfo = GetLookupInfo(directiveDescriptor, context.ErrorSink);
|
||||
|
||||
// Could not resolve the lookup info.
|
||||
if (lookupInfo == null)
|
||||
{
|
||||
return Enumerable.Empty<TagHelperDescriptor>();
|
||||
}
|
||||
|
||||
if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.RemoveTagHelper)
|
||||
{
|
||||
resolvedDescriptors.RemoveWhere(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
|
||||
}
|
||||
else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper)
|
||||
{
|
||||
var descriptors = ResolveDescriptorsInAssembly(lookupInfo.AssemblyName,
|
||||
directiveDescriptor.Location,
|
||||
context.ErrorSink);
|
||||
|
||||
// Only use descriptors that match our lookup info
|
||||
descriptors = descriptors.Where(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
|
||||
|
||||
resolvedDescriptors.UnionWith(descriptors);
|
||||
}
|
||||
}
|
||||
else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper)
|
||||
catch (Exception ex)
|
||||
{
|
||||
var descriptors = ResolveDescriptorsInAssembly(lookupInfo.AssemblyName);
|
||||
var directiveName = "@" + directiveDescriptor.DirectiveType.ToString().ToLowerInvariant();
|
||||
|
||||
// Only use descriptors that match our lookup info
|
||||
descriptors = descriptors.Where(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
|
||||
|
||||
resolvedDescriptors.UnionWith(descriptors);
|
||||
context.ErrorSink.OnError(
|
||||
directiveDescriptor.Location,
|
||||
Resources.FormatTagHelperDescriptorResolver_EncounteredUnexpectedError(
|
||||
directiveName,
|
||||
directiveDescriptor.LookupText,
|
||||
ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,13 +87,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <param name="assemblyName">
|
||||
/// The name of the assembly to resolve <see cref="TagHelperDescriptor"/>s from.
|
||||
/// </param>
|
||||
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the directive.</param>
|
||||
/// <param name="errorSink">Used to record errors found when resolving <see cref="TagHelperDescriptor"/>s
|
||||
/// within the given <paramref name="assemblyName"/>.</param>
|
||||
/// <returns><see cref="TagHelperDescriptor"/>s for <see cref="ITagHelper"/>s from the given
|
||||
/// <paramref name="assemblyName"/>.</returns>
|
||||
// This is meant to be overridden by tooling to enable assembly level caching.
|
||||
protected virtual IEnumerable<TagHelperDescriptor> ResolveDescriptorsInAssembly(string assemblyName)
|
||||
protected virtual IEnumerable<TagHelperDescriptor> ResolveDescriptorsInAssembly(string assemblyName,
|
||||
SourceLocation documentLocation,
|
||||
ParserErrorSink errorSink)
|
||||
{
|
||||
// Resolve valid tag helper types from the assembly.
|
||||
var tagHelperTypes = _typeResolver.Resolve(assemblyName);
|
||||
var tagHelperTypes = _typeResolver.Resolve(assemblyName, documentLocation, errorSink);
|
||||
|
||||
// Convert types to TagHelperDescriptors
|
||||
var descriptors = tagHelperTypes.SelectMany(TagHelperDescriptorFactory.CreateDescriptors);
|
||||
|
|
@ -88,7 +117,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
string.Equals(descriptor.TypeName, lookupInfo.TypeName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static LookupInfo GetLookupInfo(TagHelperDirectiveDescriptor directiveDescriptor)
|
||||
private static LookupInfo GetLookupInfo(TagHelperDirectiveDescriptor directiveDescriptor,
|
||||
ParserErrorSink errorSink)
|
||||
{
|
||||
var lookupText = directiveDescriptor.LookupText;
|
||||
var lookupStrings = lookupText?.Split(new[] { ',' });
|
||||
|
|
@ -100,9 +130,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
lookupStrings.Any(string.IsNullOrWhiteSpace) ||
|
||||
(lookupStrings.Length != 1 && lookupStrings.Length != 2))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText),
|
||||
nameof(lookupText));
|
||||
errorSink.OnError(
|
||||
directiveDescriptor.Location,
|
||||
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Grab the assembly name from the lookup text strings. Due to our supported lookupText formats it will
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
|
|
@ -27,18 +29,43 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// all valid <see cref="ITagHelper"/> <see cref="Type"/>s.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of an <see cref="Assembly"/> to search.</param>
|
||||
/// <returns>An <see cref="IEnumerable{Type}"/> of valid <see cref="ITagHelper"/> <see cref="Type"/>s.</returns>
|
||||
public IEnumerable<Type> Resolve(string name)
|
||||
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the associated
|
||||
/// <see cref="Parser.SyntaxTree.SyntaxTreeNode"/> responsible for the current <see cref="Resolve"/> call.
|
||||
/// </param>
|
||||
/// <param name="errorSink">The <see cref="ParserErrorSink"/> used to record errors found when resolving
|
||||
/// <see cref="ITagHelper"/> <see cref="Type"/>s.</param>
|
||||
/// <returns>An <see cref="IEnumerable{Type}"/> of valid <see cref="ITagHelper"/> <see cref="Type"/>s.
|
||||
/// </returns>
|
||||
public IEnumerable<Type> Resolve(string name,
|
||||
SourceLocation documentLocation,
|
||||
[NotNull] ParserErrorSink errorSink)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull,
|
||||
nameof(name));
|
||||
errorSink.OnError(documentLocation,
|
||||
Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull);
|
||||
|
||||
return Type.EmptyTypes;
|
||||
}
|
||||
|
||||
var assemblyName = new AssemblyName(name);
|
||||
var libraryTypes = GetLibraryDefinedTypes(assemblyName);
|
||||
|
||||
IEnumerable<TypeInfo> libraryTypes;
|
||||
try
|
||||
{
|
||||
libraryTypes = GetLibraryDefinedTypes(assemblyName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorSink.OnError(
|
||||
documentLocation,
|
||||
Resources.FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(
|
||||
assemblyName.Name,
|
||||
ex.Message));
|
||||
|
||||
return Type.EmptyTypes;
|
||||
}
|
||||
|
||||
var validTagHelpers = libraryTypes.Where(IsTagHelper);
|
||||
|
||||
// Convert from TypeInfo[] to Type[]
|
||||
|
|
@ -48,17 +75,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
// Internal for testing, don't want to be loading assemblies during a test.
|
||||
internal virtual IEnumerable<TypeInfo> GetLibraryDefinedTypes(AssemblyName assemblyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
|
||||
return assembly.DefinedTypes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(assemblyName.Name), ex);
|
||||
}
|
||||
return assembly.ExportedTypes.Select(type => type.GetTypeInfo());
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
|
|
|
|||
|
|
@ -243,9 +243,8 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
// https://github.com/aspnet/Razor/issues/129
|
||||
if (!isPlainTextValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
RazorResources.FormatTagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols(
|
||||
attributeDescriptor.PropertyName));
|
||||
_writer.WriteLine(";");
|
||||
return;
|
||||
}
|
||||
|
||||
// We aren't a bufferable attribute which means we have no Razor code in our value.
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Razor.Parser
|
|||
|
||||
if (TagHelperDescriptorResolver != null)
|
||||
{
|
||||
var descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree);
|
||||
var descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree, rewritingContext.ErrorSink);
|
||||
var tagHelperProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider);
|
||||
|
|
@ -203,10 +203,15 @@ namespace Microsoft.AspNet.Razor.Parser
|
|||
/// specified <paramref name="documentRoot"/>.
|
||||
/// </summary>
|
||||
/// <param name="documentRoot">The <see cref="Block"/> to scan for tag helper registrations in.</param>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<TagHelperDescriptor> GetTagHelperDescriptors([NotNull] Block documentRoot)
|
||||
/// <param name="errorSink">Used to manage <see cref="RazorError"/>s encountered during the Razor parsing
|
||||
/// phase.</param>
|
||||
/// <returns><see cref="TagHelperDescriptor"/>s that are applicable to the <paramref name="documentRoot"/>
|
||||
/// </returns>
|
||||
protected virtual IEnumerable<TagHelperDescriptor> GetTagHelperDescriptors([NotNull] Block documentRoot,
|
||||
[NotNull] ParserErrorSink errorSink)
|
||||
{
|
||||
var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver);
|
||||
var addOrRemoveTagHelperSpanVisitor =
|
||||
new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver, errorSink);
|
||||
return addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,12 +15,15 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
public class AddOrRemoveTagHelperSpanVisitor : ParserVisitor
|
||||
{
|
||||
private readonly ITagHelperDescriptorResolver _descriptorResolver;
|
||||
private readonly ParserErrorSink _errorSink;
|
||||
|
||||
private List<TagHelperDirectiveDescriptor> _directiveDescriptors;
|
||||
|
||||
public AddOrRemoveTagHelperSpanVisitor([NotNull] ITagHelperDescriptorResolver descriptorResolver)
|
||||
public AddOrRemoveTagHelperSpanVisitor([NotNull] ITagHelperDescriptorResolver descriptorResolver,
|
||||
[NotNull] ParserErrorSink errorSink)
|
||||
{
|
||||
_descriptorResolver = descriptorResolver;
|
||||
_errorSink = errorSink;
|
||||
}
|
||||
|
||||
public IEnumerable<TagHelperDescriptor> GetDescriptors([NotNull] Block root)
|
||||
|
|
@ -30,7 +33,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
// This will recurse through the syntax tree.
|
||||
VisitBlock(root);
|
||||
|
||||
var resolutionContext = GetTagHelperDescriptorResolutionContext(_directiveDescriptors);
|
||||
var resolutionContext = GetTagHelperDescriptorResolutionContext(_directiveDescriptors, _errorSink);
|
||||
var descriptors = _descriptorResolver.Resolve(resolutionContext);
|
||||
|
||||
return descriptors;
|
||||
|
|
@ -38,9 +41,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
|
||||
// Allows MVC a chance to override the TagHelperDescriptorResolutionContext
|
||||
protected virtual TagHelperDescriptorResolutionContext GetTagHelperDescriptorResolutionContext(
|
||||
[NotNull] IEnumerable<TagHelperDirectiveDescriptor> descriptors)
|
||||
[NotNull] IEnumerable<TagHelperDirectiveDescriptor> descriptors,
|
||||
[NotNull] ParserErrorSink errorSink)
|
||||
{
|
||||
return new TagHelperDescriptorResolutionContext(descriptors);
|
||||
return new TagHelperDescriptorResolutionContext(descriptors, errorSink);
|
||||
}
|
||||
|
||||
public override void VisitSpan(Span span)
|
||||
|
|
@ -55,7 +59,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
TagHelperDirectiveType.RemoveTagHelper :
|
||||
TagHelperDirectiveType.AddTagHelper;
|
||||
|
||||
var directiveDescriptor = new TagHelperDirectiveDescriptor(codeGenerator.LookupText, directive);
|
||||
var directiveDescriptor = new TagHelperDirectiveDescriptor(codeGenerator.LookupText,
|
||||
span.Start,
|
||||
directive);
|
||||
|
||||
_directiveDescriptors.Add(directiveDescriptor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
|
|
@ -15,15 +16,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// </summary>
|
||||
/// <param name="directiveDescriptors"><see cref="TagHelperDirectiveDescriptor"/>s used to resolve
|
||||
/// <see cref="TagHelperDescriptor"/>s.</param>
|
||||
/// <param name="errorSink">Used to aggregate <see cref="Parser.SyntaxTree.RazorError"/>s.</param>
|
||||
public TagHelperDescriptorResolutionContext(
|
||||
[NotNull] IEnumerable<TagHelperDirectiveDescriptor> directiveDescriptors)
|
||||
[NotNull] IEnumerable<TagHelperDirectiveDescriptor> directiveDescriptors,
|
||||
[NotNull] ParserErrorSink errorSink)
|
||||
{
|
||||
DirectiveDescriptors = new List<TagHelperDirectiveDescriptor>(directiveDescriptors);
|
||||
ErrorSink = errorSink;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TagHelperDirectiveDescriptor"/>s used to resolve <see cref="TagHelperDescriptor"/>s.
|
||||
/// </summary>
|
||||
public IList<TagHelperDirectiveDescriptor> DirectiveDescriptors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to aggregate <see cref="Parser.SyntaxTree.RazorError"/>s.
|
||||
/// </summary>
|
||||
public ParserErrorSink ErrorSink { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -12,10 +14,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// Instantiates a new instance of <see cref="TagHelperDirectiveDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="lookupText">A <see cref="string"/> used to find tag helper <see cref="System.Type"/>s.</param>
|
||||
/// <param name="location">The <see cref="SourceLocation"/> of the directive.</param>
|
||||
/// <param name="directiveType">The <see cref="TagHelperDirectiveType"/> of this directive.</param>
|
||||
public TagHelperDirectiveDescriptor([NotNull] string lookupText, TagHelperDirectiveType directiveType)
|
||||
public TagHelperDirectiveDescriptor([NotNull] string lookupText,
|
||||
SourceLocation location,
|
||||
TagHelperDirectiveType directiveType)
|
||||
{
|
||||
LookupText = lookupText;
|
||||
Location = location;
|
||||
DirectiveType = directiveType;
|
||||
}
|
||||
|
||||
|
|
@ -28,5 +34,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// The <see cref="TagHelperDirectiveType"/> of this directive.
|
||||
/// </summary>
|
||||
public TagHelperDirectiveType DirectiveType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SourceLocation"/> of the directive.
|
||||
/// </summary>
|
||||
public SourceLocation Location { get; private set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue