I'm returning a Page<SomeObject> In my method endpoints. In my OpenAPI UI, how can I generate a description of the fields that come from org.springframework.data.domain.Page type.
Normally what I do is go to the Object that I want to document and annotate its fields with #Schema(description = "some description"). But I don't have access to the Page class. How can I document its fields?
I'm probably missing something simple here, but any help would be appreciated.
PS: I am using OpenAPI v3 and springdoc
You can use OpenApiCustomiser.
Let's say for example you have this controller:
#RestController
public class HelloController {
#GetMapping("/hello")
Page<PersonDTO> getPage() {
return null;
}
}
Let's suppose you want add the description the Pageable element:
#Bean
public OpenApiCustomiser pageableOpenApiCustomiser() {
return openApi -> {
Schema pageableSchema = openApi.getComponents().getSchemas().get("Pageable");
pageableSchema.setDescription("my description");
};
}
The same logic applies for any other attribute you nned to customize.
Related
Looking to add the AuthorizeFilterAttribute or AnonymousFilterAttribute to an endpoint in Swashbuckle's implementation of Swagger so I can see which attribute is used on each endpoint in the generated documentation file in a running webapi that ends in /swagger. Is this currenlty possible?
I specifically would like to add a big bold label that says this endpoint is [Anonymous] or that endpoint is using [Authorize] and have them look differently that the summary or remark text.
Also I would like to be able to filter out all the different types of these restriction filter attributes for each endpoint including [NonAction], [Authorize], and [Anonymous] where one of these might be at the top of each controller endpoint. Maybe even eventually add other types of FilterAttributes besides these on each endpoint.
Currently it looks like only the HTTP Methods, the request and response objects can be retrieved in the current implementation so I was not able to find definitive information on this.
Since this is a Swagger implementation do these .NET specific attribute filters not translate to Swashbuckle b/c they only implement what's in the Swagger specification and nothing else?
Finally are their .NET specific extensions to Swashbuckle's implementation that do this?
Thanks!
For the part adding the label to unprotected methods/actions you could use an operation filter like this
public class UnprotectedOperationFilter : IOperationFilter
{
private bool HasAttribute(MethodInfo methodInfo, Type type, bool inherit)
{
// inhertit = true also checks inherited attributes
var actionAttributes = methodInfo.GetCustomAttributes(inherit);
var controllerAttributes = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(inherit);
var actionAndControllerAttributes = actionAttributes.Union(controllerAttributes);
return actionAndControllerAttributes.Any(attr => attr.GetType() == type);
}
public void Apply(Operation operation, OperationFilterContext context)
{
bool hasAuthorizeAttribute = HasAttribute(context.MethodInfo, typeof(AuthorizeAttribute), true);
bool hasAnonymousAttribute = HasAttribute(context.MethodInfo, typeof(AllowAnonymousAttribute), true);
// so far as I understood the action/operation is public/unprotected
// if there is no authorize or an allow anonymous (allow anonymous overrides all authorize)
bool isAuthorized = hasAuthorizeAttribute && !hasAnonymousAttribute;
if (!isAuthorized)
{
operation.Description =
"<p><bold>BIG BOLD LABEL indicating an UPROTECTED PUBLIC method</bold></p>"
+ operation.Description;
}
}
}
and add it with
services.AddSwaggerGen(c => { c.OperationFilter<UnprotectedOperationFilter>();} );
I didn't understand what you mean with filter out different attributes but I hope the code above helps you to check if the attribute is present and do what you desire to do.
Is there a way to make a reflection for a view model property as an element with different name and id values on the html side.
That is the main question of what I want to achieve. So the basic introduction for the question is like:
1- I have a view model (as an example) which created for a filter operation in view side.
public class FilterViewModel
{
public string FilterParameter { get; set; }
}
2- I have a controller action which is created for GETting form values(here it is filter)
public ActionResult Index(FilterViewModel filter)
{
return View();
}
3- I have a view that a user can filter on some data and sends parameters via querystring over form submit.
#using (Html.BeginForm("Index", "Demo", FormMethod.Get))
{
#Html.LabelFor(model => model.FilterParameter)
#Html.EditorFor(model => model.FilterParameter)
<input type="submit" value="Do Filter" />
}
4- And what I want to see in rendered view output is
<form action="/Demo" method="get">
<label for="fp">FilterParameter</label>
<input id="fp" name="fp" type="text" />
<input type="submit" value="Do Filter" />
</form>
5- And as a solution I want to modify my view model like this:
public class FilterViewModel
{
[BindParameter("fp")]
[BindParameter("filter")] // this one extra alias
[BindParameter("param")] //this one extra alias
public string FilterParameter { get; set; }
}
So the basic question is about BindAttribute but the usage of complex type properties. But also if there is a built in way of doing this is much better.
Built-in pros:
1- Usage with TextBoxFor, EditorFor, LabelFor and other strongly typed view model helpers can understand and communicate better with each other.
2- Url routing support
3- No framework by desing problems :
In general, we recommend folks don’t write custom model binders
because they’re difficult to get right and they’re rarely needed. The
issue I’m discussing in this post might be one of those cases where
it’s warranted.
Link of quote
And also after some research I found these useful works:
Binding model property with different name
One step upgrade of first link
Here some informative guide
Result: But none of them give me my problems exact solution. I am looking for a strongly typed solution for this problem. Of course if you know any other way to go, please share.
Update
The underlying reasons why I want to do this are basically:
1- Everytime I want to change the html control name then I have to change PropertyName at compile time. (There is a difference Changing a property name between changing a string in code)
2- I want to hide (camouflage) real property names from end users. Most of times View Model property names same as mapped Entity Objects property names. (For developer readability reasons)
3- I don't want to remove the readability for developer. Think about lots of properties with like 2-3 character long and with mo meanings.
4- There are lots of view models written. So changing their names are going to take more time than this solution.
5- This is going to be better solution (in my POV) than others which are described in other questions until now.
Actually, there is a way to do it.
In ASP.NET binding metadata gathered by TypeDescriptor, not by reflection directly. To be more precious, AssociatedMetadataTypeTypeDescriptionProvider is used, which, in turn, simply calls TypeDescriptor.GetProvider with our model type as parameter:
public AssociatedMetadataTypeTypeDescriptionProvider(Type type)
: base(TypeDescriptor.GetProvider(type))
{
}
So, everything we need is to set our custom TypeDescriptionProvider for our model.
Let's implement our custom provider. First of all, let's define attribute for custom property name:
[AttributeUsage(AttributeTargets.Property)]
public class CustomBindingNameAttribute : Attribute
{
public CustomBindingNameAttribute(string propertyName)
{
this.PropertyName = propertyName;
}
public string PropertyName { get; private set; }
}
If you already have attribute with desired name, you can reuse it. Attribute defined above is just an example. I prefer to use JsonPropertyAttribute because in most cases I work with json and Newtonsoft's library and want to define custom name only once.
The next step is to define custom type descriptor. We will not implement whole type descriptor logic and use default implementation. Only property accessing will be overridden:
public class MyTypeDescription : CustomTypeDescriptor
{
public MyTypeDescription(ICustomTypeDescriptor parent)
: base(parent)
{
}
public override PropertyDescriptorCollection GetProperties()
{
return Wrap(base.GetProperties());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return Wrap(base.GetProperties(attributes));
}
private static PropertyDescriptorCollection Wrap(PropertyDescriptorCollection src)
{
var wrapped = src.Cast<PropertyDescriptor>()
.Select(pd => (PropertyDescriptor)new MyPropertyDescriptor(pd))
.ToArray();
return new PropertyDescriptorCollection(wrapped);
}
}
Also custom property descriptor need to be implemented. Again, everything except property name will be handled by default descriptor. Note, NameHashCode for some reason is a separate property. As name changed, so it's hash code need to be changed too:
public class MyPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor _descr;
private readonly string _name;
public MyPropertyDescriptor(PropertyDescriptor descr)
: base(descr)
{
this._descr = descr;
var customBindingName = this._descr.Attributes[typeof(CustomBindingNameAttribute)] as CustomBindingNameAttribute;
this._name = customBindingName != null ? customBindingName.PropertyName : this._descr.Name;
}
public override string Name
{
get { return this._name; }
}
protected override int NameHashCode
{
get { return this.Name.GetHashCode(); }
}
public override bool CanResetValue(object component)
{
return this._descr.CanResetValue(component);
}
public override object GetValue(object component)
{
return this._descr.GetValue(component);
}
public override void ResetValue(object component)
{
this._descr.ResetValue(component);
}
public override void SetValue(object component, object value)
{
this._descr.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return this._descr.ShouldSerializeValue(component);
}
public override Type ComponentType
{
get { return this._descr.ComponentType; }
}
public override bool IsReadOnly
{
get { return this._descr.IsReadOnly; }
}
public override Type PropertyType
{
get { return this._descr.PropertyType; }
}
}
Finally, we need our custom TypeDescriptionProvider and way to bind it to our model type. By default, TypeDescriptionProviderAttribute is designed to perform that binding. But in this case we will not able to get default provider that we want to use internally. In most cases, default provider will be ReflectTypeDescriptionProvider. But this is not guaranteed and this provider is inaccessible due to it's protection level - it's internal. But we do still want to fallback to default provider.
TypeDescriptor also allow to explicitly add provider for our type via AddProvider method. That what we will use. But firstly, let's define our custom provider itself:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
private readonly TypeDescriptionProvider _defaultProvider;
public MyTypeDescriptionProvider(TypeDescriptionProvider defaultProvider)
{
this._defaultProvider = defaultProvider;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new MyTypeDescription(this._defaultProvider.GetTypeDescriptor(objectType, instance));
}
}
The last step is to bind our provider to our model types. We can implement it in any way we want. For example, let's define some simple class, such as:
public static class TypeDescriptorsConfig
{
public static void InitializeCustomTypeDescriptorProvider()
{
// Assume, this code and all models are in one assembly
var types = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetProperties().Any(p => p.IsDefined(typeof(CustomBindingNameAttribute))));
foreach (var type in types)
{
var defaultProvider = TypeDescriptor.GetProvider(type);
TypeDescriptor.AddProvider(new MyTypeDescriptionProvider(defaultProvider), type);
}
}
}
And either invoke that code via web activation:
[assembly: PreApplicationStartMethod(typeof(TypeDescriptorsConfig), "InitializeCustomTypeDescriptorProvider")]
Or simply call it in Application_Start method:
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
TypeDescriptorsConfig.InitializeCustomTypeDescriptorProvider();
// rest of init code ...
}
}
But this is not the end of the story. :(
Consider following model:
public class TestModel
{
[CustomBindingName("actual_name")]
[DisplayName("Yay!")]
public string TestProperty { get; set; }
}
If we try to write in .cshtml view something like:
#model Some.Namespace.TestModel
#Html.DisplayNameFor(x => x.TestProperty) #* fail *#
We will get ArgumentException:
An exception of type 'System.ArgumentException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: The property Some.Namespace.TestModel.TestProperty could not be found.
That because all helpers soon or later invoke ModelMetadata.FromLambdaExpression method. And this method take expression we provided (x => x.TestProperty) and takes member name directly from member info and have no clue about any of our attributes, metadata (who cares, huh?):
internal static ModelMetadata FromLambdaExpression<TParameter, TValue>(/* ... */)
{
// ...
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression) expression.Body;
propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : (string) null;
// I want to cry here - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ...
}
For x => x.TestProperty (where x is TestModel) this method will return TestProperty, not actual_name, but model metadata contains actual_name property, have no TestProperty. That is why the property could not be found error thrown.
This is a design failure.
However despite this little inconvenience there are several workarounds, such as:
The easiest way is to access our members by theirs redefined names:
#model Some.Namespace.TestModel
#Html.DisplayName("actual_name") #* this will render "Yay!" *#
This is not good. No intellisense at all and as our model change we will have no any compilation errors. On any change anything can be broken and there is no easy way to detect that.
Another way is a bit more complex - we can create our own version of that helpers and forbid anybody from calling default helpers or ModelMetadata.FromLambdaExpression for model classes with renamed properties.
Finally, combination of previous two would be preferred: write own analogue to get property name with redefinition support, then pass that into default helper. Something like this:
#model Some.Namespace.TestModel
#Html.DisplayName(Html.For(x => x.TestProperty))
Compilation-time and intellisense support and no need to spend a lot of time for complete set of helpers. Profit!
Also everything described above work like a charm for model binding. During model binding process default binder also use metadata, gathered by TypeDescriptor.
But I guess binding json data is the best use case. You know, lots of web software and standards use lowercase_separated_by_underscores naming convention. Unfortunately this is not usual convention for C#. Having classes with members named in different convention looks ugly and can end up in troubles. Especially when you have tools that whining every time about naming violation.
ASP.NET MVC default model binder does not bind json to model the same way as it happens when you call newtonsoft's JsonConverter.DeserializeObject method. Instead, json parsed into dictionary. For example:
{
complex: {
text: "blabla",
value: 12.34
},
num: 1
}
will be translated into following dictionary:
{ "complex.text", "blabla" }
{ "complex.value", "12.34" }
{ "num", "1" }
And later these values along with others values from query string, route data and so on, collected by different implementations of IValueProvider, will be used by default binder to bind a model with help of metadata, gathered by TypeDescriptor.
So we came full circle from creating model, rendering, binding it back and use it.
The short answer is NO and long answer still NO. There is no built-in helper, attribute, model binder, whatever is it (Nothing out of box).
But what I did in before answer (I deleted it) was an awful solution that I realized yesterday. I am going to put it in github for who still wants to see (maybe it solves somebody problem) (I don't suggest it also!)
Now I searched it for again and I couldn't find anything helpful. If you are using something like AutoMapper or ValueInjecter like tool for mapping your ViewModel objects to Business objects and if you want to obfuscate that View Model parameters also, probably you are in some trouble. Of course you can do it but strongly typed html helpers are not going to help you alot. I even not talking about the if other developers taking branch and working over common view models.
Luckily my project (4 people working on it, and its commercial use for) not that big for now, so I decided to change View Model property names! (It is still lot work to do. Hundreds of view models to obfuscate their properties!!!) Thank you Asp.Net MVC !
There some ways in the links which I gave in question. But also if you still want to use the BindAlias attribute, I can only suggest you to use the following extension methods. At least you dont have to write same alias string which you write in BindAlias attribute.
Here it is:
public static string AliasNameFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var memberExpression = ExpressionHelpers.GetMemberExpression(expression);
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
var aliasAttr = memberExpression.Member.GetAttribute<BindAliasAttribute>();
if (aliasAttr != null)
{
return MvcHtmlString.Create(aliasAttr.Alias).ToHtmlString();
}
return htmlHelper.NameFor(expression).ToHtmlString();
}
public static string AliasIdFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var memberExpression = ExpressionHelpers.GetMemberExpression(expression);
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
var aliasAttr = memberExpression.Member.GetAttribute<BindAliasAttribute>();
if (aliasAttr != null)
{
return MvcHtmlString.Create(TagBuilder.CreateSanitizedId(aliasAttr.Alias)).ToHtmlString();
}
return htmlHelper.IdFor(expression).ToHtmlString();
}
public static T GetAttribute<T>(this ICustomAttributeProvider provider)
where T : Attribute
{
var attributes = provider.GetCustomAttributes(typeof(T), true);
return attributes.Length > 0 ? attributes[0] as T : null;
}
public static MemberExpression GetMemberExpression<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
MemberExpression memberExpression;
if (expression.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)expression.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)expression.Body;
}
return memberExpression;
}
When you want to use it:
[ModelBinder(typeof(AliasModelBinder))]
public class FilterViewModel
{
[BindAlias("someText")]
public string FilterParameter { get; set; }
}
In html:
#* at least you dont write "someText" here again *#
#Html.Editor(Html.AliasNameFor(model => model.FilterParameter))
#Html.ValidationMessage(Html.AliasNameFor(model => model.FilterParameter))
So I am leaving this answer here like this. This is even not an answer (and there is no answer for MVC 5) but who searching in google for same problem might find useful this experience.
And here is the github repo: https://github.com/yusufuzun/so-view-model-bind-20869735
working on a project right now and I'm relatively new to MVC, I have a model which looks something like
public class MyViewModel
{
public string check1 {get;set;}
public string check2 {get;set;}
}
I also have a class which I want to use to validate these checks using a custom regex, such as
public class WebCommon
{
public static bool Validation(string validate)
{
bool validatestatus = false;
Regex check = new Regex(my regex here);
if (check.IsMatch(validate))
{
validatestatus = true;
return validatestatus;
}
else
{
return validationstatus;
}
}
}
Basically what I'd like to do is call that method inside the webcommon class, inside the model itself to check when I take in the values for the get;sets in my model, and if it returns false, then I want it to throw an error within the view saying that it is not a valid input. Thanks!
This question here is about making custom regular expression attrbutes and should answer your question.
Check out either Regular Expression Attribute
Or implement and IValidatableObject
I know MVC4 uses NewtonSoft Json de/serialization. I was wondering how I can exclude a property on serialization to client, without using any of the data annotations like JsonIgnore/DataMemberIngore etc (the assembly is used elsewhere and can't be changed. Can I implement a custom formatter/JsonSerializerSettings/Dynamic ContractResolver etc for a specific object type and then filter out a specific property name?
Any help much appreciated.
Edit. Came up with the following as a first attempt. If anyone has a more elegant solution please let me know...
public class DynamicContractResolver : DefaultContractResolver
{
public DynamicContractResolver()
{
}
protected override IList<JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, Newtonsoft.Json.MemberSerialization.Fields);
if (type == typeof(SomeType))
{
var matchedProp = properties.Where(v=> v.PropertyName=="SomeProperty").FirstOrDefault();
if (matchedProp!=null)
{
properties.Remove(matchedProp);
}
}
return properties;
}
}
Plumbed into global.asax:
HttpConfiguration config = GlobalConfiguration.Configuration;
JsonSerializerSettings serializerSetting = new JsonSerializerSettings
{
ContractResolver = new DynamicContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
config.Formatters.JsonFormatter.SerializerSettings = serializerSetting;
Regards
Phil
This is solvable with a proper overriding
Override a Json function(s) that is in the MVC Controller class.
In this class you can customize the data. (with another mapping,
with reflection, ...)
Create a custom JsonResult class, just override the default one. You
are also able to override there the default serialization which is
going on there.
Now:
You can exclude this data when you already have a JSON
serialized string ... This would be the easiest option. Basically just string operations.
On the other hand you will also be able to call a custom NewtonSoft
serialization and you can do there whatever you want ... Even use custom serializations.
Forgive me if this has been asked before; I couldn't find anything close after a few searches:
I'm trying to write an ActionFilter in MVC that will "intercept" an IQueryable and nullify all the parent-child relationships at runtime. I'm doing this because Linq does not serialize objects properly if they have parent-child relationships (it throws a circular reference error because the parent refers to the child, which refers back to the parent and so on), and I need the object serialized to Json for an Ajax call. I have tried marking the child relationship in the DBML file with a privacy status of internal, and while this fixes the serialization problem, it also hides the child members from the view engine when the page renders, causing another error to be thrown. So, by fixing one problem, I cause another.
The only thing that fixes both problems is to manually set the child members to null just before returning the serialization, but I'm trying to avoid doing that because it's cumbersome, not reusable, etc. I'd rather use an ActionFilter to inspect the IQueryable that is being serialized and nullify any members with a Type of EntitySet (how Foreign Keys/Associations are represented). However, I don't have much experience with Reflection and can't find any examples that illustrate how to do something like this. So... is this possible with Reflection? Is there a better way to accomplish the same thing? I'll post the relevant code tomorrow when I'm back at my work computer.
Thanks,
Daniel
As promised, the code:
[GridAction]
public ActionResult _GetGrid()
{
IQueryable<Form> result = formRepository.GetAll();
foreach (Form f in result)
{
f.LineItems = null;
f.Notes = null;
}
return View(new GridModel<Form> { Data = result });
}
An added wrinkle is that I'm using the new Telerik MVC Extensions, so I'm not actually serializing the Json myself -- I'm just returning the IQueryable in an IGridModel, and the action filter [GridAction] does the rest.
So, just in case anyone's curious, here's how I finally solved this problem: I modified Damien Guard's T4 template to include the attribute [ScriptIgnore] above entities of type Association. This lets the JSON serializer know to not bother serializing these, thus preventing the circular reference problem I was getting. The generated code ends up looking like this:
private EntitySet<LineItem> _LineItems;
[ScriptIgnore]
[Association(Name=#"Form_LineItem", Storage=#"_LineItems", ThisKey=#"Id", OtherKey=#"FormId")]
public EntitySet<LineItem> LineItems
{
get {
return _LineItems;
}
set {
_LineItems.Assign(value);
}
}
This fixes the serialization problem I was having without disabling the use of child tables through LINQ. The grid action on the controller ends up looking like this:
[GridAction]
public ActionResult _GetGrid()
{
return View(new GridModel<Form> { Data = formRepository.GetAll() });
}
There are two options, one is to ignore those properties during serialization using [XmlIgnore]. The other one is to nullify the properties using reflection.
Ignore in serialization, simple usage sample that shows how to use default value in serialization:
[Serializable]
public class MyClass
{
[XmlIgnore]
public int IgnoredVal { get; set; }
public int Val { get; set; }
}
public void UsageSample()
{
var xmlSerializer = new XmlSerializer(typeof(MyClass));
var memoryStream = new MemoryStream();
var toSerialize = new MyClass { IgnoredVal = 1, Val = 2 };
xmlSerializer.Serialize(memoryStream, toSerialize);
memoryStream.Position = 0;
var deserialize = (MyClass)xmlSerializer.Deserialize(memoryStream);
Assert.AreEqual(0, deserialize.IgnoredVal);
Assert.AreEqual(2, deserialize.Val);
}
Nullify with reflection, code sample:
public void NullifyEntitySetProperties(object obj)
{
var entitySetProperties = obj.GetType().GetProperties()
.Where(property => property.PropertyType == typeof(EntitySet));
foreach (var property in entitySetProperties)
{
property.SetValue(obj, null, null);
}
}
In my opinion, if the first option can be done used in your code it's better. This option is more direct and economic.