-
Notifications
You must be signed in to change notification settings - Fork 0
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
[WIP] Improve middleware api #1
[WIP] Improve middleware api #1
Conversation
- #1626 - does not "provide an api to generate multiple / all documents"; could be layered on this work - otherwise, meets requirements listed in #1626 - assumes the following answers to open questions in #1626 (generally means everything is opt-in) - without an `AddSwagger()` call, `IDocumentProvider` service will not exist - registered document name / identifier is new `SwaggerSettings<>.DocumentName` property - document is not registered unless `DocumentName` is non-`null` i.e. `IDocumentProvider` won't know about it - `SwaggerSettings<>.MiddlewareBasePath` and `SwaggerRoute` are independent of `DocumentName` - middleware does not depend on the `IDocumentProvider` or registrar services; duplicating some code - many thanks to @rynowak for writing this proposal
…umentProvider` - remove some code from files added in 5b7e0a3b34 - revert changes made in 5b7e0a3b34 to existing files
- `internal` again - commit 4057107.
namespace NSwag.AspNetCore.Documents | ||
{ | ||
/// <summary>The document based on the <see cref="AspNetCoreToSwaggerGeneratorSettings"/> settings.</summary> | ||
public class AspNetCoreToSwaggerDocument : AspNetCoreToSwaggerGeneratorSettings, ISwaggerDocument |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine changing names around and moved a few classes myself. But GitHub doesn't seem to be showing anything as a rename, making it difficult to compare and contrast. Let me know what names you want and comparisons will be easier.
FYI my equivalent of this class includes DocumentName
and PostProcess
methods and leaves generation to the document provider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The final goal is to have a ISwaggerGenerator interface which is directly implemented by the generator class so this here can be completely avoided. Coming soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have mentioned: It's probably better for AspNetCoreToSwaggerDocument
and AspNetCoreToSwaggerGeneratorSettings
to have a "has a" rather than "is a" relationship. The depth of inheritance here is rather deep.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid more and more code, I removed AspNetCoreToSwaggerDocument and use ISwaggerGenerator which is directly implemented by AspNetCoreToSwaggerGenerator and then registered in the SwaggerDocumentRegistry… this way we the logic for correctly setting up the settings is where it belongs and we dont need all those new classes...
/// <summary>Adds services required for OpenAPI 3.0 generation (enforces OpenAPI 3.0 for all registered documents).</summary> | ||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param> | ||
/// <param name="configure">Configure the document registry.</param> | ||
public static IServiceCollection AddOpenApi(this IServiceCollection serviceCollection, Action<SwaggerDocumentRegistry> configure = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI Rather than expose the registry to users, I used builder methods.
|
||
foreach (var settings in registry.Documents.Select(t => t.Value).OfType<AspNetCoreToSwaggerDocument>()) | ||
{ | ||
settings.SchemaType = SchemaType.OpenApi3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea but worry about overriding choices users make in their configure
delegate. Would prefer the property was of type Nullable<SchemaType>
and NJsonSchema defaulted to Swagger2
when it's null
. Then, could make this change conditional on SchemaType == null
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another alternative when using my builder approach (AddDocument(...)
on the IOpenApiBuilder
(or ISwaggerBuilder
sticking with "Swagger" more as you suggested) would be to use a AspNetCoreToSwaggerDocument
subclass that sets SchemaType
in its constructor. The AddOpenApi(...)
method would then take an Action<AspNetCoreToOpenApiDocument>
parameter.
…s(...)` - add NSwag.Sample.OpenApi project - rework bd9c7fc; revert part of that commit e.g. undo `SwaggerExtensions` changes nit: - correct doc comment for `AspNetCoreToSwaggerGeneratorSettings` WIP: - some doc comments are currently empty - names are tentative
2da52e8
to
a13c550
Compare
/// <param name="documentName">The document name.</param> | ||
/// <param name="configure">The configure action.</param> | ||
/// <returns>The registry.</returns> | ||
public SwaggerDocumentRegistry AddDocument<TDocument>(string documentName, Action<TDocument> configure = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the use case for additional IDocument
implementations?
{ | ||
var settings = new TDocument(); | ||
configure?.Invoke(settings); | ||
_documents[documentName] = settings; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is brand new code. I thought you wanted to require unique document names?
namespace NSwag.AspNetCore.Documents | ||
{ | ||
/// <summary>The document based on the <see cref="AspNetCoreToSwaggerGeneratorSettings"/> settings.</summary> | ||
public class AspNetCoreToSwaggerDocument : AspNetCoreToSwaggerGeneratorSettings, ISwaggerDocument |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have mentioned: It's probably better for AspNetCoreToSwaggerDocument
and AspNetCoreToSwaggerGeneratorSettings
to have a "has a" rather than "is a" relationship. The depth of inheritance here is rather deep.
|
||
foreach (var settings in registry.Documents.Select(t => t.Value).OfType<AspNetCoreToSwaggerDocument>()) | ||
{ | ||
settings.SchemaType = SchemaType.OpenApi3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another alternative when using my builder approach (AddDocument(...)
on the IOpenApiBuilder
(or ISwaggerBuilder
sticking with "Swagger" more as you suggested) would be to use a AspNetCoreToSwaggerDocument
subclass that sets SchemaType
in its constructor. The AddOpenApi(...)
method would then take an Action<AspNetCoreToOpenApiDocument>
parameter.
/// <remarks>This is the same as UseSwagger(), configure the document in AddSwagger/AddOpenApi().</remarks> | ||
/// <param name="app">The app.</param> | ||
/// <param name="configure">Configure additional settings.</param> | ||
public static IApplicationBuilder UseOpenApi(this IApplicationBuilder app, Action<SwaggerMiddlewareSettings> configure = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an opportunity here to reduce the depth of the middleware pipeline when users configure multiple documents. No longer necessary to have a middleware per document.
Further, the user now needs to be concerned about making both SwaggerRoute
and documentName
unique. Routing avoids that.
/// <remarks>This is the same as UseOpenApi(), configure the document in AddSwagger/AddOpenApi().</remarks> | ||
/// <param name="app">The app.</param> | ||
/// <param name="configure">Configure additional settings.</param> | ||
public static IApplicationBuilder UseSwagger(this IApplicationBuilder app, Action<SwaggerMiddlewareSettings> configure = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest picking one method name for the Use...(...)
methods. The UseSwagger(...)
and UseOpenApi(...)
methods do exactly the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep using only UseSwagger/AddSwagger and maybe ill rename the whole project from Swagger to OpenApi at some point.. otherwise it gets really confusing as some methods do the exact same thing with different names... for me Swagger is v2 and OpenAPI is v3 but this is not really true - so let's stick to only swagger (as done in this PR).
}); | ||
|
||
return app; | ||
throw new NotSupportedException("Use " + nameof(AddSwagger) + "(), " + nameof(UseSwaggerWithApiExplorer) + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Obsolete]
is intended for the case where a method or class continues to work as it did before. That is, it allows users to upgrade their dependencies and temporarily ignore the warnings. This breaks users and is equivalent to removing the existing method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to open a PR in RSuter/NSwag with this branch to make it easier to compare and contrast. I'll also remove the new sample from the other PR to reduce the clutter.
|
||
app.UseSwaggerUi(typeof(Startup).GetTypeInfo().Assembly, s => | ||
app.UseSwaggerUi3(options => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ui3 handles multiple documents and it seems this ignores that feature while increasing the chances UseSwagger(...)
and UseSwaggerUi3(...)
are misaligned.
@dougbu sorry for all this mess. I've updated the description of this PR with more information. I'd really like to merge this PR into your PR and then maybe work from there if needed - but i think all scenarios are covered in the most clean and extensible way. |
Could you please open the PR I suggested in your repo to make it easier to compare and contrast? It's very difficult to see what remains of the original proposals here.
That is a significant improvement.
My main concerns here are the ease with which users can misconfigure their middleware and the depth of the middleware pipeline when users have multiple documents. A smaller concern is that middleware are conventionally configured using |
Can you post a sample of the advanced config how it would look like with your solution? One ugly thing which remains here (for now) is the configuration of the UIs, i.e. they use a config of SwaggerUiSettings but they should use SwaggerUiSettings. But because the former is needed in the legacy library and WebApiToSwaggerGeneratorSettings/GeneratorSettings must just be ignored I think it's fine for now... will remove the Web API based generator eventually and all this too :-) |
Created a new PR: RicoSuter#1678 |
From RicoSuter#1658 discussions services.AddSwagger();
services.Configure<SwaggerMiddlewareOptions>(o => o.SwaggerRoute = "/openApi/{documentName}/swagger.json");
services.Configure<SwaggerUi3Options>(o => o.SwaggerUiRoute = "/myUi"); |
bbf9e37
to
2f71924
Compare
Turning off the duplicate noise coming from this PR. RicoSuter#1678 is the place. |
Merge into PR RicoSuter#1658
IDocumentProvider
service and its implementation RicoSuter/NSwag#1658Question:
Simple config:
Advanced config: