Creating a Catch-All Route - asp.net-mvc-4

I've found several examples online (especially on StackOverflow) for creating a catch-all route in ASP.NET MVC, but this doesn't appear to be working for me in MVC4:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("elfinder.connector");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "CatchAll",
url: "{*url}",
defaults: new { controller = "Home", action = "CatchAll" }
);
}
With the controller action defined as:
public ActionResult CatchAll(string url)
{
// catch the URL and use it for something
}
The goal here is to intercept all potential 404 errors and, depending on the URL, either respond with a 404 or with something else. However, I never end up in the CatchAll action, I just get a default 404 for URLs like:
~/foo
~/this/is/a/test/url
Am I doing something wrong here? I'd like to avoid having to catch the errors in Application_Error and, if it's a 404, re-forward from there. It just seems kind of messy. It seems to me that the proper way to handle these requests would be to simply route them to the correct action, no?
UPDATE: While the URL purist in me wants to avoid the use of Response.Redirect, I'm currently trying the following as a global catch-all for requests:
protected void Application_Error()
{
Exception exception = Server.GetLastError();
HttpException httpException = exception as HttpException;
if (httpException != null)
if (httpException.GetHttpCode() == 404)
{
Server.ClearError();
Response.Redirect(String.Format("~/Home/CatchAll/?url={0}", Request.Path));
}
}
However, this only works if the structure of the URL matches a route. Otherwise it looks like IIS doesn't even send the request to the application, it just returns a 404 directly. I need pretty much a global handler where any URL requested of the website which doesn't map to an action gets directed.
UPDATE: It looks like a combination of two things should do the trick for me. First, catching 404s in Application_Error (or Application_EndRequest as some people suggest, either way works for my needs) covers anything that matches a route structure but doesn't find an action. However, there's still the case of requests which don't match a route structure.
For varying reasons, I don't want to have to tweak IIS for this. The solution should be contained within the application itself. Luckily, I can guarantee the latest .NET and IIS, which allows me to use Web.config settings to instruct IIS on how to handle errors. So I'm currently trying this:
<httpErrors errorMode="Custom" defaultResponseMode="Redirect" defaultPath="/Home/CatchAll">
<clear/>
</httpErrors>
However, this doesn't appear to be quite right. It is affecting IIS in that the error response has changed, but it's still an error response. Which is:
HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.
So it looks like I'm on the right track, I'm just having a difficult time finding a working example of using httpErrors in the Web.config to send all 404s (or even all errors, it makes little difference for my needs) to an action.

Related

Adding Attribute Route on Controller adds Parameter to Url

I want to change the URL that will access my controller, but when I do, instead of getting controller/action/id, I get controller/action?id=(the id number) in the URL.
I am using default routing in my MVC .Net Core.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
On my controller I have [Route("DifferentName/{action=index}")] for attribute routing.
I have tried adding [Route("DifferentName/{action=index}/{id?}")]
but I get an exception "RoutePatternException: The route parameter name 'id' appears more than one time in the route template."
These attributes are incorrect according to the documentation:
[Route("DifferentName/{action=index}/{id?}")]
[Route("DifferentName/{action=index}")]
This is how it should look like for controller when we want to only change its part of URL:
[Route("DifferentName/[action]")]
[action]
[action] will be replaced by your action name when asp .net core will be resolving URLs.
More information about routing and can be found in the documentation
but I get an exception "RoutePatternException: The route parameter
name 'id' appears more than one time in the route template."
I have reproduced your error. I need to confirm with you whether the reason for your error is to add Route attribute to both controller and action.
If so, as the exception points out, you don't need to specify route repeatedly.
Modify the following settings to implement the url like 'Differentname/action/5':
[Route("DifferentName/{action=index}")]
public class ShowPdfController : Controller
{
public IActionResult Index()
{
return View();
}
[Route("{id?}")]
public IActionResult Action(int id)
{
return View();
}
}
Then, you can see the test result of this setting:

How to create custom Routing in ASP.NET Core

