When I use OData with C# Web API, I can hide any field easily by either using Ignore
method when creating EDM or using IgnoreDataMember
attribute. However, it doesn't hide these properties from Swagger UI. In this article, I share what I am doing to sync up OData model and Swagger UI.
You can skip to "Hide property from the swagger" if you don't care about OData.
Hide class property in OData with ASP.NET
There are several ways to hide the property in OData. Let's say I enabled OData to dotnet new Web API template (the WeatherForecast).
I modified WeatherForecast class to
- Add key
- Add secret property
- Update TemperatureF to have setter
using System;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace swaggertest
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF {
get
{
return 32 + (int)(TemperatureC / 0.5556);
}
set { }
}
[Key]
public string Summary { get; set; }
public string Secret { set; get; }
}
}
If I run the solution and query the data, I see the following.
I can hide the secret property by updating model builder.
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new();
builder.EntitySet<WeatherForecast>("WeatherForecasts");
builder.EntityType<WeatherForecast>().Ignore(x => x.Secret);
builder.EnableLowerCamelCase();
return builder.GetEdmModel();
}
I can also use IgnoreDataMember instead of using ignore when building model.
using System;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace swaggertest
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF {
get
{
return 32 + (int)(TemperatureC / 0.5556);
}
set { }
}
[Key]
public string Summary { get; set; }
[IgnoreDataMember]
public string Secret { set; get; }
}
}
Swagger doc
Even though I could control OData result and schema, it won't reflect swagger doc. So I still see the Secret property defined in WeatherForecast model in swagger UI.
Hide property from the swagger
I can use ISchemaFilter to control it. Add new class to the solution.
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace swaggertest
{
public class MySwaggerSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema?.Properties == null)
{
return;
}
var ignoreDataMemberProperties = context.Type.GetProperties()
.Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null);
foreach (var ignoreDataMemberProperty in ignoreDataMemberProperties)
{
var propertyToHide = schema.Properties.Keys
.SingleOrDefault(x => x.ToLower() == ignoreDataMemberProperty.Name.ToLower());
if (propertyToHide != null)
{
schema.Properties.Remove(propertyToHide);
}
}
}
}
}
Then specify the filter in startup.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "swaggertest", Version = "v1" });
c.SchemaFilter<MySwaggerSchemaFilter>();
});
services.AddOData();
}
Generated Swagger UI
How this works?
Each model is passed as OpenApiSchema argument to ISchemaFilter.Apply
method. I can obtain attributes which assigned to properties of the model by using GetCustomAttribute
method from Reflection namespace. So I look for properties which has "IgnoreDataMember" attribute (In this case, Secret property).
Then call schema.Properties.Remove
method to remove them.
SwaggerExclude attribute?
Some article or stackoverflow answers indicates to use SwaggerExclude
attribute to specify which properties to hide. I think it's good idea to define your own custom attribute for granular control. But I use IgnoreDataMember
attribute to decide which properties to hide. Of course you can write a bit more code in Apply
method to do more complex logic.
Summary
OData and Swagger are totally different technology which end up inconsistent results between document and behavior. I try to sync then as easy as possible, but if you know better way, I am happy to hear the solution!
Top comments (2)
Can you do this in DataAnnotations.Schema?
I haven't tried that yet. Do you have any idea?