Always render antiforgery tokens if `!CanRenderAtEndOfForm`
- #5005 - also add `FormContext` doc comments
This commit is contained in:
parent
581a5ea573
commit
8a6e99c7c0
|
|
@ -156,15 +156,19 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
throw new ArgumentNullException(nameof(viewContext));
|
||||
}
|
||||
|
||||
// If we're inside a BeginForm/BeginRouteForm, the antiforgery token might have already been
|
||||
// created and appended to the 'end form' content OR the form tag helper might have already generated
|
||||
// an antiforgery token.
|
||||
if (viewContext.FormContext.HasAntiforgeryToken)
|
||||
var formContext = viewContext.FormContext;
|
||||
if (formContext.CanRenderAtEndOfForm)
|
||||
{
|
||||
return HtmlString.Empty;
|
||||
}
|
||||
// Inside a BeginForm/BeginRouteForm or a <form> tag helper. So, the antiforgery token might have
|
||||
// already been created and appended to the 'end form' content (the AntiForgeryToken HTML helper does
|
||||
// this) OR the <form> tag helper might have already generated an antiforgery token.
|
||||
if (formContext.HasAntiforgeryToken)
|
||||
{
|
||||
return HtmlString.Empty;
|
||||
}
|
||||
|
||||
viewContext.FormContext.HasAntiforgeryToken = true;
|
||||
formContext.HasAntiforgeryToken = true;
|
||||
}
|
||||
|
||||
return _antiforgery.GetHtml(viewContext.HttpContext);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@ using Microsoft.AspNetCore.Html;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Information about the current <form>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Literal <form> elements in a view will share the default <see cref="FormContext"/> instance unless tag
|
||||
/// helpers are enabled.
|
||||
/// </remarks>
|
||||
public class FormContext
|
||||
{
|
||||
private Dictionary<string, bool> _renderedFields;
|
||||
|
|
@ -14,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
private IList<IHtmlContent> _endOfFormContent;
|
||||
|
||||
/// <summary>
|
||||
/// Property bag for any information you wish to associate with a <form/> in an
|
||||
/// Gets a property bag for any information you wish to associate with a <form/> in an
|
||||
/// <see cref="Rendering.IHtmlHelper"/> implementation or extension method.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> FormData
|
||||
|
|
@ -30,12 +37,36 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an indication the current <form> element contains an antiforgery token. Do not use
|
||||
/// unless <see cref="CanRenderAtEndOfForm"/> is <c>true</c>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the current <form> element contains an antiforgery token; <c>false</c> otherwise.
|
||||
/// </value>
|
||||
public bool HasAntiforgeryToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication the <see cref="FormData"/> property bag has been used and likely contains entries.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the backing field for <see cref="FormData"/> is non-<c>null</c>; <c>false</c> otherwise.
|
||||
/// </value>
|
||||
public bool HasFormData => _formData != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication the <see cref="EndOfFormContent"/> collection has been used and likely contains entries.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the backing field for <see cref="EndOfFormContent"/> is non-<c>null</c>; <c>false</c>
|
||||
/// otherwise.
|
||||
/// </value>
|
||||
public bool HasEndOfFormContent => _endOfFormContent != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IHtmlContent"/> collection that should be rendered just prior to the next </form>
|
||||
/// end tag. Do not use unless <see cref="CanRenderAtEndOfForm"/> is <c>true</c>.
|
||||
/// </summary>
|
||||
public IList<IHtmlContent> EndOfFormContent
|
||||
{
|
||||
get
|
||||
|
|
@ -49,8 +80,22 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an indication whether extra content can be rendered at the end of the content of this
|
||||
/// <form> element. That is, <see cref="EndOfFormContent"/> will be rendered just prior to the
|
||||
/// </form> end tag.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if the framework will render <see cref="EndOfFormContent"/>; <c>false</c> otherwise. In
|
||||
/// particular, <c>true</c> if the current <form> is associated with a tag helper or will be generated by
|
||||
/// an HTML helper; <c>false</c> when using the default <see cref="FormContext"/> instance.
|
||||
/// </value>
|
||||
public bool CanRenderAtEndOfForm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary mapping full HTML field names to indications that the named field has been rendered in
|
||||
/// this <form>.
|
||||
/// </summary>
|
||||
private Dictionary<string, bool> RenderedFields
|
||||
{
|
||||
get
|
||||
|
|
@ -64,6 +109,14 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an indication based on <see cref="RenderedFields"/> that the given <paramref name="fieldName"/> has
|
||||
/// been rendered in this <form>.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The full HTML name of a field that may have been rendered.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the given <paramref name="fieldName"/> has been rendered; <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public bool RenderedField(string fieldName)
|
||||
{
|
||||
if (fieldName == null)
|
||||
|
|
@ -77,6 +130,12 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates <see cref="RenderedFields"/> to indicate <paramref name="fieldName"/> has been rendered in this
|
||||
/// <form>.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The full HTML name of a field that may have been rendered.</param>
|
||||
/// <param name="value">If <c>true</c>, the given <paramref name="fieldName"/> has been rendered.</param>
|
||||
public void RenderedField(string fieldName, bool value)
|
||||
{
|
||||
if (fieldName == null)
|
||||
|
|
|
|||
|
|
@ -641,6 +641,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var htmlGenerator = GetGenerator(metadataProvider);
|
||||
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
||||
viewContext.FormContext.CanRenderAtEndOfForm = true;
|
||||
viewContext.FormContext.HasAntiforgeryToken = hasAntiforgeryToken;
|
||||
|
||||
// Act
|
||||
|
|
@ -651,6 +652,30 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
Assert.Equal(expectedAntiforgeryHtmlField, antiforgeryField);
|
||||
}
|
||||
|
||||
// This test covers use of the helper within literal <form> tags when tag helpers are not enabled e.g.
|
||||
// <form action="/Home/Create">
|
||||
// @Html.AntiForgeryToken()
|
||||
// </form>
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void GenerateAntiforgery_AlwaysGeneratesAntiforgeryToken_IfCannotRenderAtEnd(bool hasAntiforgeryToken)
|
||||
{
|
||||
// Arrange
|
||||
var expected = "<input name=\"formFieldName\" type=\"hidden\" value=\"requestToken\" />";
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var htmlGenerator = GetGenerator(metadataProvider);
|
||||
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
||||
viewContext.FormContext.HasAntiforgeryToken = hasAntiforgeryToken;
|
||||
|
||||
// Act
|
||||
var result = htmlGenerator.GenerateAntiforgery(viewContext);
|
||||
|
||||
// Assert
|
||||
var antiforgeryField = HtmlContentUtilities.HtmlContentToString(result, HtmlEncoder.Default);
|
||||
Assert.Equal(expected, antiforgeryField);
|
||||
}
|
||||
|
||||
// GetCurrentValues uses only the IModelMetadataProvider passed to the DefaultHtmlGenerator constructor.
|
||||
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue