Cia*_*her 6 autofac fluentvalidation tag-helpers asp.net-core
我有一个像这样的简单表单,它使用了 @Html.EditorFor 扩展名:
<form method="post">
@Html.EditorFor(x => x.SystemSettings.EmailFromAddress)
<submit-button title="Save"></submit-button>
</form>
Run Code Online (Sandbox Code Playgroud)
我想利用 .NET Core 的标签助手,让我的表单看起来像这样:
<form method="post">
<editor asp-for="SystemSettings.EmailFromAddress"/>
<submit-button title="Save"></submit-button>
</form>
Run Code Online (Sandbox Code Playgroud)
我最终也想拥有自己的自定义标签助手,这样我就可以做这样的事情:
<text-box asp-for="SystemSettings.EmailFromAddress"></text-box>
Run Code Online (Sandbox Code Playgroud)
我有一个由 @Html.EditorFor 扩展呈现的字符串模板:
@model string
<div class="form-group">
<label asp-for="@Model" class="m-b-none"></label>
<span asp-description-for="@Model" class="help-block m-b-none small m-t-none"></span>
<div class="input-group">
<input asp-for="@Model" class="form-control" />
<partial name="_ValidationIcon" />
</div>
<span asp-validation-for="@Model" class="validation-message"></span>
</div>
Run Code Online (Sandbox Code Playgroud)
为此,我看到有人实现了一个EditorTagHelper,它看起来像这样:
[HtmlTargetElement("editor", TagStructure = TagStructure.WithoutEndTag,
Attributes = ForAttributeName)]
public class EditorTagHelper : TagHelper
{
private readonly IHtmlHelper _htmlHelper;
private const string ForAttributeName = "asp-for";
private const string TemplateAttributeName = "asp-template";
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
[HtmlAttributeName(TemplateAttributeName)]
public string Template { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public EditorTagHelper(IHtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
if (!output.Attributes.ContainsName(nameof(Template)))
{
output.Attributes.Add(nameof(Template), Template);
}
output.SuppressOutput();
(_htmlHelper as IViewContextAware).Contextualize(ViewContext);
output.Content.SetHtmlContent(_htmlHelper.Editor(For.Name, Template));
await Task.CompletedTask;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,当我使用 EditorTagHelper 时,它似乎缺少不显眼的 Javascript 验证属性:
使用@Html.EditorFor,它会被渲染:
<input class="form-control valid" type="text" data-val="true" data-val-required="Email From Address cannot be empty" id="SystemSettings_EmailFromAddress" name="SystemSettings.EmailFromAddress" value="whatever@test.com" aria-required="true" aria-invalid="false" aria-describedby="SystemSettings_EmailFromAddress-error">
Run Code Online (Sandbox Code Playgroud)
它具有 data-val 属性,因此应用了客户端验证。
当我使用EditorTagHelper 时,它会呈现:
<input class="form-control valid" type="text" id="SystemSettings_EmailFromAddress" name="SystemSettings.EmailFromAddress" value="whatever@test.com" aria-invalid="false">
Run Code Online (Sandbox Code Playgroud)
未应用不显眼的验证属性。我正在使用 FluentValidation 并且我指定了一个 AbstractValidator 如下:
public class SystemSettingsValidator : AbstractValidator<SystemSettings>
{
public SystemSettingsValidator()
{
RuleFor(x => x.EmailFromAddress).NotEmpty()
.WithMessage("Email From Address cannot be empty");
}
}
Run Code Online (Sandbox Code Playgroud)
我发现如果我删除了 AbstractorValidator 并简单地向我的模型属性添加了 [Required] 属性,则验证可以正常工作。这表明 FluentValidation 存在问题。可能是配置问题。
我正在使用 Autofac 依赖注入来扫描我的程序集并注册验证器类型:
builder.RegisterAssemblyTypes(Assembly.Load(assembly))
.Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
.AsImplementedInterfaces()
.PropertiesAutowired()
.InstancePerLifetimeScope();
Run Code Online (Sandbox Code Playgroud)
这似乎工作正常。以防万一,我还尝试从 fluent 验证选项中注册验证器,如下所示:
.AddFluentValidation(fv =>
{
fv.RegisterValidatorsFromAssemblies(new List<Assembly>
{Assembly.GetExecutingAssembly(), Assembly.Load(nameof(Entities))});
})
Run Code Online (Sandbox Code Playgroud)
这似乎也很好。
需要注意的一件事是,我之前遇到的一个问题是,当包含标签助手时,使用 Autofac 程序集扫描会破坏应用程序。我添加了一个过滤器以确保在注册这些依赖项时不包含标签助手,例如
builder.RegisterAutowiredAssemblyInterfaces(Assembly.Load(Web))
.Where(x => !x.Name.EndsWith("TagHelper"));
Run Code Online (Sandbox Code Playgroud)
我在这里上传了代码的工作示例:https : //github.com/ciaran036/coresample2
导航到设置页面以查看我尝试验证的字段。
此问题似乎也会影响视图组件。
谢谢。
我认为问题出在标签助手中,因为它使用IHtmlHelper.Editor而不是IHtmlHelper<TModel>.EditorFor生成 HTML 内容。它们并不完全相同。
正如您所指出的,FluentValidation 正如您所期望的那样注入验证属性@Html.EditorFor(x => x.SystemSettings.EmailFromAddress)。但是,对于@Html.Editor("SystemSettings.EmailFromAddress"),这就是您的自定义标记帮助程序正在执行的操作,FluentValidation 不会注入验证属性。这样就排除了标签助手本身,并将问题转移到Editor调用上。
我还注意到它Editor无法解决<label asp-for(或您正在使用的其他<span asp-description-for标签帮助器),因此这表明这不是 FluentValidation 特定的问题。
我无法使用Required自定义标签助手/的属性复制您的成功Editor- 该Required属性仅在使用时注入验证属性EditorFor。
Editor和 的内部结构EditorFor相似,但有一个关键区别,它们解析ModelExplorer用于生成 HTML 内容的实例的方式不同,我怀疑这就是问题所在。请参阅下文了解这些差异。
像PropertyName设置为 null 和Metadata.Property不设置为Editor,但设置为EmailFromAddress和SystemSettings.EmailFromAddressfor 之EditorFor类的事情是我们所看到的行为的潜在原因。
痛苦的部分是标签助手ModelExplorer通过For属性拥有一个有效的实例。但没有内置的规定将其提供给 html 帮助程序。
至于解决方案,最明显的似乎是使用EditorFor而不是Editor但它看起来并不容易。它可能涉及反思和构建表达式。
另一种选择是,考虑到标签帮助器可以ModelExplorer正确解决问题,那就是扩展并HtmlHelper重写该GenerateEditor方法(两者最终都会调用),以便您可以传入 ModelExplorer 并解决问题。EditorEditorFor
public class CustomHtmlHelper : HtmlHelper, IHtmlHelper
{
public CustomHtmlHelper(IHtmlGenerator htmlGenerator, ICompositeViewEngine viewEngine, IModelMetadataProvider metadataProvider, IViewBufferScope bufferScope, HtmlEncoder htmlEncoder, UrlEncoder urlEncoder) : base(htmlGenerator, viewEngine, metadataProvider, bufferScope, htmlEncoder, urlEncoder) { }
public IHtmlContent CustomGenerateEditor(ModelExplorer modelExplorer, string htmlFieldName, string templateName, object additionalViewData)
{
return GenerateEditor(modelExplorer, htmlFieldName, templateName, additionalViewData);
}
protected override IHtmlContent GenerateEditor(ModelExplorer modelExplorer, string htmlFieldName, string templateName, object additionalViewData)
{
return base.GenerateEditor(modelExplorer, htmlFieldName, templateName, additionalViewData);
}
}
Run Code Online (Sandbox Code Playgroud)
更新您的标签助手以使用它:
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
if (!output.Attributes.ContainsName(nameof(Template)))
{
output.Attributes.Add(nameof(Template), Template);
}
output.SuppressOutput();
(_htmlHelper as IViewContextAware).Contextualize(ViewContext);
var customHtmlHelper = _htmlHelper as CustomHtmlHelper;
var content = customHtmlHelper.CustomGenerateEditor(For.ModelExplorer, For.Metadata.DisplayName ?? For.Metadata.PropertyName, Template, null);
output.Content.SetHtmlContent(content);
await Task.CompletedTask;
}
Run Code Online (Sandbox Code Playgroud)
最后注册新助手,越早越好
services.AddScoped<IHtmlHelper, CustomHtmlHelper>();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
399 次 |
| 最近记录: |