domaindrivendev / Swashbuckle.WebApi

Seamlessly adds a swagger to WebApi projects!
BSD 3-Clause "New" or "Revised" License
3.07k stars 678 forks source link

Anonymous types response model support #1180

Open Bemjamin opened 7 years ago

Bemjamin commented 7 years ago

Hello,

Is there support to show the anonymous types response model? In my controller I am returning these anonymous to avoid the exposure of certain properties and the creation of DTO's, exemple:

var result = from r in rooms select new { roomId = r.Id, roomCapacity = r.Capacity, roomPrice = r.Price }; return Ok(new { availabilities = result });

So, must I always return typed objects to have the documentation?

Thank you.

heldersepu commented 7 years ago

Take a look at SwaggerResponse, that might be what you need: https://github.com/heldersepu/SwashbuckleTest/blob/master/Swagger_Test/Controllers/IHttpActionResultController.cs#L24

Bemjamin commented 7 years ago

That's my problem, I don't have a typed object as return. I am always returning anonymous and SwaggerResponse requires a typed object.

The thing is, I received a requirement where I must to return the same object with different property names. Instead of creating another class (DTO), I just returned an anonymous one with the required properties but this doesn't help. So imagine that you have the following class into your project:

public class Home { public int Id, public int Name }

And you receive a requirement that you must return Home objects with the following properties:

{ homeId: "", homeFullName: ""}.

I dont want to change my properties or create a DTO to answer specifically to this requirement. I would like to be able to create a response type model showing this "new type" return.

heldersepu commented 7 years ago

You can create spaghetti code or proper code the choice is yours. The way I see it: you don't want to have a typed object as return

Bemjamin commented 7 years ago

Exactly, so that's why I am asking: Is this scenario supported or no?

Bemjamin commented 7 years ago

I want to be able to show a response exemple without a type. There is no naming involved:

Response Class (Status 200) OK

Model Example Value

[ { "homeId": 0, "homeFullName": 0, } ]

heldersepu commented 7 years ago

You can hardcode the example with an IDocumentFilter. that allows you to modify the resulting json document to. But that is a bad idea as you will need to maintain code on two different locations

Bemjamin commented 7 years ago

I know this is an horrible idea, but don't worry, I am working on something dead. Thank you, I will give it a try.

Rwing commented 6 years ago

@Bemjamin good job! I had same problem now, is there any progress?

mobsoftware commented 6 years ago

I agree that anonymous types should be handled and swashbuckle should generate documentation from them as they are ultimately compiled into classes within the IL

Mmatiasn commented 5 years ago

You can hardcode the example with an IDocumentFilter. that allows you to modify the resulting json document to. But that is a bad idea as you will need to maintain code on two different locations

I'm thinking of doing the following myself. But if you still think it's a bad approach, I would like to know from you.

I'm thinking I could iterate over the anonymous objects and group together any identical ones. [ProducesResponseType(new{Text = "", Id=0}, 200)]

Then in the IDocumentFilter map anonymous objects to documentation?

public class AnonDescriptionsDocumentFilter : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    {
        // Add logic to give matching anonymous classes some global documentation
        // var anon1 = new{Text = "", Id=0};
        // var anon2 = new{Text = "", Id=0};
        // if ( anon1 ~= annon2) {
        //     assign anon documentation for this anon object.
        // }
        // Else have a global anonymous names with hash id of some kid. So developers are aware of 
        // everything potentially returned by their application
    }
}

This is semi sudo code. It wouldn't work exactly like this but I hope the core concept is understood.

MGhandour92 commented 5 years ago

Any Progress ?

Laumania commented 4 years ago

Seems like no progress?

I totally get you @Bemjamin and don't understand why @heldersepu sees this a spagetticode or bad practice.

The reason I want anonymous types as the result of my REST API, is that it's much harder for me or others to break.

If we have "DTO" class, which I guess most have, the problem is that that class is indirectly a part of your API interface. Remember, the consumer of the API is getting JSON - not a class. They might map it to a matching class on their end, but they don't get a class/object with a class name returned.

So, the way you can easially break your API if you are not very careful is when you want to rename properties on your "dto" class. This can happen with anonymous types too, but you are rigth there in the API method once you do the rename, so there should be a bell ringing :)

Renaming a property of a DTO, might not be that obvious, as the dto might be in a different file, different library or maybe it's in the controller itself (which is at least better as it's closer). Then you rename a property on your dto, by VS magic and bam! "Name" is now "Firstname". You don't see a problem, because your test, if you have that, might rely on the same dto, so that knows the renaming too...expect when you deploy it, all your consumers, that only see the actually JSON, not have a broken API, as they dont have the expected "Name" property.

I know, there are other ways and multiple ways to avoid this - however - what I'm trying to get at is that there is a valid argument for returning anonymous types in a REST API :)

However, I am here because this is still not supported as I can see, so have to fo it some other way :)

crclz commented 4 years ago

The Reflection API cannot go into a method's body. ( https://stackoverflow.com/questions/2693881/can-i-use-reflection-to-inspect-the-code-in-a-method )

So, when the code is return new{a=1,b=2}, Reflection API cannot get the structure of the anonymous type.

What is able to do this is Roslyn API. Analyzers (e.g. FxCop) use RoslynAPI, and VisualStudio refactor functions also use RoslynAPI.

Fortunately VisualStudio provides a way to convert anonymous type to class. Just press CTRL+. (or right-click and choose the first item) and follow the instructions, vs will generate a class for you. If your anonymous type is nested, like new{a=1,b=new{c=2}}, you shold first convert the inner type to a class, then the outter class. Is you want to add some properites in the future, you can modify the generated class. The converting function of VS is just like a scaffold function.

By the way, I think explicit type is good, its act as a document. If you are using https://github.com/OpenAPITools/openapi-generator to generate front end client code, the front end developers can benifit a lot from it. Also the back end developer will spend less time communicating with the front end developer.

thiagofidelis commented 3 years ago

putting

[ProducesResponseType(typeof(object),StatusCodes.Status200OK)]

works for me

azeebazy commented 6 months ago

Is this still not supported?