I need help with URL rewriting. I am new to ASP.NET Core MVC. When I type anything in {param} part then routing should redirect it to my controller.
So if anyone to types in {param} like
https://mydoamin.com/{param}
then it should be redirected to this url:
https://mydoamin.com/{controller}/{action}/{actionurl}={param}
I found the answer for my question. Just define new custom route in your startup.cs file before your default route.
routes.MapRoute(
"Member", // Route name
"{actionURL}", // URL with parameters
new { controller = "Pages", action = "Details" } // Parameter defaults
);
It's working form me.
We can do the same using this approach.
I find this more convenient.
[Http("add/user/{user}/{password}")]
public IActionResult AddUser(string user, string password)
{
//do your things here...
}
I could recommend you to see this blog post from Stephen Walther: ASP.NET 5 Deep Dive: Routing
I am not sure if this works as you want. https://mydoamin.com/{controller}/{action}/{actionurl}={param} seems not to be a valid URL to me. The part {actionurl}={param} is probably the query part which is comes as a key/value pair and starts always with a ?. You could probably fix your routing if your desired URL would look like https://mydoamin.com/{controller}/{action}/?key1=value1&key2=value2
I don't know still this is useful OR not but in asp.net core 3 this worked for me like a charm.
routes.MapRoute(
"MoviesByReleaseDate",
"movies/released/{year}/{month}",
new { controller = "Movies", action = "ByReleaseDate" }
);

Asp.Net Web API action string route mapping

I have a contoller named customer and an action method for GetAllCustomers which recieves single string input parameter as FirstName. The issue is that I want the url to be like
MyAPI/Customer/MyFirstName
but the above stated URL is not working for me instead it working as below
MyAPI/Customer?firstname=MyFirstName
How can I make it work like the first url using ApplicationRouting (nuget package)? I have configured the attrbute route for the action method as below.
[GET("Customer/{firstname}")]
public List<Customer> GetAllCustomersFirstname(String firstname)
{
//code goes here
}
Edit
Here is the reference whatI am trying to achieve but it is not happening for me in ApiController
Attribute Routing Github
Open up ApiConfig in the AppStart folder. You need to add the route for that particular action. I think you'll need more than just what you showed there for route since you have to tell it which action to call and what methods to allow. E.g.
config.Routes.MapHttpRoute(
name: "yourRoute",
routeTemplate: "api/{controller}/{firstname}",
defaults: new { controller = "Customer", action = "GetAllCustomersFirstname" });

URL rewrite in ASP.NET 4.5 and Web API

