MapHttpRoute to the Swashbuckle Swagger UI to use custom handler - c#

I've got Swashbuckle going in my web api project. I want to get a custom handler invoked only when a user tries to hit the swagger ui page. I don't want to add the handler to the pipeline.
I thought something like this might work but it doesn't:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "SwaggerUI",
routeTemplate: "docsite/{*assetPath}",
defaults: null,
constraints: null,
handler: new MyHandler() { InnerHandler = new HttpControllerDispatcher(config) }
I use route attributes everywhere else in the project so I map those attributes first.
My swagger config is using
.EnableSwaggerUi("docsite/{*assetPath}",c =>
hence the docsite route.
I don't want to add the handler to the pipeline because I don't want it to get called with every web api call. I'm using it to restrict access to the swagger UI.
Also I'm using OWIN to authenticate one particular api so I get this error when I try to call that api
The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'MyHandler' is not null.
Parameter name: handlers
Figured I can avoid the error by just making sure the handler only happens when I do a swagger ui route.
Who wants to help!

Related

what is the purpose of the EnableSwagger routeTemplate parameter?

I'm trying to solve an issue where my controllers are decorated with a RoutePrefix like this:
[RoutePrefix("api/v{version:apiVersion}/users")]
But Swagger displays the urls in the TOC like this:
/api/v{version}/users/search
I was experimenting with the EnableSwagger routeTemplate parameter but I ended up getting the errors displayed in the inline comments below:
GlobalConfiguration.Configuration
.EnableSwagger(
//"v{version:apiVersion}", //A potentially dangerous Request.Path value was detected from the client (:).
//"v{version}", //Can't read swagger JSON from http://localhost:52056/v{version}
//"{version}", //Can't read swagger JSON from http://localhost:52056/{version}
//"v2", //Can't read swagger JSON from http://localhost:52056/v2
c =>
{
//...
}
}
What is the proper usage of the EnableSwagger routeTemplate parameter? And what type of use case scenarios is this parameter designed to address? Is this parameter designed to assist with the particular problem I described? Or am I attempting to use this parameter incorrectly or improperly?
From my understanding, it's to allow customization of the routes to not be defined by the traditional ASP.NET MVC/Web API route configs, like so :
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
While you might find something like the above in a WebApiConfig.cs file, you can change the path for Swagger JSON endpoints using the RouteTemplate.
From the Readme :
By default, Swagger JSON will be exposed at the following route -
"/swagger/{documentName}/swagger.json". If necessary, you can change
this when enabling the Swagger middleware. Custom routes MUST include
the {documentName} parameter.
app.UseSwagger(c =>
{
c.RouteTemplate = "api-docs/{documentName}/swagger.json";
});
NOTE: If you're using the SwaggerUI middleware, you'll also need to update its configuration to reflect the new endpoints:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/api-docs/v1/swagger.json", "My API V1");
})
For versioning APIs, you can write a custom resolver most likely. And to that point, I think you've made it a little harder on yourself by versioning your APIs this way. If you simply created a v1/{resource} and v2/{resource} you could certainly write a custom resolver to look at the version in the route and use the c.MultipleApiVersions() API to wire everything up according to the route.

Asp.net MVC API - Custom Model Routing / Request Transformation

I am creating an Web API application which takes jwt tokens as arguments.
However, I would like to make this transparent to most of the application.
I would like to take a request that looks like:
website/{controller}/{action}/{token}
then, parse and verify it, then sending it to the controllers as if it were requested in the following format: website/{controller}/{action}/user/{sub}/ect/{ect}
Is there a way for me to implement a catch all transform class in the existing framework? Or will I have to make a transformation method per action?
You certainly don't need a method per action. You can map it to a single controller method that does the routing:
routes.MapRoute(
name: "ProfileRoute",
url: "{controller}/{action}/{token}",
defaults: new { controller = "Home", action = "RouteRequest" },
);
This will then map to HomeController.RouteRequest(string token).
Not sure if you can do what you want in the actual route configuration, but I think not.
You should pass the token in the header. From wikipedia:
Whenever the user wants to access a protected route or resource, the
user agent should send the JWT, typically in the Authorization header
using the Bearer schema. The content of the header might look like the
following:
Authorization: Bearer eyJhbGci......yu5CSpyHI
You can then write a Authentication Filter or custom message handler to process the tokens.

