问题
I've currently got a BaseController class that inherits from System.Web.Mvc.Controller
. On that class I have the HandleError
Attribute that redirects users to the "500 - Oops, we screwed up" page. This is currently working as expected.
THIS WORKS
<HandleError()> _
Public Class BaseController : Inherits System.Web.Mvc.Controller
''# do stuff
End Class
I also have my 404 pages working on a Per-ActionResult basis which is again working as expected.
THIS WORKS
Function Details(ByVal id As Integer) As ActionResult
Dim user As Domain.User = UserService.GetUserByID(id)
If Not user Is Nothing Then
Dim userviewmodel As Domain.UserViewModel = New Domain.UserViewModel(user)
Return View(userviewmodel)
Else
''# Because of RESTful URL's, some people will want to "hunt around"
''# for other users by entering numbers into the address. We need to
''# gracefully redirect them to a not found page if the user doesn't
''# exist.
Response.StatusCode = CInt(HttpStatusCode.NotFound)
Return View("NotFound")
End If
End Function
Again, this works great. If a user enters something like http://example.com/user/999 (where userID 999 doesn't exist), they will see the appropriate 404 page, and yet the URL will not change (they are not redirected to an error page).
I CAN'T GET THIS IDEA TO WORK
Here's where I'm having an issue. If a user enters http://example.com/asdf- they get kicked over to the generic 404 page. What I want to do is leave the URL in tact (IE: not redirect to any other page), but simply display the "NotFound" view as well as push the HttpStatusCode.NotFound
to the client.
For an example, just visit https://stackoverflow.com/asdf where you'll see the custom 404 page and see the URL left in tact.
Obviously I'm missing something, but I can't figure it out. Since "asdf" doesn't actually point to any controller, my base controller class isn't kicking in, so I can't do it in the "HandleError" filter in there.
Thanks in advance for the help.
Note: I absolutely do not want to redirect the user to a 404 page. I want them to stay at the existing URL, and I want MVC to push the 404 VIEW to the user.
Edit:
I have also tried the following to no avail.
Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.RouteExistingFiles = False
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
routes.IgnoreRoute("Assets/{*pathInfo}")
routes.IgnoreRoute("{*robotstxt}", New With {.robotstxt = "(.*/)?robots.txt(/.*)?"})
routes.AddCombresRoute("Combres")
''# MapRoute allows for a dynamic UserDetails ID
routes.MapRouteLowercase("UserProfile", _
"Users/{id}/{slug}", _
New With {.controller = "Users", .action = "Details", .slug = UrlParameter.Optional}, _
New With {.id = "\d+"} _
)
''# Default Catch All Valid Routes
routes.MapRouteLowercase( _
"Default", _
"{controller}/{action}/{id}/{slug}", _
New With {.controller = "Events", .action = "Index", .id = UrlParameter.Optional, .slug = UrlParameter.Optional} _
)
''# Catch All InValid (NotFound) Routes
routes.MapRoute( _
"NotFound", _
"{*url}", _
New With {.controller = "Error", .action = "NotFound"})
End Sub
My "NotFound" route is doing nothing.
回答1:
Found my answer on my other SO question. Thanks very much Anh-Kiet Ngo for the solution.
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// A good location for any error logging, otherwise, do it inside of the error controller.
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "YourErrorController");
if (httpException != null)
{
if (httpException.GetHttpCode() == 404)
{
routeData.Values.Add("action", "YourErrorAction");
// We can pass the exception to the Action as well, something like
// routeData.Values.Add("error", exception);
// Clear the error, otherwise, we will always get the default error page.
Server.ClearError();
// Call the controller with the route
IController errorController = new ApplicationName.Controllers.YourErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
}
}
回答2:
You could try :
<customErrors mode="On" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="~/Error.aspx"/>
</customErrors>
http://msdn.microsoft.com/en-us/library/system.web.configuration.customerrorssection.redirectmode.aspx
If the RedirectMode property is set to ResponseRewrite, the user is sent to error page and the original URL in the browser is not changed.
回答3:
Routes are pattern matches. Your not found route isn't working because the pattern of your incorrect URL matches an earlier route.
So:
''# Default Catch All Valid Routes
routes.MapRouteLowercase( _
"Default", _
"{controller}/{action}/{id}/{slug}", _
New With {.controller = "Events", .action = "Index", .id = UrlParameter.Optional, .slug = UrlParameter.Optional} _
)
''# Catch All InValid (NotFound) Routes
routes.MapRoute( _
"NotFound", _
"{*url}", _
New With {.controller = "Error", .action = "NotFound"})
If you enter: http://example.com/asdf- then that matches the "Default" route above - MVC goes looking for asdf-Controller
and can't find it, so exception thrown.
If you go to http://example.com/asdf-/action/id/slug/extra then "Default" no longer matches and the "NotFound" route will be followed instead.
You could add a filter for all your known controllers to the "Default" route, but that's going to be a pain to maintain when you add new controllers.
You shouldn't need to hook into the Application_Error
event at all, but I haven't found a better way around the missing controller yet - I asked it as another question.
回答4:
I got this to work by simply returning the action defined in the base controller, rather than redirecting to it.
[HandleError]
public ActionResult NotFound()
{
Response.StatusCode = 404;
Response.TrySkipIisCustomErrors = true;
return View("PageNotFound", SearchUtilities.GetPageNotFoundModel(HttpContext.Request.RawUrl));
}
(The "SearchUtilities.GetPageNotFoundModel" bit is about generating suggestions by feeding the URL into our search engine)
In any action method that inherits the base, I can simply call this:
return NotFound();
... whenever I catch an invalid parameter. And the catch-all route does the same.
回答5:
Set it up in the web config:
<system.web>
<customErrors mode="On" defaultRedirect="error.html"/>
</system.web>
error.html is your custom page that you can have to display any arbitrary HTML to the user.
回答6:
It's big miss from ASP.NET MVC framework because you can't customize (real customization I mean not just-one-error-page-for-anything) 404 pages without "crunches" like 1 answer. You can use one 404 error for all not-found causes, or you can customize multiple 500 error pages via [HandleError], but you CAN NOT customize 404 errs declaratively. 500 errs are bad due to SEO reasons. 404 errs are good errs.
来源:https://stackoverflow.com/questions/3431211/asp-net-mvc-how-to-throw-a-404-page-similar-to-that-on-stackoverflow