0

I've some "bad boys" users which call my website using controller that doesn't exist. Such as www.myapp.com/.svn/, or www.myapp.com/wp-admin/.

The app basically throw a System.Web.HttpException, which this ExceptionMessage: The controller for path '...' was not found or does not implement IController.

Is there a way to exactly catch this "sub" System.Web.HttpException in a robust way? (not comparing the string of ExceptionMessage, of course).

Any sub InnerException type and/or some "code" which indicate that exact exception?

markzzz
  • 47,390
  • 120
  • 299
  • 507
  • There are no properties on HttpException that can help you. You'll either have to figure out how to intercept the request before it becomes an exception or you're back to parsing the message. – Lasse V. Karlsen Aug 04 '21 at 10:38
  • @LasseV.Karlsen and how can I intercept it? For example, to check if the requested controller exist? – markzzz Aug 04 '21 at 10:44
  • 1
    I think you need a simple configuration inside *web.config* file. Please read the page https://stackify.com/aspnet-mvc-error-handling/ carefully. – Amirhossein Mehrvarzi Aug 08 '21 at 12:15
  • @AmirhosseinMehrvarzi this works for generic http message (i.e. 400, 401, and so on). In this case, I Need to route if controller doesn't exist – markzzz Aug 08 '21 at 14:37
  • What do you think about this topic: [How can I properly handle 404 in ASP.NET MVC?](https://stackoverflow.com/questions/619895/how-can-i-properly-handle-404-in-asp-net-mvc/620559#620559) – Antonio Leonardo Aug 11 '21 at 04:43

3 Answers3

3

You might create a custom IControllerFactory, e.g. by deriving from DefaultControllerFactory.

Its GetControllerInstance method will be called with a null value for the controllerType argument, when no matching controller could be resolved.
At this point, you are ahead of the Exception.

IController GetControllerInstance(RequestContext requestContext, Type controllerType)

Here you decide how to handle the request, like e.g. logging and/or handling the request by a specific controller.

The example below shows how the ErrorController is being used as fallback, executing its Index action method.

class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            var routeData = requestContext.RouteData;
            routeData.Values["controller"] = "Error";
            routeData.Values["action"] = "Index";
            controllerType = typeof(ErrorController);
        }

        return base.GetControllerInstance(requestContext, controllerType);
    }
}

A custom controller factory gets installed from within e.g. Application_Start in Global.asax.cs using:

ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

Note that this doesn't guard you from a related exception in case the contoller does exist, but the action doesn't.

E.g. given a HomeController without an Ufo action method, the url www.myapp.com/home/ufo results in an Exception "A public action method 'ufo' was not found on controller '...HomeController".

A simple solution to handle such scenario is by overriding HandleUnknownAction in a custom base controller of which all your controllers inherit.

The example below shows how a shared Error view (Shared\Error.cshtml) gets executed.

public abstract class BaseController : Controller
{
    protected override void HandleUnknownAction(string actionName)
    {
        View("Error").ExecuteResult(ControllerContext);
    }
}
pfx
  • 20,323
  • 43
  • 37
  • 57
0

The problem as you have stated seems like a routing problem.

Have you tried a catch all route?... That is a route that is executed when no other route matches the request's path. The catch all route is defined last and seems like:

// Specific routes here...

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

routes.MapRoute(
    "Catch-All",
    "{*controller}",
    new { controller = "Home", action = "Index" }
);

I tested with .NET Core MVC


// This is how it is configured in .NET Core MMVC
app.UseEndpoints(endpoints =>
{
   endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
   endpoints.MapControllerRoute(
            name: "catch-all",
            pattern: "{*controller}",
            defaults: new { controller="Home", action="Index" });
});

enter image description here

I think that managing the excepion, as sugested in previous answers, is a good practice, but does not solve the routing problem you are facing.

You might want to take a look to this discussion regarding similiar problem with ASP.NET MVC: ASP.NET MVC - Catch All Route And Default Route

Luis
  • 866
  • 4
  • 7
  • 1
    The code above doesn't load Home/Index if i request a random path :( (such as /wp-admin). Are you saying this? – markzzz Aug 11 '21 at 06:33
  • Updated my answer, I am testing with .NET Core (no ASP.NET MVC 4.6 available) and was able to catch-all non matching urls. Also included a link with a discussion regarding the same issue. – Luis Aug 11 '21 at 19:03
  • Instead of routing to `/home/index` you maight redirect to a 404 page. – Luis Aug 11 '21 at 19:06
  • 1
    tried, but doesn't work on .NET 4.6.1. It trigger protected void Application_Error() method, that's all. – markzzz Aug 20 '21 at 09:46
-1

Like @Amirhossein mentioned, since you are using .net 4.6 framework you can have a global exception catch inside the Global.asax file something like:

 protected void Application_Error(object sender, EventArgs e)
 { 
        var ex = Server.GetLastError();

        Debug.WriteLine(ex);

        var httpEx = ex as HttpException;
        var statusCode = httpEx?.GetHttpCode() ?? 500;

        Server.ClearError();
        ...
  }

or you can address this issue via IIS (since I presume you host your application on IIS) by adding a mapping rule (which in turn can be done in the web.config) or by adding a filter, or even a firewall rule (since by my account those kind of probing scans are not worth to be logged in your application - I believe they should be handled on another level)

theCuriousOne
  • 1,022
  • 10
  • 22