Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit the depth of properties documented by Swashbuckle for [ProducesResponseType] annotations #615

Closed
statler opened this issue Feb 27, 2018 · 5 comments

Comments

@statler
Copy link

statler commented Feb 27, 2018

I put up a stackoverflow question with no luck, so hoping someone can point me in the right direction :)
https://stackoverflow.com/questions/48987127/limit-the-depth-of-properties-documented-by-swashbuckle-for-producesresponsetyp

I am annotating like this;

// DELETE: api/MyType
    /// <summary>
    /// Deletes a MyType with specified ID
    /// </summary>
    /// <param name="key">The ID of the MyType to delete</param>
    /// <returns>The data for the deleted MyType</returns>
    [HttpDelete]
    [ProducesResponseType(typeof(MyType), 200)]

Which is fine, but MyType has many reference properties which in turn have referenced properties etc. This results in a massive document like this (but WAY bigger)

  "IsAvlOverride": true,
  "Notes": "string",
  "DateRejected": "2018-02-26T21:53:26.127Z",
  "GeometryType": 0,
  "PrimaryTagId": 0,
  "OptimisticLockField": 0,
  "Atplot": [
    {
      "UniqueId": "string",
      "Hrid": "string",
      "CreatedBy": 0,
      "CreatedOn": "2018-02-26T21:53:26.127Z",
      "ModifiedBy": 0,
      "ModifiedOn": "2018-02-26T21:53:26.127Z",
      "AtpLotId": 0,
      "AtpId": 0,
      "LotId": 0,
      "ItemInspect": "string",
      "DateInspect": "2018-02-26T21:53:26.127Z",
      "TimeInspect": "2018-02-26T21:53:26.127Z",
      "DateApproved": "2018-02-26T21:53:26.127Z",
      "ApprovedById": 0,
      "ApprovalComments": "string",
      "OptimisticLockField": 0,
      "Approval": [
        {
          "UniqueId": "string",
          "Hrid": "string",
          "CreatedBy": 0,
          "CreatedOn": "2018-02-26T21:53:26.127Z",
          "ModifiedBy": 0,
          "ModifiedOn": "2018-02-26T21:53:26.127Z",
          "ApprovalId": 0,
          "ApprovalItemTypeId": 0,
          "ApprovalSubtypeId": 0,
          "RequestToId": 0,
          "RequestById": 0,
          "RequestText": "string",
          "RequestDate": "2018-02-26T21:53:26.127Z",
          "RequiredDate": "2018-02-26T21:53:26.127Z",
          "EmailDate": "2018-02-26T21:53:26.127Z",
          "ResponseDate": "2018-02-26T21:53:26.127Z",
          "ResponseText": "string",
          "ApprovalStatusTypeId": 0,
          "ProjectId": 0,
          "CloseOutDate": "2018-02-26T21:53:26.127Z",
          etc...

How can I create a operation/schema/document filter that limits the depth of recursion to just the top level, and just uses the type name for reference properties?

e.g.

  "IsAvlOverride": true,
  "Notes": "string",
  "DateRejected": "2018-02-26T21:53:26.127Z",
  "GeometryType": 0,
  "PrimaryTagId": 0,
  "OptimisticLockField": 0,
  "Atplot": [Atplot_object]
@domaindrivendev
Copy link
Owner

Are you referring to the Swagger JSON document, or the example/model that's displayed in the swagger-ui?

If it's the latter, you have some options. Your example isn't even valid JSON, so that's clearly not possible. But, you can switch to "Model" mode, which shows a model description instead of an example, and you can also limit the expand depth for that model description:

app.UseSwaggerUI(c =>
{
    c.DefaultModelRendering(ModelRendering.Model);
    c.DefaultModelExpandDepth(1); 
});

@statler
Copy link
Author

statler commented Feb 27, 2018

Ah sorry for no being specific - the output was just a copy of the data shown in the swagger UI as the output for the method - not the JSON itself.

Although your suggestion makes the UI much better and is a definite improvement, I want to achieve the same result but without including the objects in the JSON itself. The JSON is already 160kb and will get much bigger - it is a really large schema. I am finding that the UI can take a while to update for methods with large output models

Example JSON at https://pastebin.com/VLr5guYX

@domaindrivendev
Copy link
Owner

domaindrivendev commented Feb 27, 2018

By design, Swashbuckle treats the code as truth! So, it documents the entire API surface area, including all serializable properties on models. If your API surface area is too complex to document, then I might contend that it's going to be difficult to use, and might encourage a simpler design.

Of course it's not my place to dictate how you design your API but I will say that SB's fundamental goal is to automatically document API's based on the surface area that can be inferred from code - routes, controllers, actions methods and models. If you find you need to start fighting this, then it may not be the right tool for your specific use-case.

With all that said, a final suggestion would be to wire up a DocumentFilter (see readme) that clears the entire Definitions dictionary from the Swagger document before it's returned. I believe this will cause only the model names to be displayed, in other words a depth of 0. You could smarten up the implementation further to only remove definitions that are displayed at a depth of 1 or 2 or beyond but that would require some complex traversing through the rest of the doc to figure that out.

@ilya-chumakov
Copy link

ilya-chumakov commented Apr 1, 2020

@domaindrivendev
Is it possible to limit the depth of generated examples now, or at least disable examples (for a particular endpoint maybe)?
I have a problem with swagger for an OData endpoint. The controller's action returns IQueryable, so it would be nice to use the real entity in [ProducesResponseType] to avoid code duplication.
OData allows limiting the expanding of nested collections, it's 2 by default. So the whole $expand feature is pretty secure. The only problem is to document it properly in Swagger: the generated example describes the whole database because of navigation properties in the entity, making the entire JSON cumbersome and almost unusable.

@MasoudShah
Copy link

The only workaround I came up with, is to filter the virtual properties of my db models (witch defines my db relations), or remove their body with a simple string. This is how it can be done:

public class MySwaggerFilter : ISchemaFilter
{
	public void Apply(OpenApiSchema schema, SchemaFilterContext context)
	{
		if (schema?.Properties == null)
		{
			return;
		}
		var dic = new Dictionary<string, OpenApiSchema>(StringComparer.InvariantCultureIgnoreCase);
		foreach (var schemaProperty in schema.Properties)
		{
			dic.Add(schemaProperty.Key, schemaProperty.Value);
		}
		var excludedProperties = context.Type.GetProperties().Where(t => t.GetGetMethod().IsVirtual);
		foreach (PropertyInfo excludedProperty in excludedProperties)
		{
			if (dic.ContainsKey(excludedProperty.Name))
			{
				var name = dic.FirstOrDefault(c => c.Key.Equals(excludedProperty.Name, StringComparison.OrdinalIgnoreCase)).Key;
				schema.Properties.Remove(name);
				schema.Properties.Add(name, new OpenApiSchema());
			}
		}
	}
}

It is nice to have the virtual body for 1 depth level, or at least define them like empty arrays or objects, but this kind of filtering can only replace them with a simple string. Finally you have to add your schema filter in configuration like this:

builder.Services.AddSwaggerGen(options =>
{
    ...
    ...

    options.SchemaFilter<MySwaggerFilter>();
}).AddSwaggerGenNewtonsoftSupport();

I would be glad if I see any edition to filter, to have a better result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants