HTML resources that need authentication and returning 404 for non-existent resources

I want to be able to return a 404 page when a resource doesn’t exist. At the same time, if a resource requires authentication, I want to be able to redirect the user to the login page. If I add code in RawHttpHandlers, do I need to check if a resource exists and handle 404 myself? What is the best way to do this?

Currently, I’m doing something like this:

// This should handle all 404's
CustomErrorHttpHandlers[HttpStatusCode.NotFound] = new CustomStaticFileHandler("/404.html");

 RawHttpHandlers.Add(httpReq =>
        {
            // Because of this, the above won't handle the 404 if my incorrect path has the extension in it
            // which is why i handle it in the code below inside this block
            if (httpReq.PathInfo.EndsWith(".html"))
            {
                if (!files.Any(s => httpReq.PathInfo.ToLower().Contains(s)))
                {
                    var session = httpReq.GetSession();
                    if (!session.IsAuthenticated) 
                    {
                        var file = HostContext.VirtualPathProvider.GetFile(httpReq.PathInfo);
                        if (file == null)
                        {
                            return new CustomStaticFileHandler("/404.html");
                        }

                        if (httpReq.Headers.GetValues(httpReq.Headers.AllKeys[10])[0] != "XMLHttpRequest")
                        {
                            session.ReferrerUrl = httpReq.PathInfo;

                            return new RedirectHttpHandler
                            {
                                AbsoluteUrl = "/Login.html",
                            };
                        }

                        return new CustomResponseHandler((request, response) => HttpError.Unauthorized("User is unauthorized"));
                    }
                }
            }

Redirecting to a login page is automatically supported by AuthFeature.HtmlRedirect which by default redirects to ~/login when an unauthenticated user tries to access an [Authenticate] Service.

If you enable the ServiceStack Razor plugin it will redirect to /login.cshtml, otherwise you can change what AuthFeature.HtmlRedirect redirects to.

You can redirect non-authenticated users accessing a Razor page with:

@{
    RedirectIfNotAuthenticated();
}

See EditTestPlan.cshtml in HttpBenchmarks for an example. You can display different content depending if a user Is Authenticated or not (i.e. show a login form) with:

@if (IsAuthenticated)
{
    //...
}

See this StackOverflow answer for redirecting 404 pages to a custom handler, i.e. you can redirect to /404.cshtml with:

CustomHttpHandlers[HttpStatusCode.NotFound] =  new RazorHandler("/404");

Or a static /404.html page with:

CustomHttpHandlers[HttpStatusCode.NotFound] = new CustomStaticFileHandler("/404.html");

Thanks, I’m not using Razor and the redirection is to another page - I had changed the name for this post.

Any idea why throwing an exception shows the default ASP.NET 404 page, but if SS handles it directly (without going into my handler), it returns the correct 404 page?

Here’s my code:

CustomErrorHttpHandlers[HttpStatusCode.NotFound] = new CustomStaticFileHandler("/404.html");
CustomErrorHttpHandlers[HttpStatusCode.InternalServerError] = new CustomStaticFileHandler("/500.html");

//RawHttp Handler contains the code below:
var file = HostContext.VirtualPathProvider.GetFile(httpReq.PathInfo);
if (file == null)
{
    throw new HttpException((int)HttpStatusCode.NotFound, "Not Found");
    
    //The line below will redirect correctly
    //return new CustomStaticFileHandler("/404.html");
}

Because you can’t throw exceptions in a Handler, you need to write to the HTTP Response directly.

Suspected it was something on those lines. Thanks for clarifying and your quick response!