I have an self hosted Web API project, so I had to use Yao's blog post to make help page work. Next, I've had to secure some of my methods from unauthorized use. I've implemented this idea.
Now the fun part. I have 3 routes:
/help which leads to Help Page,
/authentication/authenticate is used to call authentication method and it expects user credentials and returns security token in case of success
and /transaction/{action}/{id} this route needs to be secured from unauthorized use.
So basically, I need to make all routes, where controller = transaction, to be processed by TokenInspector.
1. Scenario: if I have routing configuration like this:
_config.Routes.MapHttpRoute(
name: "AuthenticatedOnly",
routeTemplate: "transaction/{action}/{id}",
defaults: new {controller = "Transaction", action="GetNewTaskId", id=RouteParameter.Optional},
constraints: null,
handler: tokenInspector
);
_config.Routes.MapHttpRoute(
"Default",
"{controller}/{action}/{id}",
defaults: new { controller="Help", action="Index", id = RouteParameter.Optional}
);
Everything works fine, except Help page shows only POST Authentication/Authenticate
entry
2. Scenario: if I change routing config to:
_config.Routes.MapHttpRoute(
name: "AuthenticatedOnly",
routeTemplate: "transaction/{action}/{id}",
defaults: new {},
constraints: null,
handler: tokenInspector
);
_config.Routes.MapHttpRoute(
"Default",
"{controller}/{action}/{id}",
defaults: new { controller="Help", action="Index", id = RouteParameter.Optional}
);
Help page works fine and shows all the methods, but /transaction is not secured anymore and is working without token.
3. Scenario:
_config.Routes.MapHttpRoute(
name: "AuthenticatedOnly",
routeTemplate: "transaction/{action}/{id}",
defaults: new {id=RouteParameter.Optional},
constraints: null,
handler: tokenInspector
);
_config.Routes.MapHttpRoute(
"Default",
"{controller}/{action}/{id}",
defaults: new { controller="Help", action="Index", id = RouteParameter.Optional}
);
Works both authentication and help page, but when I make request like /Transaction/GetNewTaskId with valid Token in it's header, I get 404.
Update
Could anyone explain, how help page generation depends on registered routes? Is there any way to tweak it and enforce ApiExplorer to print out controller contained stuff?
Update 2
After some more struggling and investigating, I found a solution, which matches my goal - to keep documentation as well as security pattern.
I've implemented a custom message handler (basically, I used my TokenInspector, but added url filtering to it's logic).
So, I have single route now:
_config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { controller = "Help", action = "Index", id=RouteParameter.Optional }
);
and this is how I launch the server:
_config = new ExtendedHttpSelfHostConfiguration(ServiceAddress);
TokenInspector tokenInspector = new TokenInspector() { InnerHandler = new HttpRoutingDispatcher(_config) };
_server = new HttpSelfHostServer(_config, tokenInspector);
ConfigureHost(_config);
_server.OpenAsync();
Probably, the question as it was, could not be answered in this way, but anyway, thank you all for your effort!
Regards, insomnium_
//This is for your public controllers
//this route will ONLY catch requests for Help and Authentication controllers only
//you will need to include any new public controller that uses the route pattern
_config.Routes.MapHttpRoute(
name: "Public",
routeTemplate: "{controller}/{action}/{id}",
constraints: new { controller = #"^(Help|Authentication)$" },
defaults: new { controller="Help", action="Index", id = RouteParameter.Optional}
);
//Everything that is not Help or Authentication will use this route, which will check for the valid token you mention
//This route is defaulting to /Transaction/GetNewTaskId
_config.Routes.MapHttpRoute(
name: "AuthenticatedOnly",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { controller = "Transaction", action="GetNewTaskId", id=RouteParameter.Optional},
handler: tokenInspector
);
Use this approach for more flexible method access management
config.Routes.MapHttpRoute(
name: "PublicMethods",
routeTemplate: "api/{controller}/{action}",
constraints: new {action = #"^(public)-(.)*$"},
defaults: new {controller = "Account"}
);
config.Routes.MapHttpRoute(
name: "PublicControllers",
routeTemplate: "api/{controller}/{action}",
constraints: new {controller = #"^(Environment|Account)$"},
defaults: new {controller = "Account"}
);
config.Routes.MapHttpRoute(
name: "AuthorizedUsersOnly",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: tokenInspector
);
So I have few open for each user controllers and if it's necessary I make some methods accessible for not authorized users by adding 'public' prefix to action name
Related
I have a custom route below and action.
[System.Web.Mvc.Route("Sites/{id:string}/Cache")]
public ResponseMessage<Result> DeleteCache ([FromUri] string id)
{
and when I got the the help page it gives three examples to use this call:
DELETE Sites/{id}/Cache
DELETE Sites/{id}
DELETE api/Sites/DeleteCache?id={id}
I'd like to keep the first one and remove the others. Is there a built in way to do this?
Here is my WebApiConfig.cs snippit....
config.Routes.MapHttpRoute(
name: "DeleteCache",
routeTemplate: "{controller}/{id}/Cache",
defaults: new { controller = "Sites", action = "DeleteCache" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
HelpPage will list every valid route for each controller. If you want a route to not apply to a specific controller you have to add contraints to the route to make it not match anymore :
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new {controller = "((?!Sites).)*" }
);
This uses a negative lookahead regex to match every Controllers not named Sites
I'm practicing MVC routing and now I'm stuck in a situation which I don't understand how to solve. I have two controllers and action in both controllers and two routes in RouteConfig class. Here:
RouteConfig
routes.MapRoute(
name: "Students",
url: "{Class}/{Students}",
defaults: new { controller = "Class", action = "Students" });
routes.MapRoute(
name: "SubjectDetail",
url: "{Class}/{Subject}",
defaults: new { controller = "Subject", action = "SubjectDetail"});
Now the problem is when I go to the class/Students url it works fine but in case of class/subject it again redirects me to the class/Students url. I know there is some route pattern mistakes. How to solve this issue. Thanks.
Try:
routes.MapRoute(
name: "Students",
url: "classes/{classId}/students/{studentId}",
defaults: new { controller = "Class", action = "Students" });
routes.MapRoute(
name: "SubjectDetail",
url: "classes/{classId}/subjects/{subjectId}",
defaults: new { controller = "Subject", action = "SubjectDetail"});
I need to serve POST requests to http://domain_name.com using custom code (i.e. controller)
I understand that I can configure to serve POST requests to a route like
http://domain_name.com/api/SomeController/id
using
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But I want to serve POST request to http://domain_name.com
How do I do that ?
Please do not downvote without pointing me to answer or a reason.
You can map some controller as default
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new {controller="values", id = RouteParameter.Optional }
);
By default MVC 4 application has the following routes in ProjectFolder\App_Start\RoutConfig.cs:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
In browser, any call to http://host:port/controller/ would take you to http://host:port/controller/Index.
Now, if you want to change the start page, lets say SomeController\MyAction, you can change it like:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "SomeController", action = "MyAction", id = UrlParameter.Optional }
);
But then http://host:port/controller/ won't display http://host:port/controller/Index but the 404 error page. You would need to explicitly append /Index in URL.
If you change the startup page from project properties and keep the routes to Home\Index, then everything would work fine except the URL root http://host:port/ won't display the startup page. When you run the application it will append the startup page name in the URL. But if you have relative hyperlink like Home somewhere in your application, it would not take you to the desired homepage
Is there a way to configure the startup page "SomeController\MyAction" such that; root URL always point to it and the Index be the default view of every controller?
Something equivalent to Ruby on Rails' route:
root :to => "some_controller#my_action"
which keeps the default view binding to index action.
You need to add extra route just for Site root, before default route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(name: "SiteRoot", url: "",
defaults: new { controller = "SiteRoot", action = "Main" });
routes.MapRoute(name:"Default", url:"{controller}/{action}/{id}",
defaults:new {controller = "Home", action = "Index", id = UrlParameter.Optional});
}
Now "/" will take you to SiteRoot/Main, while "/Home" will take you to Home/Index.
Note that in this case "/SiteRoot" will also take you to "SiteRoot/Index", not to "SiteRoot/Main". This can be easily changed with additional route specific for url: "/SiteRoot".
Additionally, if you want to be extra precise, in default route you can remove part controller = "Home", since 1st route now covers this case. However, leaving default route as it is will also work.
routes.MapRoute(name:"Default", url:"{controller}/{action}/{id}",
defaults:new { action = "Index", id = UrlParameter.Optional});
It sounds like you want this:
routes.MapRoute(
name: "SomeController Default",
url: "SomeController/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This is the how the default routing for ASP.NET Web API works, like this:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I'm making a Restful service in Which I have a list of items, which could be queried as such:
GET Api/Items
Which lists all items.
But of course I'd also need these items to be listed as 'most popular', or 'belonging to user x' or 'belonging to category Y'
When glancing at the stackoverflow 2.0 api to see how they solved this they named their URLS as following:
GET Api/Items/MostPopular
And this methodology I'd like to adopt as well as it does seem to make sense and looks good.
However, How can I configure Web-API to allow this URL syntax as well?
The default route is as following:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
So I would guess that I need to add my extra routing in front of it.
I could do it like this: (If this even works)
config.Routes.MapHttpRoute(
name: "SpecializedApi",
routeTemplate: "api/{controller}/MostPopular",
defaults: new { id = RouteParameter.Optional }
);
But then it would add the MostPopular bit for all my controllers which I don't like.
Does something like this work?
config.Routes.MapHttpRoute(
name: "SpecializedApi",
routeTemplate: "api/Items/MostPopular",
defaults: new { id = RouteParameter.Optional }
);
And is this really the way to go as my routing table would quickly become very big and possibly unmaintainable?
The best would be to add another get action and configure a generic route rather then a specific route.
First add action for most popular
// add action for Most Popular
[ActionName("MostPopular")]
public MyResult GetMostPopular()
{
return null;
}
Setup route to handle the action.
// Controller with ID
// To handle routes like `/api/Items/1`
config.Routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" } // Only integers
);
// Controllers with Actions
// To handle routes like `/api/Items/MostPopular`
config.Routes.MapHttpRoute(
name: "ControllerAndAction",
routeTemplate: "api/{controller}/{action}"
);
Probably the best maintainable if you do not deviate from the default to much,
however you should specify the controller and action in the route like this:
config.Routes.MapHttpRoute(
name: "SpecializedApi",
routeTemplate: "api/Items/MostPopular/{id}",
defaults: new { controller = "wheretogo",
action = "wichactiontotake",
id = RouteParameter.Optional
}
);
or this works too:
config.Routes.MapHttpRoute(
name: "SpecializedApi",
routeTemplate: "api/test/{action}/{id}.html",
defaults: new { controller = "test" }
);
Check out this link when using fake files for configurating the IIS:
http://haacked.com/archive/2008/11/26/asp.net-mvc-on-iis-6-walkthrough.aspx