I'm upgrading an ASP.NET Core API project from v5 to v6.
Service config in v5:
services.AddSwaggerGen();
Service config in v6:
builder.Services.AddEndpointsApiExplorer(); // what is this?
builder.Services.AddSwaggerGen();
What is AddEndpointsApiExplorer? Everything works as expected whether I add it or not.
I use the "ASP.NET API Versioning" library. Are they related? If so, must I use both, just the library, or is the library unnecessary now?
AddEndpointsApiExplorer is for Minimal APIs whereas AddApiExplorer requires, at least, MVC Core. For API projects, AddControllers calls AddApiExplorer on your behalf.
But Why Does Everything Still Work With AddEndpointsApiExplorer?
With the introduction of Endpoint Routing, everything in the routing system boils down to an Endpoint. ASP.NET Core uses the Application Model, namely ApplicationModel, ControllerModel, and ActionModel, to create Endpoint instances and register them with the routing system. Minimal APIs, however, use a builder to directly create and register individual Endpoint instances.
The default API Explorer implementation provides a IApiDescriptionProvider that builds ApiDescription instances from the Application Model. Minimal APIs do not have an Application Model so there is nothing to build ApiDescription instances from. The API Explorer provides these descriptions, which are commonly used by tools such as OpenAPI generators. Without any descriptions, there would be no support for Minimal APIs and OpenAPI; that would be bad (or, at least, certainly not accepted by developers). To address that, the ASP.NET Core team created a second IApiDescriptionProvider that only considers Endpoint.
If Everything is an Endpoint, Why Not Merge Implementations?
There's two parts to this answer. First, changing the original IApiDescriptionProvider implementation would introduce a public, breaking change. At a minimum, new constructor arguments would be required. Since it was a major version bump, this approach wasn't off the table, but it turns out to be irrelevant. The bigger issue is that the original IApiDescriptionProvider implementation and AddApiExplorer live in and depend on MVC Core. Minimal APIs only require the routing abstractions. There is no way to merge the two without adding unnecessary coupling. To address this problem, AddEndpointsApiExplorer was added which adds an implementation that only requires an IApiDescriptionProvider implementation based on bare bones Endpoint definitions from the routing system.
If AddEndpointsApiExplorer exists and I call it, do I even need AddApiExplorer anymore? Maybe. The metadata exposed and available on Minimal API Endpoint instances is much lighter than the Application Model; after all, they are minimal. Behind the scenes, a IApiDescriptionGroupCollectionProvider implementation takes a sequence of IApiDescriptionProvider instances. If AddEndpointsApiExplorer and AddApiExplorer are called, then both providers will execute. If only AddEndpointsApiExplorer is called, it will work with regular 'ol controllers, but the descriptions' information fidelity might be less than what you are accustomed to. If you are only authoring Minimal APIs, then AddEndpointsApiExplorer is required if you want API Explorer support.
The fidelity between the two methods is improving even more in .NET 7.0. In some future release, it's possible we might see these approaches coalesce into one.
TLDR; .AddEndpointsApiExplorer() was created to support Minimal Api's.
Searching the doc's via google shows a number of pages that include a call to .AddEndpointsApiExplorer(). But no mention of why you need it, or if it is required when migrating from a v5 project. The documentation is definitely lacking.
Working backwards from the source code & git blame, I found the related project. So the answer appears to be related to support for Minimal Api's.
I believe some new services were created to extract return type information from these new minimal api's, in a way that might apply in a more general way when using Endpoint Routing without MVC.
If you are using MVC, perhaps via .AddControllers(), .AddApiExplorer() would be called for you. Providing the services that swagger depends on for describing controller actions. If that's all you need, then this new api call doesn't seem to be required.
While the documentation for using swagger with minimal api's includes a call to .AddEndpointsApiExplorer(). Even that doesn't explain exactly why it is required.
Why does .AddEndpointsApiExplorer() exist at all? Why were the new features excluded from .AddApiExplorer()? Why was this method rename left out of other documentation for v6?
Perhaps we should create an issue on https://github.com/dotnet/aspnetcore/ or https://github.com/dotnet/AspNetCore.Docs/ to ask for clarification so that others don't have to ask these questions.
TL;DR
Only use AddEndpointsApiExplorer if you use v6's "minimal APIs", which look like this:
app.MapGet("/", () => "Hello World!");
Related
In our microservices architecture there are several APIs that are part of shared library included with each service. We want these APIs (ex. /cache/delete) to appear in all services. However, what we cannot figure out is how to dynamically assign the URL route. So what we need is the URL route base (specific to the service) would be prefixed to the resource/action. For example:
https://example.com/api/service1/cache/delete
https://example.com/api/service2/cache/delete
In C# v10 interpolated constant strings were introduced, that would allow something like [Route($"{prefix}/cache/delete)]. But we will not be on this version for a while.
I was wondering if there is another possible implementation for this.
It sounds like you have each service do its own route prefix. I'm not typically a fan of that as I just have other things do that, like the API gateway or use some sort of service discovery tool (Consul, service discovery in Kubernetes, etc.).
If you did this, you'd be able to just map to /cache and it would make life easy on both ends (the service and the library).
If you want to keep the same pattern you're currently doing, you can tap into IEndpointRouteBuilder. Each service can then use the default you set (/cache), or define their own endpoint. That's where the service can insert their own prefix and you take the responsibility off of the library for making sure it has the right endpoint.
Trying to do a catch-all generic library that deals with something like that routing, which is specific to each service is asking for trouble. You would be forcing yourself into the same routing pattern for every service you ever make that uses caching even though you may want something else. Allow the route to be flexible and managed by the service rather than coupling the two together like that.
I have an ASP.NET Core 3.1 API and I am introducing a new version for one of my controllers.
I am using the Microsoft.AspNetCore.Mvc.Versioning NuGet package and I have set the new version to be the default version.
All my other controllers should work with both the old version (1.0) and the new version (1.1).
For example:
[ApiVersion("1.0", Deprecated = true)]
public class MySampleController
{
}
[ApiVersion("1.1")]
public class MyNewSampleController
{
}
[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("1.1")]
public class AllOtherController
{
}
Questions:
Do I really have to add all versions to all controllers?
Is there a better/correct way of handling this?
I have tried to use [ApiVersionNeutral] but that does not seem correct and, according to the documentation, should only be used in special cases.
If I do not add the [ApiVersion] attribute, it defaults to the new 1.1 version and 1.0 no longer works.
Since this is my first SO question, I hope itfalls within the guidelines :)
Q: Do I really have to add all versions to all controllers?
A: Yes. API versions are discrete. A client should get exactly what they ask for and an appropriate error response if the server cannot satisfy the request.
Q: Is there a better/correct way of handling this?
A: There are several possible options. A better or more correct way of handling things is highly subjective. There are numerous factors and the final decision may simply come down to preference.
A common misconception is that API Versioning uses attributes. It really doesn't care about attributes, it's just one possibility and one that tends to resonate with developers as metadata. You have the choice to use out-of-the-box attributes, custom attributes, out-of-the-box conventions, or your own custom conventions.
The choice of how to apply the version metadata is typically of preference and practical management. A common scenario is to organize controllers in folders by API version. In several .NET languages, the folder name translates to part or all of the corresponding namespace. This arrangement is common enough that there is an out-of-the-box VersionByNamespaceConvention for it. For details, see the documentation. The By Namespace Sample also demonstrates how to up such a project end-to-end.
API version-neutral means that an API takes any and all versions, including none at all. It can mean you don't care about the API version or that you accept a whole range that you'll deal with yourself. It is really only meant to be used with APIs that never change over time. For example, a HTTP DELETE operation typically does not change over time or across versions.
It's not 100% clear what you mean by:
"If I do not add the [ApiVersion] attribute, it defaults to the new 1.1 version and 1.0 no longer works."
There are no defaults per se. This statement seems to imply that you have set the AssumeDefaultVersionWhenUnspecified option to true. You should not do that unless you have a very good reason to do so. That is probably one of the most abused features of API Versioning. A client must know the version it is asking for. Allowing a client to not specify a version and having things transition from 1.0 to 1.1 can break the client. The server can make no assumption that it won't. This feature was meant to grandfather in existing services that didn't previously have an explicit version defined. That scenario only exists when API Versioning is first enabled. As stated above, all controllers must have one or more discrete API versions, but the original set of APIs didn't have it explicitly defined. If this feature didn't exist, then the baseline set of clients that didn't know about an API version would break.
I'm working on documentation for an API server implemented using ServiceStack. I have a few questions. If it makes more sense, I can move these to separate posts.
IgnoreDataMember is mentioned in multiple places as the way to hide DTO properties from metadata, but that's not an option if you publish the message to queue since that works via serialization and it skips those fields...
The Exclude attribute [Exclude(Feature.Metadata)] is supposed to hide DTO's from the meatadata page. While it works at top-level classes, it doesn't filter out properties of a base class. In other words if MyBaseDto is marked to exlude, but MyEndpointDto inherits from MyBaseDto, properties from both classes are shown. Is this a bug?
Removing services from metadata works really well, but I don't see how to remove them from Swagger/OpenAPI. I can implement the OperationFilter callback, but there's no obvious way there to actually remove anything. If I use the ResourceFilterPattern regex property, it works by DTO, not route. I'd love to remove any route that starts with /internal for instance.
The LogoUrl property of the OpenApiFeature and SwaggerFeature doesn't seem to actually affect the page anywhere. Is there another step to take?
On the Swagger UI page, why does every route include the text " XX is not defined!" I'm not sure what's it's supposed to be pull from.
I need help understanding how OpenAPI and Swagger fit together. Why are they two separate features? If I just add the OpenAPI feature, I can still bring up the Swagger UI. What does the Swagger feature bring to the table?
The difference between ServiceStack's Swagger Feature and Open API is that Swagger Feature implements the older Swagger 1.2 spec where as Open API implements the newer Swagger 2.0 spec which has been renamed from Swagger. If you're unsure which to use, use the newer Open API.
Swagger UI is just the UI and ServiceStack bundles a version of the Swagger UI which works with both the older Swagger 1.2 spec as well as the newer Open API 2.0 spec.
[Exclude(Feature.Metadata)] or [ExcludeMetadata] works as intended, annotate it on DTOs you wish to exclude from ServiceStack's metadata services which also removes them being included in the returned Swagger/Open API spec + UI.
You can use the ApiDeclarationFilter to modify the entire OpenApiDeclaration before it's returned, you can remove routes by removing them from the Paths Dictionary and Types definitions from being included by removing them from Definitions Dictionary. You can submit other feature requests for missing features you want implemented.
The LogoUrl has been fixed for Open API and we've also added LogoHref so you can change the url that the logo goes to which by default refreshes the Swagger UI page. This change is available from v4.5.13 that's now available on MyGet.
Open API is the spec Swagger UI is the UI
The purpose of both Open API and Swagger plugins is to implement the Swagger specification which is a generic and machine readable json format to describe Service APIs. Only one of the benefits is to provide a dynamic UI which ServiceStack provides by bundling the Swagger UI that works with their respective API versions. Other purposes of implementing the Open API spec include enabling automated tooling like generating a Azure AutoRest client or importing your Service into Azure's Management API.
My idea consists of two main elements:
Take C# Dto's (Data-tranfer-objects) and convert them into typescript interfaces to ensure client-side models are in sync with server side.
Take ASP .Net core controller endpoints and convert them to typescript classes that uses a http-service or similar. Again, to ensure client-side requests are in sync with the server.
And whenever a change have been made to a controller or dto, the typescript generated items should then refresh to stay in sync while developing.
I have done some research and found the following Stack Overflow threads and other sources:
DTO to TypeScript generator which suggest using the TypeLite library, which seems great, but according to the documentation, this either requires a [TsClass] Attribute or a reference to class on startup. But, since the project structure I'm using is setup so that all dto's is located in a *.Dtos namespace, I'm kinda missing a TypeScript.Definitions().ForNameSpace(). Also, this only solves the first idea/problem.
Swashbuckly.AspNetCore Would allow me to generate swagger documentation from both the controllers and dto's, and then the task would be to someway interpret the swagger documentation and create typescript classes and interfaces from that. The cons is that as far as i can read, this requires me to startup the server, which if possible i would like to avoid since it would make it hard to update on file change.
FYI, this is a new project I'm about to start, so there's no legacy code to update, also, all of the ASP .NET Core endpoints will return IActionResult to enable the return of Ok(), BadRequest() and so on. So to get the return model would in my mind be hard, since there's not an easy way to get the dto it produces, if any.
So, i have thought of the following solutions that solves both problems:
Create a separate package/application that uses the Swashbuckly lib and generates the models and controllers without starting up the whole server.
Create annotations on every endpoint, something along the lines of [Produces(SomeDto)], where after i would create a small console-application that uses reflection to get information and generate typescript from that. This would of cause requires developers to keep this information in sync, so in my mind there's kinda duplicate information.
But, both of these solutions would not auto-update on C# source file save.
Looking forward to any discussions/suggestions.
Considering your first point, I made a C# DTO to typescript interface generator that uses MSBUILD tasks so its completely independent of your workflow. It also just does it from the source which makes it a bit less stable but you don't have to make any template files.
Find it here or just search for MTT on nuget
In case you are still looking.... I think Typewriter http://frhagn.github.io/Typewriter/ is your solution. You can generate templates specifying what and how to transform.
It doesn't meet all of my needs only because I need a tool to dynamically generate a complicated folder structure, but that's coming in their v2 roadmap.
Besides that, it does a lot of heavy lifting and is pretty easy to configure.
I've spent days trying to mock, stub and fake my way to a testable application. I usually don't test controller actions, but test all my other classes and patterns instead.
The wall I hit was with the new attribute routing feature. While I can use the routing classes to register my rules etc. I get this error when MapMvcAttributeRoutes is called.
This method cannot be called during the application's pre-start initialization phase
This is discussed here.
MapMvcAttributeRoutes: This method cannot be called during the application's pre-start initialization phase
To be honest, I can't understand the answer(s). Not the code, but its fragmented into versions, links to other bugs, GitHub etc.
I'm a bit lost as to the bottom line answer:
As of 23rd October, 2014. Is it possible to register all routes under test conditions, what version of MVC do I need and which classes/methods do I call to do it?
At present, my classes using UrlHelper are screwing up because needed routes are missing. I am injecting subclasses to bypass the issue, but I don't think its unreasonable to fake the runtime MVC environment and have my app run without lots of DI acrobatics.
It would be nice if these was a simple helper in the framework itself that could take a JSON object describing a raw HTTP request and have the Controller, HttpContext, ControllerContext etc. etc. all created properly as if it were a real request off the wire.
Thanks,
Luke
Good question, and I think that the answer is that little or no thought was given to testing routes in the design of this part of the framework. There may be ways to test routes, but they will be indirect, undocumented and prone to break when a new version of MVC ships.
I have a blog post here on my experiences on the topic. I also suggest that you also campaign for better testability in ASP vNext in the public issue tracker.
During a daily stand-up, a colleague mentioned he was integration testing via an in-memory web server. Intrigued, he showed me how and I was amazed, learnt something :-)
You can new-up an HttpServer instance and have it read your config and then invoke the server instance. I have not tried it, but I see no reason why this wouldn't enumerate your routes and code needing proper config will all work.
This SO question is related and may help in how to set this up:
How does the In-Memory HttpServer know which WebAPI project to host?