-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add generic variants of MVC attributes #47075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="T">A type which configures a middleware pipeline.</typeparam> | ||
| public class MiddlewareFilterAttribute<T> : MiddlewareFilterAttribute | ||
| { | ||
| /// <summary> | ||
| /// Instantiates a new instance of <see cref="MiddlewareFilterAttribute"/>. | ||
| /// </summary> | ||
| public MiddlewareFilterAttribute() : base(typeof(T)) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.AspNetCore.Mvc.ModelBinding; | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="TBinder">A <see cref="Type"/> which implements <see cref="IModelBinder"/>.</typeparam> | ||
| /// <remarks> | ||
| /// This is a derived generic variant of the <see cref="ModelBinderAttribute"/>. | ||
| /// Ensure that only one instance of either attribute is provided on the target. | ||
| /// </remarks> | ||
| public class ModelBinderAttribute<TBinder> : ModelBinderAttribute where TBinder : IModelBinder | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of <see cref="ModelBinderAttribute"/>. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Subclass this attribute and set <see cref="BindingSource"/> if <see cref="BindingSource.Custom"/> is not | ||
| /// correct for the specified type parameter. | ||
| /// </remarks> | ||
| public ModelBinderAttribute() : base(typeof(TBinder)) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="T">The type of metadata class that is associated with a data model class.</typeparam> | ||
| /// <remarks> | ||
| /// This is a derived generic variant of the <see cref="ModelMetadataTypeAttribute"/> | ||
| /// which does not allow multiple instances on a single target. | ||
| /// Ensure that only one instance of either attribute is provided on the target. | ||
| /// </remarks> | ||
| public class ModelMetadataTypeAttribute<T> : ModelMetadataTypeAttribute | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ModelMetadataTypeAttribute" /> class. | ||
| /// </summary> | ||
| public ModelMetadataTypeAttribute() : base(typeof(T)) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="T">The <see cref="Type"/> of object that is going to be written in the response.</typeparam> | ||
| /// <remarks> | ||
| /// This is a derived generic variant of the <see cref="ProducesAttribute"/>. | ||
| /// Ensure that only one instance of either attribute is provided on the target. | ||
| /// </remarks> | ||
| public class ProducesAttribute<T> : ProducesAttribute | ||
| { | ||
| /// <summary> | ||
| /// Initializes an instance of <see cref="ProducesAttribute"/>. | ||
| /// </summary> | ||
| public ProducesAttribute() : base(typeof(T)) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="T">The <see cref="Type"/> of object that is going to be written in the response.</typeparam> | ||
| public class ProducesResponseTypeAttribute<T> : ProducesResponseTypeAttribute | ||
| { | ||
| /// <summary> | ||
| /// Initializes an instance of <see cref="ProducesResponseTypeAttribute"/>. | ||
| /// </summary> | ||
| /// <param name="statusCode">The HTTP response status code.</param> | ||
| public ProducesResponseTypeAttribute(int statusCode) : base(typeof(T), statusCode) { } | ||
|
|
||
| /// <summary> | ||
| /// Initializes an instance of <see cref="ProducesResponseTypeAttribute"/>. | ||
| /// </summary> | ||
| /// <param name="statusCode">The HTTP response status code.</param> | ||
| /// <param name="contentType">The content type associated with the response.</param> | ||
| /// <param name="additionalContentTypes">Additional content types supported by the response.</param> | ||
| public ProducesResponseTypeAttribute(int statusCode, string contentType, params string[] additionalContentTypes) | ||
| : base(typeof(T), statusCode, contentType, additionalContentTypes) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Diagnostics; | ||
| using Microsoft.AspNetCore.Mvc.Filters; | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="TFilter">The <see cref="Type"/> of filter to find.</typeparam> | ||
| [DebuggerDisplay("ServiceFilter: Type={ServiceType} Order={Order}")] | ||
| public class ServiceFilterAttribute<TFilter> : ServiceFilterAttribute where TFilter : IFilterMetadata | ||
| { | ||
| /// <summary> | ||
| /// Instantiates a new <see cref="ServiceFilterAttribute"/> instance. | ||
| /// </summary> | ||
| public ServiceFilterAttribute() : base(typeof(TFilter)) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using Microsoft.AspNetCore.Mvc.Filters; | ||
|
|
||
| namespace Microsoft.AspNetCore.Mvc; | ||
|
|
||
| /// <inheritdoc /> | ||
| /// <typeparam name="TFilter">The <see cref="Type"/> of filter to create.</typeparam> | ||
| public class TypeFilterAttribute<TFilter> : TypeFilterAttribute where TFilter : IFilterMetadata | ||
| { | ||
| /// <summary> | ||
| /// Instantiates a new <see cref="TypeFilterAttribute"/> instance. | ||
| /// </summary> | ||
| public TypeFilterAttribute() : base(typeof(TFilter)) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -287,6 +287,18 @@ public void GetAttributesForProperty_WithModelType_IncludesTypeAttributes() | |
| attribute => Assert.IsType<ClassValidator>(attribute)); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void GetAttributeForProperty_WithModelType_HandlesMultipleAttributesOnType() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great! This covers ModelMetadataType. Are there other attributes that had AllowMultiple=false?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So unlike the Ditto the same situation for So we can throw exceptions at runtime that warn about multiple attributes on the same type but that feels a little heavy handed given the existing behavior for these attributes. Thoughts? |
||
| { | ||
| // Arrange | ||
| var modelType = typeof(InvalidBaseViewModel); | ||
| var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.RouteValue)); | ||
|
|
||
| // Assert | ||
| var exception = Assert.Throws<InvalidOperationException>(() => ModelAttributes.GetAttributesForProperty(modelType, property)); | ||
| Assert.Equal("Only one ModelMetadataType attribute is permitted per type.", exception.Message); | ||
| } | ||
|
|
||
| [ClassValidator] | ||
| private class BaseModel | ||
| { | ||
|
|
@@ -322,7 +334,7 @@ private class DerivedModelWithAttributes : BaseModel | |
| { | ||
| } | ||
|
|
||
| [ModelMetadataType(typeof(BaseModel))] | ||
| [ModelMetadataType<BaseModel>] | ||
| private class BaseViewModel | ||
| { | ||
| [Range(0, 10)] | ||
|
|
@@ -335,7 +347,11 @@ private class BaseViewModel | |
| public string RouteValue { get; set; } | ||
| } | ||
|
|
||
| [ModelMetadataType(typeof(DerivedModel))] | ||
| [ModelMetadataType<BaseModel>] | ||
| [ModelMetadataType(typeof(BaseModel))] | ||
| private class InvalidBaseViewModel : BaseViewModel { } | ||
|
|
||
| [ModelMetadataType<DerivedModel>] | ||
| private class DerivedViewModel : BaseViewModel | ||
| { | ||
| [StringLength(2)] | ||
|
|
@@ -358,7 +374,7 @@ public override bool IsValid(object value) | |
| } | ||
| } | ||
|
|
||
| [ModelMetadataType(typeof(MergedAttributesMetadata))] | ||
| [ModelMetadataType<MergedAttributesMetadata>] | ||
| private class MergedAttributes | ||
| { | ||
| [Required] | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.