I am trying to hide some API's and maybe even some fields from documentation.
I can't figure out how to hide them except using the Obsolete Attribute but this make the API invalid as well.
The swagger-UI supports it it seems: https://groups.google.com/forum/#!topic/swagger-swaggersocket/LgrU5ecwcoE
How can I support this in WEB API?
Thanks!
This is a duplicate of #153.
Re individual fields, if you don't want to document them then I assume they're only present on the class for internal use? If this is the case, just mark them internal instead of public and Swashbuckle will ignore them.
If you really do need them to be public but don't want to document them, there are other options I can share. First though, I'd like to understand your use-case a little better - the only valid reason I can think of for exposing a property in your API but hiding it in your API docs would be if it's obsolete.
Than you so much. I tried to find this my self but I was unable to.
As for fields... the issue is pretty crazy, but I have some fields that are only used for official APP's and would rather not display them to end users so they don't get confused.
I'll figure something out ;)
Thanks again!!
Hi domaindrivendev,
You say above you can't think of a use case for not displaying swagger info for an API. I have a site that has web services that are for customer use and another web service that responds to requests from my billing service. Although this other service is protected in various ways so that it can't be spoofed I don't want to document it - it encourages people to play and distracts from the goodies.
Andy
An example would be an Id property that you populate internally to facilitate in update or an insert - that you don't want provided by the client.
I want to have the Id on my model, but not in the documentation. I can't mark it as internal as the Id is used by other things (queries and the like). It's not obsolete at all - I just want to hide it
@wooderz Look into document filters, I have a few examples here:
https://github.com/heldersepu/SwashbuckleTest/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L323
you can do exactly that with one of those
@heldersepu I'm not sure that will help? It'll be for all models etc then? We want it only for certain models... would be great to be able to write an attribute that swashbuckle will then use to hide or not a property
@wooderz Please provide a project with the models and I will help you out.
I'm sure there is a pattern we can identify...
@heldersepu something along the lines of
public async Task<IHttpActionResult> Put(NewUserWriteModel newUser)
{
newUser.Id = Guid.NewGuid();
await _messageBusSender.PostMessage<NewUserWriteModel>(newUser);
return Ok();
}
With NewUserWriteModel being
public class NewUserWriteModel
{
[SomeAttributeHereToOnlyIgnoreInSwagger]
public Guid Id { get; set; }
public string Email {get; set; }
}
We can't use [JsonIgnore] because when it's serialised through MassTransit we lose the Id.
That is just one model, if that is all your document filter will be very simple!
That's one example. We have 100s of similar models we'd like to do the same to. Hence an attribute would be really useful
@wooderz Similar models... you mean all with public Guid Id that all need to hide? that is easy
@wooderz Here is an option using StringLengthAttribute:
```c#
using System;
using System.ComponentModel.DataAnnotations;
namespace Swagger_Test.Models
{
public class NewUserWriteModel
{
[StringLength(9999)]
public Guid Id { get; set; }
public string Email { get; set; }
public string Name { get; set; }
}
}
and here is the docFilter:
```c#
private class HideStuffDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
foreach (var definition in swaggerDoc.definitions)
{
foreach (var prop in definition.Value.properties.ToList())
{
if (prop.Value.maxLength == 9999)
definition.Value.properties.Remove(prop);
}
}
}
}
Look here for a few more attributes you can use in a similar way:
https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/Swagger/SchemaExtensions.cs#L17
Here is a cleaner solution that I wrote for the AspNetCore Swashbuckle:
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var definition in swaggerDoc.Definitions)
{
var type = Type.GetType("**NameSpace**." + definition.Key + ",**AssemblyName**");
if (type != null)
{
var properties = type.GetProperties();
foreach (var prop in properties)
{
var ignoreAttribute = prop.GetCustomAttribute(typeof(SwaggerIgnoreAttribute), false);
if (ignoreAttribute != null)
definition.Value.Properties.Remove(prop.Name);
}
}
}
}
You'll need to replace the Namespace and the AssemblyName with your namespace and dll name, as well as create a custom attribute:
public class SwaggerIgnoreAttribute : Attribute { }
Decorate the member you wish to hide with the attribute and you are good to go.
It's worth noting @mbprocede implementation uses reflection and the calls:
Type.GetType("**NameSpace**." + definition.Key + ",**AssemblyName**");
type.GetProperties();
prop.GetCustomAttribute(typeof(SwaggerIgnoreAttribute), false);
That could be very time consuming if your project is large.
If your project is large and swagger receives a lot of traffic consider using some caching
I created my filter in the same way @heldersepu did, but with a better name for my purposes:
public class HideStuffDocumentFilter : IDocumentFilter {
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) {
foreach (var definition in swaggerDoc.Definitions) {
foreach (var prop in definition.Value.Properties.ToList()) {
if (prop.Value.MaxLength == int.MaxValue) {
definition.Value.Properties.Remove(prop);
}
}
}
}
}
and my attribute (here is the trick, extend the attribute):
public class ApiIgnoreAttribute : MaxLengthAttribute {
public ApiIgnoreAttribute() : base(int.MaxValue) {
}
}
Then just decorate your models:
public class BillPost {
[ApiIgnore]
public string SdpId { get; set; }
[ApiIgnore]
public string Provider { get; set; }
...
}
It works just fine and no reflection :)
@andrecarlucci Does this work for asp.net core? Tried, but unsuccessful.
This are the error. System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Array'.
Based on the answer from @mbprocede (Thank you so much!)
I have slightly improved it so that the namespace does not have to be known. This solution works in .NET framework 4.7.1. It will most likely work in other versions of .NET too as well as .NET Core.
public class SwaggerDocumentCustomIgnoreFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
List<Type> AllTypes = Assembly.GetAssembly(typeof(SwaggerDocumentCustomIgnoreFilter)).GetTypes().ToList();
foreach (var definition in swaggerDoc.definitions)
{
var type = AllTypes.FirstOrDefault(x => x.Name == definition.Key);
if (type != null)
{
var properties = type.GetProperties();
foreach (var prop in properties)
{
var ignoreAttribute = prop.GetCustomAttribute(typeof(SwaggerIgnoreAttribute), false);
if (ignoreAttribute != null)
{
definition.Value.properties = definition.Value.properties
.Where(x => !string.IsNullOrWhiteSpace(x.Key) && !x.Key.Equals(prop.Name, StringComparison.OrdinalIgnoreCase))
.ToDictionary(x => x.Key, x => x.Value);
}
}
}
}
}
}
```
@JasonLoKiSmith
I'm trying to remove, but its not removing from the list. Any clue?
@JasonLoKiSmith inspired me to write a slightly different solution based on my needs.
This was written in .NET Core 2.2, but I can also see this working in .NET Framework with minimal modification.
public sealed class SwaggerDocumentCustomIgnoreFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
List<Type> allTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.ToList();
List<Type> allTypesWithAttribute = allTypes
.Where(t => t.GetCustomAttributes(true).OfType<SwaggerDocumentCustomIgnoreAttribute>().Any())
.ToList();
foreach (Type type in allTypesWithAttribute)
{
swaggerDoc.Definitions.Remove(type.Name);
}
}
}
Most helpful comment
I created my filter in the same way @heldersepu did, but with a better name for my purposes:
and my attribute (here is the trick, extend the attribute):
Then just decorate your models:
It works just fine and no reflection :)