Restrict API route to controller namespace with ASP.NET

I have found a similar question that relates to standard asp.net controllers, but i have tried it and it doesn't seem to be working with the apicontroller.
Restrict route to controller namespace in ASP.NET Core
I want to allow for an API to be deprecated in the future without needing to change the url. To solve this i will put a version prefix at the start of the route. This way i can add some modifications to an endpoint without breaking any integrations with this API.
config.Routes.MapHttpRoute(
name: "V1 API",
routeTemplate: "v1/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "V2 API",
routeTemplate: "v2/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I want to keep the same controller names and in most cases everything will be the exact same, the response and request body will be the only things that change. I have tried to add a subdirectory in the controllers folder in order to add v1 and v2 to the namespace of the controller, but i get an error about there being 2 controllers having the same name.
How can i modify the routes so that it will point to the v1 namespace for the controller instead of just searching for any class within the controller. I dont want to have to add the version to the controller name as i want to make the revision updates as seamless as possible.
I think you could solve this issue by adding the route prefix to each controller.
Can you use a URL query parameter?
https://localhost:8000/{controller}/{action}/{id}?version=?
You could then use control flow within the controller to determine what logic to use, based on the version.

C# Web Services Routing Controller to Defined URL

I am new to C#, committing to a little Spike. I have built a few POCO OWIN self-hosting services and am trying to create a controller but guides and documentation everywhere appear to be pointing me in the incorrect direction.
I am adding default mapping to a configuration method inside a startup class. Then in my controller I have a simple GET method. This works like a charm, when I send a request to it using the url defined in my startup routing the method gets invoked. But now I need to set up a second method to be invoked by a new url.
I don't understand what I am missing and what I am not understanding but everything I attempt does not work for me:
relevant code in the controller:
// GET api/ManagedService
public string[] Get()
{
Start();
return new string[]
{
"Job Processed."
};
}
Relevant code in my startup class:
HttpConfiguration config = new HttpConfiguration();
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/start"
);
So on visiting api/{controller}/start I invoke my get but I want to invoke a different method with api/{controller}/receiptandsend.
I'm not 100% sure I understand what the problem is but try to change your routeTemplate to "api/{controller}/{action}" instead. The routeTemplate you are using assumes that you have an action (method) called start on every controller. Alternatively you could try to add another route, something like name: "anotherOne", routeTemplate: "api/{controller}/receiptandsend" but I don't think that is really the way to do it correctly.
edit: By the way, your Get method should probably be named something else, like StartJob or something, "Get" seems a bit misleading seeing that you don't get anything back except a text saying that you started some other process.

Unit testing WebApi routes with custom handlers

I'm having a problem testing routes and I think it's due to the route using a custom handler.
This question points to a similar issue, but doesn't quite fix what I'm doing.
I have a RouteConfiguration class that simply houses the route definitions to enable me to pull them in for the application as well as any testing routines:
// the route
ApiRoutes.MapHttpRoute(
name: "Custom",
routeTemplate: "api/service/{service}/{method}/{arguments}",
defaults: new { arguments = RouteParameter.Optional },
constraints: null,
handler: new ServiceDispatcher(_httpConfiguration, _controllerHelper)
);
Now when trying to test this using a similar pattern to this way of mocking the route it doesn't find the route (404). The select part looks like this (other code in this article is not shown):
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/service/foo/bar/foobar");
DefaultHttpControllerSelector controllerSelector = new DefaultHttpControllerSelector(configuration);
HttpControllerDescriptor descriptor = controllerSelector.SelectController(message);
My (educated?) guess is this DefaultHttpControllerSelector but I don't know how or if it can be changed to support what I'm doing.
So, can I make this current routine work using custom handlers or am I barking up the wrong tree?
When you have a MessageHandler attached to the route, the controller dispatcher is never even called. It doesn't get that far.
If you want to test the routes, I would just call
var routeDate = config.Routes.GetRouteData(request);
Then you can check the route values and test for the service, method and arguments parameters.

Categories

Resources