I would like to implement a simple URL shortener feature like Bitly.
My Controller name: WebController
My Action name: Redirect
As the name suggests the action redirects the user from the short URL to the full URL.
To call this action I need: https://myappdomain.com/web/redirect?id=3422
But I would like to be able to call this feature in a much shorter way with a different (shorter) domain and without the need to call the action name: https://shorterdomain.com/3422
Can you guide me how can I do this? I am a bit lost even for what to search for:(
Add a route to the shorter URL so MVC knows what controller and action will handle the request. Something like this:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "redirection",
pattern: "{id:int}",
defaults: new { controller = "web", action = "redirect" });
... your existing routes
});
Related
In our RouteConfig.cs file we have the following default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Original", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Path.To.Controllers" }
);
Our application is split into several different "Areas". This particular route works perfectly fine.
We were asked to change one of our URLs, however the underlying codebase is the same. In an effort to avoid breaking existing links out there I'd like to setup my controller to support two different routes:
Here's an example of what the original URL looks like:
website.com/MyArea/Original
With the aforementioned "Default" route in place, this will be directed to the OriginalController in the MyArea Area, and will hit the default Index action since none was specified.
My goal is to setup another URL that will also direct itself to the OriginalController. That is,
website.com/MyArea/Other
Should route to the OriginalController in the MyArea Area, and hit the default Index action.
I've added the following to the RouteConfig.cs file:
routes.MapRoute(
name: "Other",
url: "Other/{action}/{id}",
defaults: new { controller = "Original", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Path.To.Controllers" }
);
What I'm finding is that the Default route config is always used in favor of the Other route config, which causes a binding issue stating "OtherController could not be found". I suspect this is because they have a very similar signature in the url template, but I'm not entirely sure how to get around that.
I'm aware that there's a Route attribute also, which I'm not opposed to using. I unfortunately was unsuccessful in getting that setup correctly though.
After researching and attempting several different combinations I still can't get both URLs to route to one controller.
What am I doing wrong?
I was able to get the expected result using RouteAttribute on the controller itself (thank you #Michael for the resources and making me take another look at the RouteAttribute), rather than conventional MapConfig routing. As I described in my question above, I was having difficulties when attempting the Route approach in that I was receiving 404 errors stating the "resource could not be found".
It turns out the above errors were due to the fact that my attribute routing wasn't being wired up in the correct order, which forced the conventional route to be used (e.g. Default MapConfig) over my Route attributes.
I stumbled upon this SO post which states:
You are probably combining convention based routing with attribute
routing, and you should register your areas after you map the
attribute routes.
The line
AreaRegistration.RegisterAllAreas(); should be called AFTER this line:
routes.MapMvcAttributeRoutes();
When working with Areas, you must register those areas after you register the attribute routing. I was originally registering my areas in the Application_Start method of Globas.asax.cs, which is called before the RouteConfig.RegisterRoutes. I moved this registration to right below my MapMvcAttributeRoutes call in the RouteConfig.cs file, which allowed the following route attribute on the controller to work as expected:
[RouteArea("MyArea")]
[Route("Original/{action=index}", Order = 1)]
[Route("Other/{action=index}", Order = 0)]
public class OriginalController : Controller {
...
...
public async Task<ActionResult> Index() { ... }
}
With the above in place, I can now navigate to either of the below URLs which will properly route to the "Index" action of my OriginalController:
website.com/MyArea/Original
website.com/MyArea/Other
This works. However, I do have another action defined that choked up the attribute routing and caused the conventional Default route (defined via the MapConfig function) to be used. My action signature:
public async Task<ActionResult> Details(int id) {
...
}
The route to this action is: website.com/MyArea/Original/Details/123, which also satisfies the default conventional route of {area}/{controller}/{action}/{id}.
The way around this was to go a step further with defining route attributes at the action level:
[Route("Original/Details/{id:int}")]
[Route("Other/Details/{id:int}")]
public async Task<ActionResult> Details(int id) {
...
}
Now my Route Attributes are found first, then the conventional route is used.
I have a new ASP.NET Core web application with routing defined as follows:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "sitemap",
defaults: new { controller = "Sitemap", action = "Index" },
template: "sitemap.xml");
routes.MapRoute(
name: "cms",
template: "{*permalink}",
defaults: new { controller = "Content", action = "Index" },
constraints: new { permalink = new CmsRouteConstraint() });
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
When I request the URL / I see my home page served by HomeController. When I request the URL /sitemap.xml I see the XML served by SitemapController. When I request a URL for which my Match method in CmsRouteConstraint returns true I see the request being handled by ContentController.
However, all other URLs are returning a 404 response. By my understanding these URLs should still be handled by my default route and passed to HomeController but they're not. What I find strange is that regardless of whether I request URL / or /where/am/i I can see CmsRouteConstraint returning false both times, so why would the former end up being handled by HomeController and not the latter?
You need to use what you have. For exaple if you want call /yolo/swag it will be handled by deafult route but you dont have yolo controller with swag action and will return 404
Also remember about * in cms route and his catch-all route function so i reccoment use "greedy matching" at the end of route table
It is possible to add a MessageHandler only for a specific controller that is using Route Attributes?
I want to cut the request earlier in the pipeline if it doesn't contain certain headers. I want to
mention that:
I can't add another route in WebApiConfig, we must use the Routing Attributes from the controller.
I don't want to add the MessageHandler globally.
It has to be a MessageHandler (early in the pipeline). We have alternatives for this but we are trying to do this more efficient.
For example, I've decorated the controller with the following RoutePrefix: api/myapicontroller and one action with Route(""). (I know it is strange, we are selecting a different action based on querystring)
Then, I've added
config.Routes.MapHttpRoute(
name: "CustomRoute",
routeTemplate: "api/myapicontroller/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: new myMessageHandler()
);
If I put this code before config.MapHttpAttributeRoutes(); the myMessageHandler is executing but I get this Message:
No action was found on the controller 'myapicontroller' that matches
the request
If I put config.MapHttpAttributeRoutes(); first, the myMessageHandler is never executed but the my action inside myapicontroller is called.
Unfortunately, you can not set any handlers via AttributeRouting. If you want to assign handler to specific routes you have to register it via MapHttpRoute only. Except you need to add your controller name in defaults section like in Ajay Aradhya's answer and remove Route attribute from your action, because you are allowed to register routes either with Route attribute or MapHttpRoute method, but not both at the same time.
Also note that you need to create pipeline, otherwise you handler will work but request will not hit the controller action.
See my answer on similar question for details.
This article from MS docs explains the same. At last they provide a way to have controller specific handlers.But thats for the conventional routing. I don't know if this helps you or not.
config.Routes.MapHttpRoute(
name: "MyCustomHandlerRoute",
routeTemplate: "api/MyController/{id}",
defaults: new { controller = "MyController", id = RouteParameter.Optional },
constraints: null,
handler: HttpClientFactory.CreatePipeline(new HttpControllerDispatcher(config), new MyCustomDelegatingMessageHandlerA());
);
I have a HomeController.cs with some actions and can access these via http://localhost/MyWebSite/Home/Test
I'd like to shorten the URL to http://localhost/MyWebSite/Test
Therefor I added following code to the RouteConfig.RegisterRoutes method
routes.MapRoute(
name: "ShortendUrl",
url: "{action}",
defaults: new { controller = "Home", action = "Test" }
);
When trying to access it via http://localhost/MyWebSite/Test I see
HTTP-Error 403.14 - Forbidden
Where is my mistake? Is the MapRoute wrong? or is it maybe an issue at the IIS configuration?
For reference I used this blog entry: http://weblogs.asp.net/gunnarpeipman/asp-net-mvc-defining-short-urls-for-root-level-pages
I'm new to MVC and URL routing, and am looking for some guidance.
I would like to be able to have a URL per user, with the format:
http://www.example.com/username
So I added the following route:
routes.MapRoute(
"UserRoute", // Route name
"{username}", // URL
new { controller = "Users", action = "ViewUser", username = UrlParameter.Optional }
);
Which works great if it is the FIRST route, but it has messed up the default {controller}/{action}/{id} route. Now when I try to visit the root page, it tries to load my UsersController "ViewUser" action with a null parameter (or with "favicon.ico" as the parameter).
If I put my new route AFTER the default route, then MVC tries to find the controller called username and fails because it can't find it.
Is there any way to have URLs of the form {username} without mucking up the regular routing system? I could probably write a custom route for every controller I have, but that seems error-prone.
You can check out this link and see if it helps. It is also creating urls with just a username on them.
You can put a constraint to the allowed controller values for the default route, instead of creating a route for each controller.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL
new { controller = "controller1", action = "defaultaction", id = UrlParameter.Optional }, //default values
new { controller = #"controller1|controller2|controller3|..." }
);
when the controller does not match any of those it will try the next route