Route name by convention or module name in NancyFX - c#

When we create a module inside a NancyFX app, we do routing this way:
public class HappyModule : Nancy.NancyModule
{
public HappyModule() : base("/Happy")
{
Get["/"] = _ => "Received GET request";
}
}
So, when we hit http://<host>:<port>/Happy/, we get the expected response. However, I think that hard-coding routes is not a "very-maintainable" thing to do (specially on large systems), so the question is: Is there a way to set a convention or something that make routes being named by its respective module name, so this way I must not always need to extend/hard-code route the base NancyModule constructor?
What I really want to know is: Is there a routing mechanism similar to that one we have in ASP.NET WebApi (code below)?
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

The short answer to your question it nope. We don't support route conventions in Nancy.
In my personal experience, every project I've worked on I've swayed away from the conventions in MVC and ended up using Attribute Routing. The conventions only get you so far before you're like "wait, how do I get to this route again?"
Then you end up sifting through files trying to figure out where the route is registered.
APIs should be defined once and rarely ever need to change, if ever. Defining the route close to the implementation ensures that you can easily find things.
The base path allows you to define area's of a site though. For example rather than writing /admin everywhere, you could define an abstract module for the admin area.
public abstract class AdminModule : NancyModule
{
public AdminModule() : base("/admin"){}
public AdminModule(string path) : base("/admin/" + path.TrimStart('/')){}
}
Something like this, where all admin modules inherit from this to ensure all paths are prefixed with /admin

Related

Cannot get ASP.NET Web API routing to work at all

I am trying to refactor an old setup, the end goal will be that I will have a functional REST API. I am getting rid of an old NuGet service and I sort of have to rebuild everything now. I am trying to set it up using ASP.NET Web API. This will include 60+ routes (like "www.website.com/cars/{id}/engine" "www.website.com/inventory/{id}" etc..)
I have tried attribute routing and conventional routing, and nothing seems to work. I get 404's no matter what I seem to try. I am probably just not doing either of them correctly.
Here is how I am setting up configuration (I am using a self hosting package btw but this is the main configuration point):
public class SomeHostClass
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//If I am trying attribute based routing, uncomment this
//config.MapHttpAttributeRoutes();
Register(config.Routes);
// I think I need this at all times?
app.UseWebApi(config);
}
public void Register(HttpRouteCollection routes)
{
// One of many other routes.MapHttpRoute() calls
routes.MapHttpRoute(
name: "Cars",
routeTemplate: "cars/{id}/engine",
defaults: new
{
controller = "CarController",
action = "Get"
},
constraints: null);
}
}
Then here is my Car Controller:
public class CarController : ApiController
{
[HttpGet]
public object Get([FromUri] CarDTO carObject)
{
// Some code calling a private worker method
return WorkerMethod();
}
private object WorkerMethod()
{
// Worker method do stuff, return
}
}
This Get() method is never called and a 404 is returned.
Another note: I have tried using reflection to register all the routes, which slims down the code. I'll debug it and it looks like all the routes have been configured correctly within the HttpConfiguration.routes but I will get 404's still. Even without reflection doing it the way shown above- if I debug and look at the values they all seem correct, but don't work.
Another Note: I should also mention that this is the error I usually get along with the 404.
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:8000/cars/1/engine'.","MessageDetail":"No type was found that matches the controller named 'CarController'."}
How do I get these routes recognized and stop returning 404's and start returning my data?
If this is a bad way of going about this- what's the best way?

Web Api 2 Routing Issue: "No action was found on the controller..."

I am having the issue of getting a 404 on my web api routing. Although there are many posts about this issue, they mainly seem to be about changing the order of the routes so that the MVC routes don't override the api routes.
I have tried all of the solution I have come across yet nothing seems to fix my issue.
Here is my controller:
[RoutePrefix("api/paving-designer")]
public class PavingDesignerController : ApiController
{
[HttpGet]
[Route("get-forms/{userId}")]
public IHttpActionResult GetForms(Guid userId)
{
ICollection<PavingDesignerFlatForm> forms = _helper.GetForms(userId);
if (forms != null)
{
return Ok(forms);
}
else
{
return NotFound();
}
}
}
And this is my Web Api Config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new
{
id = RouteParameter.Optional
});
}
}
and this is my global asax
private void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
// RouteConfig.RegisterRoutes(RouteTable.Routes);
// Initialize Castle & install application components
_bootstrapper = CastleInitialiser.Initialise();
}
As you can see I have even tried to comment out the mvc routes to see if that made a difference
If I browse to http://localhost/api/paving-designer/get-forms/c6c489a7-46c6-420e-9e39-56797c8094cf
I get the following error:
No type was found that matches the controller named 'paving-designer'.
I have tried changing the route prefix to the following but to no avail
/api/paving-designer
/paving-designer
paving-designer
And if I browse to http://localhost/api/pavingdesigner/get-forms/c6c489a7-46c6-420e-9e39-56797c8094cf
I get the following error:
Multiple types were found that match the controller named 'pavingdesigner'. This can happen if the route that services this request ('api/{controller}/{action}/{id}') found multiple controllers defined with the same name but differing namespaces
I don't see how I can have multiple controllers as this is the only one I have.
Can anyone see where I am going wrong?
You are using both routing types.
Using attribute routing defined next route:
/api/paving-designer/get-forms/{userId}
Using default routing there is other route:
/api/{controller}/{action}/{id}
These routes are have the same template.
But using the first of them - ControllerSelector can not find paving-designerController.
Using the second - there is no action named get-forms. There are GetForms
If you remove one of them - it should work.
Ok in my particular case, the error was being caused as my IoC was registering the controller twice.
This was causing the duplicate entries, which in turn made the attribute routing fail.

