I was wondering if it's possible to throw a 404 error from within a page(code behind)? Or possibly even throw any other type of error page such as 408(timeout) or 401(authentication required)?
Note: I don't just want to have the page return a status code of 404, I want it to use the ASP.Net(or my CustomErrors) 404 error page.
Something like this in the code behind:
if(id>10){ //if id is greater than 10, then it doesn't exist here
throw 404Error();
}
You could throw an HttpException and set the corresponding status code:
throw new HttpException(404, "Not found");
It will also work with other status codes. Just a remark about the 401: as you probably know when ASP.NET MVC detects this code it automatically redirects you to the login page and getting a custom error page for the 401 status code could be a real PITA to implement.
A much better way is:
// Throws a 404 Not found
Response.Clear();
Response.StatusCode = 404;
Response.End();
There is no need to throw an exception and the above works much better when using custom error pages in the Web.Config
In regards of Page Load in ASP.NET WebForms needs some small workaround.
protected void Page_Load(object sender, EventArgs e)
{
HttpNotFound();
}
private void HttpNotFound()
{
Response.Clear();
Response.StatusCode = 404;
Response.End();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
If your client is capable of hitting the PHP page so code is executed, it means that you didn't get a 404 (client side error, "path/file not found"), nor did you get any other sort of 400-class error, BUT you are instead actually getting what should be classified as a 500 error of some sort.
The HTTP spec uses file/path as synonyms. This comes from the old days of the internet when it was super common for webservers to expose directory browsing lists.
Related
I am developing a standalone .Net Core API targeting framework .Net Core 2.2.The authentication scheme is JWTBearerTokens connecting to our ADFS Identify server.
When I call an API endpoing decorated with the [Authorize] attribute I am getting a 401 Unauthorized response, which is expected and default behaviour.
What I want to do next is instead of having that same call return a 401, I would like to return the status code to be 404. (I don't want to get into great details of why 404. Simply, I do not want to expose that the endpoint exists if a valid token is not included in request)
In previous .Net Framework WebAPI you could create your own attribute and override the HandleUnauthorizedRequest method and return the status code you want.
I have reviewed the documentation on policy-based authorization, but have not tried the sample or tried implementing it. The policy handler looks more to do with handling (return success or fail) if a policy is not fulfilled. I do not see anywhere where you can return a different status code on failure. So that only would make sense if I start checking against actual Policies.
Any insights?
Returning 404 instead of 401 is bad practice(as mentioned in the comments by #Chris Pratt) and must be avoided. Consider these cases,
You're leaving the project to someone else and they can't figure why 404 is returned
A 404 is returned when you call the homepage/Index page. Poor ideology.
Later on in the project, you decide to allow post requests without authentication. So on and so forth.
Anyways, as part of the community, I'll give you the answer...
Add this to your global.asax
void Application_EndRequest(object source, System.EventArgs args)
{
if (Response.StatusCode == 401)
{
Response.ClearContent();
Response.RedirectToRoute("ErrorH", (RouteTable.Routes["ErrorH"] as Route).Defaults);
}
}
And in routeConfig, create a route for your errorHandler :
routes.MapRoute(
"ErrorH",
"Error/{action}/{errMsg}",
new { controller = "CustomController", action = "Change401To404", errMsg = UrlParameter.Optional }
);
And in your custom controller :
public class CustomController : Controller //or Base
{
public ActionResult Change401To404(){
//Do whatever you want
}
}
PS: This is not the only way, there are many other ways to do it. But at least in this method, you can differentiate real 404 responses from 401 responses.
I'm running in the Cassini developer server inside Visual Studio 2012, and I need to redirect clients from the legacy .asp pages to .aspx pages.
Note: Ideally I would redirect clients from .asp to a friendly url, and then internally do a rewrite to .aspx
POST /ResetClock.asp
HTTP/1.1 307 Temporary Redirect
Location: //stackoverflow.us/ResetClock
And then internally:
POST /ResetClock
rewrites into /ResetClock.ashx (Yes, I changed it to .ashx; that's the virtue of url rewriting).
Like what Hanselman did
This is a lot like what Scott Hanselman did:
Request for /foo.html
gives the client a redirect to /foo
client request for /foo
is re-written into /foo.html
The attempted hack
I tried the hack solution; alter the .asp page to force a redirect to the .ashx (and live to fight with the url re-write syntax another day):
ResetClock.asp
<%
Response.Redirect("ResetClock.aspx")
Response.End
%>
Except that Cassini does not serve .asp pages at all:
This type of page is not served.
Description: The type of page you have requested is not served because it has been explicitly forbidden. The extension '.asp' may be incorrect. Please review the URL below and make sure that it is spelled correctly.
Requested URL: /WebSite/FetchTimes.asp
Which points to a related issue. The solution I end up using cannot require anything that isn't already available on the IIS7.5. And it cannot require anything that needs access to the IIS Admin tools; and must exist entirely within the web-site (e.g. the web.config).
The question
How do I re-write .asp into something more ASP.net-ish?
Edit: Changed GET to a POST to thwart nitpickers who wonder why the 307 Temporary Redirect and not 302 Found or 303 See Other.
The solution is to create an IHttpModule. HttpModules let you intercept every request, and react as you desire.
The first step is to create the plumbing of an IHttpModule:
class UrlRewriting : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(this.Application_BeginRequest);
application.EndRequest += new EventHandler(this.Application_EndRequest);
}
public void Dispose()
{
//Nothing to do here
}
private void Application_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
}
private void Application_EndRequest(object sender, EventArgs e)
{
}
}
And then register our HttpHandler in the web.config file:
web.config:
<configuration>
<system.web>
<httpModules>
<add name="UrlRewriting" type="UrlRewriting"/>
</httpModules>
</system.web>
</configuration>
Now we have a method (Application_BeginRequest) that will run every time a request is made.
Issue client redirect if they ask for ASP page
The first order of business is redirect the client to a "clean" form. For example, a request for /File.asp is redirected to /File:
private void Application_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
//Redirct any requests to /File.asp into a /File
if (context.Request.Url.LocalPath == VirtualPathUtility.ToAbsolute("~/File.asp"))
{
//Be sure to issue a 307 Temporary Redirect in case the client issued a POST (i.e. a non-GET)
//If we issued 302 Found, a buggy client (e.g. Chrome, IE, Firefox) might convert the POST to a GET.
//If we issued 303 See Other, the client is required to convert a POST to a GET.
//If we issued 307 Temporary Redirect, the client is required to keep the POST method
context.Response.StatusCode = (int)HttpStatusCode.TemporaryRedirect;
context.Response.RedirectLocation = VirtualPathUtility.ToAbsolute("~/File");
context.Response.End();
}
}
And then the internal rewrite
Now that the client will be asking for /File, we have to re-write that internally to an .aspx, or in my case, an .ashx file:
private void Application_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
//Redirct any requests to /ResetClock.asp into a /File
if (context.Request.Url.LocalPath == VirtualPathUtility.ToAbsolute("~/ResetClock.asp"))
{
//Be sure to issue a 307 Temporary Redirect in case the client issued a POST (i.e. a non-GET)
//If we issued 302 Found, the buggy client might convert the POST to a GET.
//If we issued 303 See Other, the client is required to convert a POST to a GET.
//If we issued 307 Temporary Redirect, the client is required to keep the POST method
context.Response.StatusCode = (int)HttpStatusCode.TemporaryRedirect;
context.Response.RedirectLocation = VirtualPathUtility.ToAbsolute("~/ResetClock");
context.Response.End();
}
//Rewrite clean url into actual handler
if (context.Request.Url.LocalPath == VirtualPathUtility.ToAbsolute("~/ResetClock"))
{
String path = "~/ResetClock.ashx"; //no need to map the path
context.Server.Execute(path, true);
//The other page has been executed
//Do not continue or we will hit the 404 of /ResetClock not being found
context.Response.End();
}
}
IIS contains some basic url redirection
Starting with some unknown version of IIS, they added a (now mocked) form of URL Rewriting. It doesn't issuing a client redirect, only an internal re-write. But at least it could be used to solve my problem (responding to an ASP page with ASP.net content):
web.config
<configuration>
<system.web>
<urlMappings>
<add url="~/ResetClock.asp" mappedUrl="~/ResetClock.ashx"/>
</urlMappings>
</system.web>
</configuration>
The client will still appear to have found a resource at /ResetClock.asp, but the guts of the response will have come from /ResetClock.ashx.
Note: Any code is released into the public domain. No attribution required.
I'm struggling to understand how to correctly handle errors in ASP.NET MVC4. As an example, I've created a new MVC4 project using the "Internet Application" template and updated my home controller to test out some error cases:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Hello";
return View();
}
public ActionResult About()
{
throw new HttpException(401, "Not Authorized");
}
public ActionResult Contact()
{
throw new Exception("Oh no, some error occurred...");
}
}
I have enabled customErrors in my web.config file:
<customErrors mode="On"></customErrors>
When I run the app and click "Contact", I see the ~/Views/Shared/Error.cshtml view as expected, since I have the HandleErrorAttribute registered as a global filter.
However, when I click "About", I get the standard ASP.NET yellow error page that says "Runtime Error". Why are these two exceptions being handled differently and how can I get instances of HttpException to get caught using the HandleError attribute?
CustomErrors config
Ideally, I'd like custom error pages for the following:
A custom 404 (not found) page that's nice and user friendly
A custom 401 (not authorised) page that informs the user that they do not have access (e.g. thrown after checking permissions for a particular item in the model)
A generic error page that is used in all other cases (in place of the standard yellow ASP.NET page).
I've created a new "Error" controller with views for each of the scenarios above. I have then updated customErrors in web.config like so:
<customErrors mode="On" defaultRedirect="~/Error/Trouble">
<error statusCode="404" redirect="~/Error/NotFound"></error>
<error statusCode="401" redirect="~/Error/NotAuthorized"></error>
</customErrors>
The 404 page works fine, but I don't get the 401 page at all. Instead, I get the ~/Error/Trouble view (the one specified as the defaultRedirect) when I try to access the About action on the Home controller.
Why is my custom 401 redirect page not working?
ASP.NET uses 401's internally to redirect users to the login page. Wherever you were planning to throw a 401 unauthorized, instead throw a 403 forbidden.
If you really need to return a 401 and not a 403, then you can use:
HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true
I had a similar problem where I could not get 401 errors to go to my page despite the change to the web.config.
For a 401 you will probably be seeing the standard 401 Unauthorised page, even if you have added 401 to the customerrors section in your web.config. I read that when using IIS and Windows Authentication the check happens before ASP.NET even sees the request, hence you see it's own 401.
For my project I edited the Global.asax file to redirect to a route I had created for 401 errors, sending the user to the "Unauthorised to see this" view.
In the Global.asax:
void Application_EndRequest(object sender, System.EventArgs e)
{
// If the user is not authorised to see this page or access this function, send them to the error page.
if (Response.StatusCode == 401)
{
Response.ClearContent();
Response.RedirectToRoute("ErrorHandler", (RouteTable.Routes["ErrorHandler"] as Route).Defaults);
}
}
and in the Route.config:
routes.MapRoute(
"ErrorHandler",
"Error/{action}/{errMsg}",
new { controller = "Error", action = "Unauthorised", errMsg = UrlParameter.Optional }
);
and in the controller:
public ViewResult Unauthorised()
{
//Response.StatusCode = 401; // Do not set this or else you get a redirect loop
return View();
}
First of all, quickly what exactly I want to achieve: translate particular exception into the HTTP 404 so the ASP.NET can handle it further.
I am handling exceptions in the ASP.NET (MVC2) this way:
protected void Application_Error(object sender, EventArgs e) {
var err = Server.GetLastError();
if (err == null)
return;
err = err.GetBaseException();
var noObject = err as ObjectNotFoundException;
if (noObject != null)
HandleObjectNotFound();
var handled = noObject != null;
if (!handled)
Logger.Fatal("Unhandled exception has occured in application.", err);
}
private void HandleObjectNotFound() {
Server.ClearError();
Response.Clear();
// new HttpExcepton(404, "Not Found"); // Throw or not to throw?
Response.StatusCode = 404;
Response.StatusDescription = "Not Found";
Response.StatusDescription = "Not Found";
Response.Write("The whole HTML body explaining whata 404 is??");
}
The problem is that I cannot configure default customErrors to work with it. When it is on then it never redirects to the page specified in customErrors: <error statusCode="404" redirect="404.html"/>.
I also tried to raise new HttpExcepton(404, "Not Found") from the handler but then the response code is 200 which I don't understand why.
So the questions are:
What is the proper way of translating AnException into HTTP 404 response?
How does customErrors section work when handling exceptions in Application_Error?
Why throwing HttpException(404) renders (blank) page with success (200) status?
Thanks,
Dmitriy.
In a few words, you if you manually set HTTP status in Application_Error, you lose possibility to use customErrors section handler, since you call Server.ClearError().
Handle the exception before Application_Error or derive the exception from HttpException.
What is the proper way of translating AnException into HTTP 404 response?
It's better to handle exceptions in Controller. You can introduce base class controller and handle most of exceptions in custom HandleError attribute. You can throw HttpException their and it will be properly handled by customErrors section handler.
You can also derive your ObjectNotFoundException exception from HttpException(404)
Application_Error is the last chance to handle an exception. You have only Response API to handle it. You can manually set status code and write to response or manually trigger redirect to custom error page or call Server.TransferRequest() to existing html or aspx file (not to the controller action). In current asp.net version, you cannot set or change Server.GetLastError in Application_Error method only retrieve or clear it.
How does customErrors section work when handling exceptions in Application_Error?
By calling Server.ClearError() you also clean current request error therefore it is not handled by customErrors section handler
Why throwing HttpException(404) renders (blank) page with success (200) status?
You should no throw any exception in Application_Error method. Exception means that your error handling failed.
In my page load I check something, and I want to display the standard Access Denied message without going through the hassle of creating a page to redirect to.
Is there an easy way?
You want to return a HTTP return code of 401 ("access denied")
So,
Response.Clear();
Response.StatusCode = 401;
Response.End();
should do the trick
I'd do something like
throw new HttpException(401, "Access Denied");
Response.Clear()
Response.Write("Access Denied")
Response.StatusCode = 401;
Response.End()
To send this before any other output, you have a couple of options.
One, create a HTTPHandler module, add this to web.config. This sees ASP.NET pipeline events before the page gets control, so if you can determine at that point that you don't want the page to run, you can send the 401 before the page does anything. The handler inherits IHTTPHandler, so start searching on that interface definition for documentation.
Second, you can hook into the page events PreInit and Init, and send the 401 before the Page Load method starts. Since the page class is created by this time, you can also set a flag in the page object that other methods can check to see if the page is responding with 401.
HttpResponseMessage response = new HttpResponseMessage();
try{
//try smth
}
catch (UnauthorizedAccessException UAEx){
response = Request.CreateResponse(HttpStatusCode.Unauthorized, UAEx.Message);
}
return response;