Swashbuckle.webapi: Ignore read only/private setter properties for a POST/PATCH

Created on 28 Nov 2014  路  7Comments  路  Source: domaindrivendev/Swashbuckle.WebApi

Hi,

I have some models where the setter is private. For a GET request, these values will be returned to the user, but for a POST or PATCH, they should not be provided by the user at all, so I would like the documentation to reflect this.

Can you provide the ability to ignore these when documenting a POST/PATCH, or extend IModelFilter so that Apply takes an Operation parameter and then I can provide a custom implementation that hides properties as and when required.
This would be more consistent with the IOperationFilter Apply method also.

Thanks

Most helpful comment

Just wanted to ask if there're an updates on this since 2014?

All 7 comments

Hi there - see #112 for a previous answer to this issue

There is nothing in the swagger specification to support contextual descriptions for a schema (i.e. required fields per get/put/patch) and so the only way to accomplish this in a Swagger document is to provide completely different Schema's/Models for each case.

The easy way to do this with Swashbuckle is to just separate them into separate classes in your implementation - e.g. Product and ProductUpdate. In fact, I've found this to be a better practice in general rather than having a single class that serves multiple contexts/use-cases.

The other option, if you don't want to make the split in your code, is to do it at the spec generation level - I've never tried this but it should be possible with an IOperationFilter. Here, you would have to create two separate schemas and register them via the DataTypeRegistry, then you would have to update the Operation parameter/response to ref the relevant type that you just registered. Not trivial
but probably possible.

Hello m8,

Actually, looking here at SchemaObject:
https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject
It supports "readonly" properties:
_Relevant only for Schema "properties" definitions. Declares the property as "read only". This means that it MAY be sent as part of a response but MUST NOT be sent as part of the request. Properties marked as readOnly being true SHOULD NOT be in the required list of the defined schema. Default value is false._

This would seem to be exactly what I need in this case...so how do I get my properties tagged with this?

On a related(it) note, some of my objects are dynamic (users can add their own properties), so I need to customise the schema produced (regardless of whether it is a GET or a POST. I presume you are getting PropertyInfo's for the classes in order to generate the schema info - is there a way to hook into/replace the current mechanism if my schema comes from, say, a definition in a database somewhere? There is no way to get schema info for dynamic properties in .Net directly unfortunately :/

Lastly, I do think it would be useful if you add Operation to the IModelFilter Apply method signature to give us greater scope for customisation as we can do at the OperationFilter level.

thanks for your time in considering this

The current version of SB generates Swagger version 1.2 which does not include the readOnly property.

With that said, I'm currently working on the next major version (5.0) which makes the transition to Swagger 2.0. For this, I will certainly make sure the readOnly property is set for private/internal properties.

It's also worth noting that the Swagger 2.0 spec was only released a few months ago and the tooling is still playing catch up to support all of the new metadata. The swagger-ui only has alpha support at this point and I've just confirmed that it currently does not honor the readOnly flag.

Also, due to the dependency on it, SB 5.0 will be limited to alpha releases until a stable version of swagger-ui becomes available. If you're interested, you can pull down the swagger_2.0 branch (build via build.bat) and check it out. I'm just tying up lose ends before the (already overdue) alpha release so I'll keep you posted.

Regarding your question around dynamic schema properties ... as of now, IModelFilter is the best way I can think of to achieve this.

Finally, it does not make sense to include Operation in the Apply signature because Model definitions are not context aware. Many different operations may reference the same Model definition. In other words, there will be exactly one Model definition per complex System.Type. For example, in your case with the read-only fields, which operation (the GET, PUT or PATCH) would you expect to be passed to the Apply method for that Type?

Hi,

Thanks for the comprehensive answer. I didn't realise Swagger 2 was so new.
Do you know yet how we will need to decorate our models to feed through to the readOnly property?
There are a couple of ways I can think of off the top of my head:

  • having a private (or protected) setter
  • having no setter
  • having the data annotation [ReadOnly(true)]

I'll check out the swagger 2 branch for sure, thanks for pointing me to it.

re: including Operation in Apply - I though the model might be constructed per controller Action so the model could change per Method.

Just wanted to ask if there're an updates on this since 2014?

That was tricky to solve, but it works.

public class ReadOnlySchemaFilter : ISchemaFilter
{
    public void Apply(Schema model, SchemaFilterContext context)
    {
        if (model.Properties == null)
        {
            return;
        }

        foreach (var schemaProperty in model.Properties)
        {
            var property = context.SystemType.GetProperty(schemaProperty.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

            if (property != null)
            {
                var attr = (ReadOnlyAttribute)property.GetCustomAttributes(typeof(ReadOnlyAttribute), false).SingleOrDefault();
                if (attr != null && attr.IsReadOnly)
                {
                    // https://github.com/swagger-api/swagger-ui/issues/3445#issuecomment-339649576
                    if (schemaProperty.Value.Ref != null)
                    {
                        schemaProperty.Value.AllOf = new List<Schema>()
                        {
                            new Schema()
                            {
                                Ref = schemaProperty.Value.Ref
                            }
                        };
                        schemaProperty.Value.Ref = null;
                    }

                    schemaProperty.Value.ReadOnly = true;
                }
            }
        }
    }
}

Usage:

public class Order
{
    [ReadOnly(true)]
    public int Id { get; set; }

    [ReadOnly(true)]
    public Product Product { get; set; }
}

One disadvantage: Swagger UI will hide it from POST example, but unfortunately will show it in the model tab:

image

Hello,
This does not work if you have a JsonProperty attribute on your property, as the name in the swagger and the real property name are not in sync anymore.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

raaga123 picture raaga123  路  4Comments

kongres picture kongres  路  4Comments

niemyjski picture niemyjski  路  3Comments

qwertykeith picture qwertykeith  路  5Comments

thj-dk picture thj-dk  路  5Comments