DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

C# ASP.NET : Hide model properties from Swagger doc

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

If I run the solution and query the data, I see the following.

Image description

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();
}
Enter fullscreen mode Exit fullscreen mode

Image description

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Image description

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);
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

Generated Swagger UI

Image description

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!

Discussion (0)