WebAPI MapHttpRoute doesn't work, but AttributeRouting does

I have a case where I can't get a default route to work via Config.Routes.MapHttpRoute(), but if I put the route as a route attribute in the controller it works fine.
Global.asax
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//RouteConfig.RegisterRoutes(RouteTable.Routes);
//BundleConfig.RegisterBundles(BundleTable.Bundles);
}
(removed last two as they're non-WebAPI requirements (right?) same result even if I leave them in)
WebApiConfig.cs
public static void Register(HttpConfiguration config) {
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "v1/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Controller
public class ErrorsController : ApiController
{
[HttpGet]
[Route("v1/Errors/Get")]
public IHttpActionResult Get(int id) {
return Ok();
}
[HttpPost]
[Route("v1/Errors/Submit")]
public IHttpActionResult Submit()
{
// do stuff
return Ok();
}
}
If I have the attribute routes in there, everything works fine. If I don't, I get 404s. For example, the following two report a 404 error:
localhost:myport/v1/Errors/Get?id=5
localhost:myport/v1/Errors/Submit
Yet, if I submit this:
localhost:myport/v1/Errors
I get the following response:
No HTTP resource was found that matches the request URI
'http://localhost:59498/v1/Errors/'.
No type was found that matches the controller named 'v1'.
Obviously my route configuration isn't kicking in, but for the life of my I can't tell why. I even tried changing the route name to Default instead of DefaultApi, thinking that perhaps Default had some internal significance.
"Normal" routing doesn't support this kind of routes with prefix "v1" in it. In a way or another this kind of rounting follows the old MVC routing, where the first part of the address must be the controller; so when you set the address localhost:myport/v1/Errors the system is currently looking for a controller named "v1".
If you want to use a route with a prefix before you have to stick with the attribute routing; that's why it works perfectly with attributes and it doesn't without.
May I suggest you to use a "global" prefix for the v1 thing? That would allow you to avoid repeating the same part of the URL over and over again on different resources. I suggest you to check this article for a couple of implementation details.

WebApi 2 Route Attribute not working unless using parameter

I have a controller List
[Route("api/cache/list")]
[HttpGet]
public IEnumerable<string> List()
{
...
}
But i get a 404 if i try to go to localhost:12121/api/cache/list. My webapi config looks like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
//config.SuppressDefaultHostAuthentication();
//config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Formatters.Remove(config.Formatters.XmlFormatter);
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
}
}
On the other hand if i change that controller to
[Route("api/cache/list/{id}")]
[HttpGet]
public IEnumerable<string> List(int id =0)
{
...
}
It will work. Ive tried doing it without the id as a parameter, still doenst work. What am I doing wrong here?
Extra Info:
I do have a strange setup. My WebApiConfig.cs and global.cs are in a different project. The project which has my controller will then reference the project with the WebApiConfig.cs
Rather than giving the param a default value, try making your id param nullable:
public IEnumerable<string> List(int? id)
Then check for null in code. This also helps differentiate a passed zero from an omitted param value.
I wasnt able to figure out how to fix the problem exactly but I did find a way around my problem.
First I correctly identified that when my routing was in a single assembly the exact same code worked fine. When the routing for my project in a different assembly makes it difficult for config.MapHttpAttributeRoutes(); to correctly identify attribute routes.
On the other hand using a RoutePrefix seemed to fix my issue.
Im not going to set this as my answer because I feel it doesn't truly address the problem, but I felt it would be informational to anyone who comes along this question.

Web Api Controller in other project, route attribute not working

I have a solution with two projects. One Web Api bootstap project and the other is a class library.
The class library contains a ApiController with attribute routing.
I add a reference from web api project to the class library and expect this to just work.
The routing in the web api is configured:
config.MapHttpAttributeRoutes();
The controller is simple and looks like:
public class AlertApiController:ApiController
{
[Route("alert")]
[HttpGet]
public HttpResponseMessage GetAlert()
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "alert");
}
}
But I get a 404 when going to the url "/alert".
What am I missing here? Why can't I use this controller? The assembly is definitely loaded so I don't think http://www.strathweb.com/2012/06/using-controllers-from-an-external-assembly-in-asp-net-web-api/ is the answer here.
Any ideas?
Try this. Create a class in your class library project that looks like this,
public static class MyApiConfig {
public static void Register(HttpConfiguration config) {
config.MapHttpAttributeRoutes();
}
}
And wherever you are currently calling the config.MapHttpAttributeRoutes(), instead call MyApiConfig.Register(config).
One possibility is you have 2 routes on different controllers with the same name.
I had 2 controllers both named "UploadController", each in a different namespace and each decorated with a different [RoutePrefix()]. When I tried to access either route I got a 404.
It started working when I changed the name of one of the controllers. It seems the Route Attribute is only keyed on the class name and ignores the namespace.
We were trying to solve a similar problem.
The routes within the external assembly were not registering correctly.
We found one additional detail when trying the solution shown on this page.
The call to the external assembly "MyApiConfig.Register" needed to come before the call to MapHttpRoute
HttpConfiguration config = new HttpConfiguration();
MyExternalNamespace.MyApiConfig.Register(config); //This needs to be before the call to "config.Routes.MapHttpRoute(..."
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

Categories

Resources