CT14.IT
CT14.IT

Reputation: 1747

Swashbuckle EF Core hide properties for post/put documentation

Summary

I would like to hide properties from the generated documentation model for PUT/POST requests.

More Detail

I would like to create a nicely documented API for a system I'm working on. I would like to use Swashbuckle/Swagger to automatically generate the documentation. I am using Entity Framework to define the relationship between objects in the system.

Here is an example relationship between objects.

User.cs

public class User
{
    public int Id { get; set; }
    public string ExternalReference { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts { get; }
}

Post.cs

public class Post
{
    public int Id { get; set; }
    public string ExternalReference { get; set; }
    public string Content { get; set; }
    public int UserId { get; set; }

    public User User { get; set; }

}

The following example value is generated for my GET /api/posts/{id} endpoint.

GET /api/posts/{id}

{
  "id": 0,
  "externalReference": "string",
  "content": "string",
  "userId": 0,
  "user": {
    "id": 0,
    "externalReference": "string",
    "name": "string",
    "posts": [
      null
    ]
  }
}

This is what I would like to see, it's relevant to potentially return the User object as well.

The following is the example value generated for my POST /api/posts endpoint

POST /api/posts

{
  "id": 0,
  "externalReference": "string",
  "content": "string",
  "userId": 0,
  "user": {
    "id": 0,
    "externalReference": "string",
    "name": "string"
  }
}

In my mind at least I feel the user section of the example isn't relevant for POST or PUT only the userId property is. The generated example value isn't too bad in this simple example, but if I start having objects with multiple relationships I feel it can get messy.

The question again

Is there an elegant way of supressing relational objects from the generated swagger documentation ONLY for PUT/POST methods?

Upvotes: 0

Views: 953

Answers (1)

Rena
Rena

Reputation: 36715

You could custom a OperationFilter like below:

public class CustomOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.OperationId == "Posts_post" || operation.OperationId == "Posts_put")
        {
            operation.RequestBody = new OpenApiRequestBody()
            {
                Content = new Dictionary<string, OpenApiMediaType> {
                {"application/json",
                    new OpenApiMediaType()
                    {

                        Schema = new OpenApiSchema(){
                            Example = new OpenApiObject
                                {
                                    ["ID"] = new OpenApiInteger(0),
                                    ["UserId"] = new OpenApiInteger(0),
                                    ["ExternalReference"] = new OpenApiString("string"),
                                    ["Content"] = new OpenApiString("string")
                                }
                        }
                    }
                }
            }
            };
        }
        else
        {
            return;
        }
       
    }
}

Add the name on HttpVerb attribute:

[HttpPut("{id}",Name = "Posts_put")]
public async Task<IActionResult> PutPost(int id, Post post)
{....}

[HttpPost(Name ="Posts_post")]
public async Task<ActionResult<Post>> PostPost(Post post)
{...}

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
        c.OperationFilter<CustomOperationFilter>();   //add this...
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    });

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Result:

enter image description here

Upvotes: 2

Related Questions