WebAPI Help Pages - Read Controller Attributes - c#

I am using WebAPI Help pages project from nuget to document my ASP.Net WebAPI services.
I have several controllers that have the [Authorize] attribute and several custom Attributes.
What I have not been able to achieve is to have the Attributes added to the documentation.
So if a Controller is marked as [Authorize], then the documentation for the controller would say something like "This XYZ Controller requires Authorization"
So my question is how can I modify the WebAPI help code, to document the attributes on my Controllers.
Model level attributes are working with no issue.

You can modify the installed XmlDocumentationProvider.cs at Areas\HelpPage\ for this. There modify the GetDocumentation(HttpControllerDescriptor controllerDescriptor) method. You can inspect any attributes that are decorated no the controller type controllerDescriptor.ControllerType and accordingly change the documentation.
When upgrading the HelpPage nuget package you might find doing the above inconvenient as you might want to override the contents with latest bits...so instead you can create a custom documentation provider inheriting from XmlDocumentationProvider and instead make a small modification to the installed HelpPageConfig.c file and mention to your custom provider.

Related

What does the attribute "[ApiExplorerSettings(IgnoreApi = true)]" do?

EDIT: I'm aware of what attributes in general do, the question is for this specific attribute alone. Sorry for the confusion!
I've read the following question, along with this one, which point to how the attribute is used to ignore the generated swagger/swashbuckle documentation for specific methods or whole controllers. (documentation is the swagger page with all the api's listed I believe?)
But other than swagger/swashbuckle (which is a NuGet package), what other function does this attribute possess in ASP.NET?
When applied to a public method on a controller, it prevents that method from appearing in the swagger ui.
First of all, to clarify, an attribute in C# does not trigger anything by itself. External code searches for classes, methods or properties marked with a specific attribute, and act accordingly.
Of course, there are many building blocks in ASP.NET MVC, it can be confusing sometimes.
This attribute is used by Swagger to hide the endpoint.
Also usedd (in .NET core at least) by the given implementations of IApiDescriptionProvider and other related interfaces, but that would be effective only if you actually use them (by configuring them up in Startup.cs)
(for some more details and example, see https://andrewlock.net/introduction-to-the-apiexplorer-in-asp-net-core/)
This attribute helps to control the visibility. We can use this on the controller class or an action method when we want to hide that specific controller or action from showing in the swagger UI.

Specify API version for all WEB API controllers

I am building an API using Web API .NET 2, created a BaseController class which all other controllers inherit. I would like to apply API versioning using [ApiVersion()] attribute, however I don't want to decorate each controller class with same attributes if I have more than one version of API. Is there a way to set all possible API versions for all controllers? I tried to set attributes on BaseController class, but unfortunately attributes are not inherited by derived classes.
The ApiVersionAttribute intentionally is not inherited because if you use inheritance on your controllers, for example V2.Controllers.ValuesController inherits from V1.Controllers.ValuesController, you end up with two controllers with the same route that both implement 1.0, which is ambiguous and will produce an error.
There are several possible ways to achieve your goal:
Create your own attribute, extending ApiVersionsBaseAttribute, allow it to be inherited and apply it to a base class.
Create your own attribute, implement IApiVersionProvider, allow it to be inherited, and apply it to a base class.
Enumerate all controller types in your configuration setup and use a convention that applies one or more API versions to all controllers. options.Conventions.Controller(controllerType).HasApiVersion(1.0);. This will be unioned with an explicit attributes (ex: 1.1)
You can author a custom convention, which applies one or more API versions to all or specific controllers. Currently, the convention can only be applied to an entire controller, but per-action support will be available in the next major release (as there are breaking changes to the API, even though there is no visible change for most). For example, options.Conventions.Add(new MyConvention(new ApiVersion(1,0)));.
I hope that helps.
You can use ApiVersioning middleware as shown in below example
services.AddApiVersioning(
o =>
{
o.AssumeDefaultVersionWhenUnspecified = true );
o.DefaultApiVersion = new ApiVersion( 1,0);
} );
It sets default version for all controllers to be 1.0.
Also if version is not specified, the call will be routed to controller with default version.
If you want to create new version of existing controller, then you can specify the version attribute on that controller.
Hope this helps.

ServiceStack confusion between metadata, OpenAPI, and Swagger

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.

Surface Controller or Custom Controller in Umbraco 7?