We've got a long-running ASP.NET web-forms application that was born in the .NET 1.1/IIS6 days. We're now on .NET4.5/IIS7 but we've done nothing with MVC.
We provide a catalog to customers and give them a URL they can use:
www.ourhost.com/customername
Using a custom IHttpModule we developed we pull 'customername' out of the URL to find the customer in the database. That customer's ID is then stored in the page's context* and used by virtually all the pages on the site to customize content for that customer. After this process, the above URL would be rewritten and processed as
www.ourhost.com/index.aspx
with index.aspx having access to the customer's ID via its context and it can do its thing.
This works great and we support several thousand customers with it. the rewriting logic is fairly complex because it validates customer accounts, redirects to a 'uh oh' page if the customer is invalid and to a different 'find a dealer' page if the customer has not paid, etc. etc.
Now I'd like to build some Web API controllers and MVC-style rewriting has me worried. I see many examples where rewriting happens to make URL's like this work:
www.ourhost.com/api/{controller}
but I still need these web api 'calls' to happen in the context of a customer. Our pages are getting more sophisticated with JSON/AJAX async calls but in answering those calls I still need customer context. I would like the URL's to be
www.ourhost.com/customername/api/{controller}
But I am stumped as to how to configure routing to do this and have it play nicely with our IHttpModule.
Is this even possible?
*UPDATE: When I say 'stored in the page context' I mean the HttpContext associated with each web request that includes a dictionary where I can store some page/request-specific data.
There are two parts of the answer to your issue that I can see.
Maintaining the User Info across multiple requests
Generally an MVC API application will be stateless, that is you do not retain the current users session state between requests. Well that is what I have learned or been preached many times when writing RESTFul APIs.
That been said, you can enable session state in MVC Web API by adding the following to your global.asax.cs
protected void Application_PostAuthorizeRequest()
{
// To enable session state in the WebAPI.
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
Authorising A Customer in the Request
As you have shown in the Request URL you could add the customer name, then capture that and pass it to the same routine that your current http module calls to authorise on request. You could do this with an MVC Filter.
First do a similar URL Pattern to capture your customers name in the WebApiConfig.cs, something like so;
config.Routes.MapHttpRoute(
name: "WithCustomerApi",
routeTemplate: "api/{customername}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Then add an ActionFilter to your API Controller which processes each request, checks current session info and if needed calls your authorise/customer lookup code and then saves to session state for later use. Or if no good info from customer can send to a new MVC route
So you will add an attribute something like so;
[WebApiAuthentication]
public class BaseApiController : ApiController
{
}
Then create an action filter that might look like this (note I have not tested this, just done for a pattern of how to).
public class WebApiAuthenticationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var routeData = actionContext.ControllerContext.Request.GetRouteData();
var currentContext = HttpContext.Current;
if (routeData.Route.RouteTemplate.Contains("customername"))
{
try
{
var authenticated = currentContext.Request.IsAuthenticated;
if (!authenticated)
{
var customer = routeData.Values["customername"];
// do something with customer here and then put into session or cache
currentContext.Session.Add("CustomerName", customer);
}
}
catch (Exception exception)
{
var error = exception.Message;
// We dont like the request
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
else
{
// No customer name specified, send bad request, not found, what have you ... you *could* potentially redirect but we are in API so it probably a service request rather than a user
actionContext.Response = new HttpResponseMessage(HttpStatusCode.NotFound);
}
}
}
If you create a new MVC 5 Web API Application and add in these extras and put the filter on the default values controller like so you should be able to see this running as demo of a possible solution.
This will echo the customer name back if all works ok.
[WebApiAuthentication]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
var session = HttpContext.Current.Session;
if (session != null)
{
return new string[] {"session is present", "customer is", session["CustomerName"].ToString()};
}
return new string[] { "value1", "value2" };
}
}
I offer this up as a possible solution as I say, there are religious arguments about storing session and authorising in an API but those are not the question.
Hope that helps,
Steve

MVC2 Routing with WCF ServiceRoute: Html.ActionLink rendering incorrect links!

I have a WCF service that lives side-by-side with an MVC2 web site. I'd like for my URL for the service to look like this:
http://localhost/projdir/Service
The MVC site is in its infancy so it still has all its boilerplate controllers etc.
The following code works at first glance in global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new ServiceRoute("Service", new ServiceHostFactory(),
typeof(MyService)));
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
}
The service appears just where I described and works as advertised. Great.
However, I just noticed that ordering my code this way changes all of my ActionLink's. For example, the "About" tab on the MVC site now appears like this:
http://localhost/projdir/Service?action=About&controller=Home
This is obviously incorrect (it should be http://localhost/projdir/Home/About/ ).
If I move the ServiceRoute addition below the default MapRoute() call, then I get a missing controller error. (Actually I get a "StructureMapControllerFactory did not return an instance of a controller" error, because I'm wired up with StructureMap, duh, it's not a controller to begin with.)
Interestingly, it only seems to be affecting the output of Html.ActionLink(). I can manually type in http://localhost/projdir/Home/About/ and get to the correct page.
What horribly obvious newbie mistake am I making?
Try moving the Service route after the MVC route. But to avoid the "missing controller" error that you got before, add the MVC route with a Route Constraint. These route constraints can be Regex - basically you'd want your route constraint to be any controller that is not "Service". When a request for "Service" is requested, it will make it fall through and his the WCF Service Route.
I resolved with that:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "^(?!api).*" }
);
routes.Add(new ServiceRoute("api", new DataServiceHostFactory(), typeof(dwService)));
I hope this good for you
Another solution is to inherit the ServiceRoute and override the GetVirtualPath method to return null as described here
public class AppServiceRoute : ServiceRoute
{
public AppServiceRoute(string routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
: base(routePrefix, serviceHostFactory, serviceType)
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
This way, reverse route mapping never select this route for any Action. Works like a charm