I have found out how to version my WebAPI based on namespaces using this class.
I am using Swashbuckle to add Swagger doc to my API, using the Swashbuckle Nuget package.
If I keep everything intact, when I navigate to /swagger/, I get an empty page.
In my App_Start:
public class SwaggerConfig
{
public static void Register()
{
Bootstrapper.Init(GlobalConfiguration.Configuration);
SwaggerSpecConfig.Customize(c =>
{
c.IncludeXmlComments(GetXmlCommentsPath());
});
}
private static string GetXmlCommentsPath()
{
return string.Format(#"{0}\App_Data\XmlDocumentation.xml", AppDomain.CurrentDomain.BaseDirectory);
}
}
And my web API routes:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{namespace}/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
});
}
}
If I remove the {namespace} it works (the API commands are displayed), but I want to keep this namespace information in my route.
How do I customize Swagger/Swashbuckle to make this work?
From the Swashbuckle Github repo:
There is a flaw in the above implementation of "namespace routing" as it breaks the WebApi metadata layer - ApiExplorer and hence Swashbuckle.
A workaround, although doesn't directly solve your problem, is to use attribute versioning instead, which works fine with Swashbuckle:
I.e.:
[RoutePrefix("api/v1/Features")]
public class FeaturesV1Controller : ApiController
{
[Route("Products/{product}")]
public IList<Metadata.FeatureListItemModel> Get(long product){}
Please see the two Github issues below for more info.
https://github.com/domaindrivendev/Swashbuckle/issues/317
https://github.com/domaindrivendev/Swashbuckle/issues/303
I believe that with attribute routing, your controller must have a different name for each version. I.e. the class should be named FeaturesV1Controller and FeaturesV2Controller for v2, but for the routes you can still use /api/v1/Features and /api/v2/Features
Related
I am pretty new to ASP.NET Website programming. I have an node js express application where I need to make requests to. This currently doesnt works from my asp.net site because i dont have cors enabled. I hope you can help me and if I am just beeing stupid and configured my website wrong or forgot to add a controller please let me know.
I tried adding cors package via nuget and adding it to the web.config.
In the Solution Explorer, expand the WebApi project. Open the file App_Start/WebApiConfig.cs, and add the following code to the method WebApiConfig.Register.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Then add the attribute [EnableCors] to the desired controller:
namespace MyProject.Controllers
{
[EnableCors(origins: "http://myclient.azurewebsites.net", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
}
VS 2017, New, Project, C#, ASP.NET Web app, ASP.NET 4.5.2 Empty Templates.
Unchecked folder and reference for Webforms, MVC and WebAPI. Later added MS WebApi v5.4.2 via Nuget.
Manually added "Controllers" folder.
xController.cs:
namespace v1.MyApiCallTheirApi.api
{
public class xController : ApiController
{
// GET api/<controller>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
}
index.html:
<body>
<input type="button" value="Go" onclick="go()"/>
<script type="text/javascript">
function go()
{
alert("lafdla");
$.ajax({
type: "GET",
url: "/api/x",
success: alert("ok")
});
}
</script>
</body>
$.ajax call always returns 404. Already check this, that, and many others. So far my only suspect is it may need a Global.asax for routing config, but I assume after adding API it should auto-add some hidden routing for me.
Right now your application doesn't knows how routes are created. MVC cannot know automatically unless you provide the pointers as to where each type of code is located.
Hence two ways out here (using GlobalConfiguration instance):-
a) Convention based, use map route method. No other place requires any further change.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
b) Use attributes on controller methods to define actual routes. Then add the route attribute on each action method
config.MapHttpAttributeRoutes();
Over action
[Route('api/myentity/get')
public entity GetEntity() { }
As for adding the package, it only provides you the relevant dlls required for WebAPI, doesn't makes any more changes
You can define the default action of the controller also by the Action attribute [HttpGet("")].
You need to update your action:
// GET api/<controller>
[HttpGet("")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
As a tip Postman is a nice tool to test your api requests, which is also recommendet by the .NET documentation, see here.
Summarize the WHY and SOLUTION of my problem:
Global.asax
API Routing registration via App_Start
Attribute and Convention routings can't mixed together
Once going Attribute you have to declare attribute for each method.
Although Api controller can be placed anywhere/folder and can be named anything without "Controller" suffix, it must have the "Controller" suffix for class, i.e. public class v1Controller : ApiController, just public class v1 : ApiController won't work.
Global.asax is optional but it's like _init() of a project, so is a must here because API needs routing. Whether this Api is invoked by in-project or out-of-project, as soon as it's hit the built-in App_Start init everything of the project.
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
API routing registration defined in a class typically in App_Start, convention name is WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Either this Attribute Routing
config.MapHttpAttributeRoutes();
// Or this conventional Routing, but not together.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Adding a new package from Nuget won't auto-config/add necessary items
to project.
MS WebApi is developed based on MVC routing, it's confused if MVC is needed, here is a good reading. Answer is No, you don't need MVC in order to do WebApi.
Question is regarding defining custom routes with the Route attribute.
I know that in the WebApiConfig class you always define the default route,
configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
What I cannot get working is when I want to pass another parameter. I know I can do this (code below is defined underneath the default route listed above):
//configuration.Routes.MapHttpRoute(
// name: "GetBrandImagePaths",
// routeTemplate: "api/{controller}/{id}/{type}");
But I'd rather, instead of defining all these routes in the WebApiConfig file, use custom routing. However, if I do not have the commented out code above in the file, I get a 404. Thus leading me to believe the custom Route is not even being looked at.
public class HelperApiController : ApiController
{
[HttpGet]
[Route("api/helperapi/{id}/{type}")]
public string GetBrandImages(int id, string type)
{
.....
}
}
How can I have it so I can use routes defined in the WebApiConfig file, AND defining custom routes inside individual API controllers.
Note that this project is also a MVC project (not just WebApi). Is there something I'm missing, doing incorrectly etc? I know there's numerous posts out there defining how to pass multiple params, but I think my question is a little more specific on to why one works and not the other.
You need to call config.MapHttpAttributeRoutes().
This will parse all the Controller classes and derive the routes from the attributes.
I would not mix this with the standard routing.
Attribute Routing in ASP.NET Web API 2
Enabling Attribute Routing
To enable attribute routing, call MapHttpAttributeRoutes during
configuration. This extension method is defined in the
System.Web.Http.HttpConfigurationExtensions class.
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
Attribute routing can be combined with convention-based routing. To
define convention-based routes, call the MapHttpRoute method.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I am trying to post to the following Web API:
http://localhost:8543/api/login/authenticate
LoginApi (Web API) is defined below:
[RoutePrefix("login")]
public class LoginApi : ApiController
{
[HttpPost]
[Route("authenticate")]
public string Authenticate(LoginViewModel loginViewModel)
{
return "Hello World";
}
}
WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Here is the error I get:
Request URL:http://localhost:8543/api/login/authenticate
Request Method:POST
Status Code:404 Not Found
Remote Address:[::1]:8543
Your controller name "LoginApi" needs to end in "Controller" in order for the framework to find it. For example: "LoginController"
Here is a good article which explains routing in ASP.NET Web API: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
You are using login as your route prefix on your controller so trying to call
http://localhost:8543/api/login/authenticate
will not be found as this code
[RoutePrefix("login")]
public class LoginApi : ApiController
{
//eg:POST login/authenticate.
[HttpPost]
[Route("authenticate")]
public string Authenticate(LoginViewModel loginViewModel)
{
return "Hello World";
}
}
will only work for
http://localhost:8543/login/authenticate
You need to change your route prefix to
[RoutePrefix("api/login")]
public class LoginApi : ApiController
{
//eg:POST api/login/authenticate.
[HttpPost]
[Route("authenticate")]
public string Authenticate(LoginViewModel loginViewModel)
{
return "Hello World";
}
}
Notice you are using both attribute routing on the controller/action and convention routing with config.Routes.MapHttpRoute.
config.Routes.MapHttpRoute will map the routes as per your definition "api/{controller}/{id}".
While attribute routing, will map the routes based on how you've defined them: /login/authenticate.
Also, since you are using both attribute routing and convention routing, attribute routing takes presendence. I would stick to using one or the other. Having both adds a bit of confusion as to what route will be used to access an action method.
I am using routing in my WebApi Katana application. I have the following two route mappings that work fine. My question is, can I combine these into a single route mapping using optional parameters? I can’t see an obvious way to do it and keep the required functionality. I am new to this and may have missed a technique that my help me achieve this. If the routes have to stay this way then this isn’t a problem.
config.Routes.MapHttpRoute(
name: "UnRegister",
routeTemplate: "api/services/{serviceName}/{location}",
defaults: new {controller = "MyController", location = RouteParameter.Optional});
config.Routes.MapHttpRoute(
name: "UnRegister2",
routeTemplate: "api/services/{serviceName}/{instanceId}",
defaults: new { controller = "MyController" });
The required functionality is to unregister a service by supplying the following details:
Servicename
Servicename and location
Servicename and instanceId
In ASP.NET Web API 2 you can use attribute routing and you don't have to define all your routes that way with MapHttpRoute.
The explanation can be found here.
In your Owin Startup you have to enable the attribute routing using MapHttpAttributeRoutes:
public class Startup
{
public static void Configuration(IAppBuilder app)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
// Enable attribute based routing
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
}
and your controller should look something like this:
[RoutePrefix("api/service")]
public class ServicesController : ApiController
{
[HttpGet]
[Route("{location}")]
public IHttpActionResult GetByLocation(string location)
{
return Ok();
}
[HttpGet]
[Route("{instanceId:int}")]
public IHttpActionResult GetByInstanceId(int instanceId)
{
return Ok();
}
}
As you can see I've used RoutePrefix to define the endpoint and route constraints to restrict parameters, as suggested in the article.
You can even create your own custom constraints.
The article suggest that you have to install the NuGet package Microsoft.AspNet.WebApi.WebHost.
That's not necessary aymore.