History
I'm a Web Forms developer with some .NET MVC experience, new to Umbraco and learning as I go.
So far I've been following the Umbraco documentation and videos to get set up which means all my controllers inherit from a "Controller Base" with common functions included, which in itself inherits from SurfaceController.
Recently however, I have noticed some bloggers and external reference material referencing RenderMvcController in the base class instead of SurfaceController - now the Umbraco documentation is unclear on the real differences between the two, nor which situations you should use them in.
The Question
Is there a clear and distinct difference between the imagined usage scenarios for a Surface Controller - inheriting from Umbraco.Web.Mvc.SurfaceController, and a Custom Controller - inheriting from Umbraco.Web.Mvc.RenderMvcController?
Thanks!
The documentation for the SurfaceController is here: http://our.umbraco.org/documentation/Reference/Templating/Mvc/surface-controllers
In a nutshell, the SurfaceController is used for helping to interact with views. So for example you could post a form to a surface controller. Or you could write a child action to a view with a SurfaceController
RenderMvcController is used purely for routing to published pages. So you could sub-class RenderMvcController in order to 'hijack' requests to published pages of a specific Document Type. See here http://our.umbraco.org/documentation/Reference/Templating/Mvc/custom-controllers.
To further clarify based on Digbyswift's answer:
SurfaceController = APIs or form targets (that then redirect)
RenderMvcController = custom logic to build a model or select a view for a content item (based on Document Type and, optionally, template)

Change the Location of the Controller inside the Project structure

I found this Post (How to extend where MVC looks for views) about changing the location of the View.
I was wondering if there's something similar for changing the location of the controller.
I just want to change the location of the class inside project and don't want to affect the url.
For example Instead of placing the Controller into
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
I want to achieve something like
MyMvcProject\MyGroup1\
MyController1.cs
MyController2.cs
MyMvcProject\MyGroup2\
MyController3.cs
and also support Areas:
MyMvcProject\Areas\MyGroup3\
MyController4.cs
Is it possible to achieve this? And if yes, where can I find documentation about it?
You can do what you want, and it doesn't require any special configuration, because ASP.NET MVC does not care about where you put your controllers. First, controllers are located using reflection, so the name of the folder where you put your controllers is irrelevant. Controllers are searched by type name and optionally by namespace (for disambiguation). You can even have controllers in separate projects/assemblies. As long as the controller ends up in an assembly in the bin folder then it's searchable by the framework.
As mentioned above, you'll need to create a controller factory to support your custom resolution. Here's an example:
http://develoq.net/2010/custom-controller-factory-in-asp-net-mvc/
As others have already stated you need to do one of the following:
Derive from IControllerFactory interface and provide an implementation of the CreateController and ReleaseController methods.
Derive from DefaultControllerFactory and override the default behaviours.
Here are some links to get you started:
Custom controller factory in ASP.Net
Inside the ASP.NET MVC Controller factory
Dive deep into MVC - IControllerFactory
Also, if you're willing to spend a bit of money I would also recommend the book Pro ASP.NET MVC 3 Framework as this explains almost every aspect of how the MVC framework can be customised (including an example on how to create a custom controller factory - the source code for which can be freely downloaded from the publishers website).
I think it is impossible to do this. ASP.NET MVC have defined the convention that we have to follow.
Controllers are in Controllers folder, views are in Views{ControllerName}\
I believe you cannot change the convention unless you create your own ControllerFactory.
If you really want to do that, just implement IControllerFactory interface (or try to derive from DefaultControllerFactory).
Then your Application_Start register your controller factory using ControllerBuilder.Current.SetControllerFactory method.
Look at the ControllerFactory documentation and to the MVC source code for details.
What you're asking and what your example shows are two different things; depending on which one you want to achieve, you may or may not need to do any work.
There are two requirements for a class to be a controller in the MVC Framework:
It has to have a class name of Name + "Controller"
It has to have a parameterless public constructor.
Your sample "normal" MVC layout is actually not valid:
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
Those classes wouldn't be found by MVC because they don't have the correct name, regardless of which folder they are in.
If all you want to do is change the namespace/folder names, that "just works", assuming you name them the same as the appropriate route segment(s):
MyMvcProject\MyGroup1\
Page1Controller.cs
Page2Controller.cs
MyMvcProject\MyGroup2\
Page3Controller.cs
MyMvcProject\Areas\Area1\
Area1Page1Controller.cs
This walkthrough (written for MVC 2 but works just as well in MVC3) shows you how to support Areas with the default controller behavior.
If you actually want to name them SomethingController1 or SomethingElseController5, or otherwise change the route -> classname mappings, then you do need to implement a custom ControllerFactory, and inject it into the MVC pipeline.
There are plenty of examples on the web on how to do this, including the one posted earlier.

Categories

Resources