Sitecore redirect on errors - error-handling

I know that I can extend Sitecore.Pipelines.HttpRequest.ExecuteRequest and override methods like RedirectOnItemNotFound to redirect to my custom 404 page etc. I was wondering if there is way to redirect to a custom page (that would sit in sitecore) for all errors except 404 and 500?
There is a RedirectOnNoAccess method for 403 error I guess, but I am looking for way to redirect on all errors like 400, 401, 403, 405 etc.
Sitecore v7.2
Cheers

You don't need to extend the ExecuteRequest processor, there are settings in the Sitecore section of config to handle these:
<!-- ITEM NOT FOUND HANDLER
Url of page handling 'Item not found' errors
-->
<setting name="ItemNotFoundUrl" value="/sitecore/service/notfound.aspx"/>
<!-- LINK ITEM NOT FOUND HANDLER
Url of page handling 'Link item not found' errors
-->
<setting name="LinkItemNotFoundUrl" value="/sitecore/service/notfound.aspx"/>
<!-- LAYOUT NOT FOUND HANDLER
Url of page handling 'Layout not found' errors
-->
<setting name="LayoutNotFoundUrl" value="/sitecore/service/nolayout.aspx"/>
<!-- ACCESS DENIED HANDLER
Url of page handling 'Acess denied' errors
-->
<setting name="NoAccessUrl" value="/sitecore/service/noaccess.aspx"/>
Update these values to point to the correct path. This can be a Sitecore item path, e.g. /errors/404 as long as that item exists in Sitecore. It's slightly annoying that a url parameter is added to the path, you will need to extend the processor if you want to get rid of this though. If you have a multi-site implementation then this will still work but you need to make sure that the structure is the same for all sites, since you are using a relative path. The error manager module is essentially a wrapper around these same settings, but it is better in that it is able to handle multi-site and shows the error page without making a 302 redirect first.
If you need to handle other errors then fallback to using the errors section in config to define those. The values can also be set through IIS (although it just updates the web.config anyway)
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" defaultResponseMode="ExecuteURL" defaultPath="/errors/404">
<remove statusCode="404" subStatusCode="-1" />
<remove statusCode="405" subStatusCode="-1" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="404" prefixLanguageFilePath="" path="/errors/404" responseMode="ExecuteURL" />
<error statusCode="405" prefixLanguageFilePath="" path="/errors/405" responseMode="ExecuteURL" />
<error statusCode="500" prefixLanguageFilePath="" path="/errors/static/500.html" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
http://www.iis.net/configreference/system.webserver/httperrors
https://msdn.microsoft.com/en-us/library/ms690497(v=vs.90).aspx
These can be in Sitecore by setting the URL path of an Item or static HTML files on disk, and again it works in multi-site as long as the structure is the same for all sites since the path can be relative. It is generally recommended that the 500 page is a static HTML page otherwise there is the possibility of an infinite loop (e.g. database goes down, show 500, fetch content from Sitecore, but database is down...).
Even if you use the Error Manager module, or use the Sitecore settings, I recommend that you have a 404 and 500 page defined in config. By default Sitecore will only handle dynamic and extentionless URL requests, so if a request is made for /file.txt, /style.css, /script.js or /document.pdf then you will get a standard IIS error page.
<preprocessRequest>
<processor type="Sitecore.Pipelines.PreprocessRequest.FilterUrlExtensions, Sitecore.Kernel">
<param desc="Allowed extensions (comma separated)">aspx, ashx, asmx</param>
<param desc="Blocked extensions (comma separated)">*</param>
<param desc="Blocked extensions that stream files (comma separated)">*</param>
<param desc="Blocked extensions that do not stream files (comma separated)"></param>
</processor>
</preprocessRequest>
You could allow all requests to go through Sitecore but this seems a bit heavy handed and you're making it run through additional pipelines. Setting the above will mean your static content is also gracefully handled.

You can definitely use the execute request pipeline to handle 403 and 401 errors as this pipeline is called early enough.
There is a great module that already does this on the marketplace, which you may be able to adapt to your needs.
https://marketplace.sitecore.net/en/Modules/Sitecore_Error_Manager.aspx
http://ctor.io/handling-404-and-other-errors-with-sitecore-items/

Related

Trouble with URL rewrite using Vue Router HTML5 History Mode on Azure Web App

I serve a Vue SPA from an Azure Web App. There is no server side logic (or none relevant to this problem anyway).
I am using Vue Router in HTML5 history mode. It works really well. The only problem is what happens when a user tries to access a view inside the app with a "direct" URL such as https://my.app.com/contacts. This gives a 404 Not Found, as expected.
The workaround is well known: use URL Rewriting to route such requests to the root of the app: / or /index.html.
So that's what I'm trying to do. For Azure Web Apps, the way to do it is using rewrite rules in a web.config file. My Web.config looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Handle History Mode and custom 404/500" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
This is lifted straight out of the documentation for Vue Router, here.
When I access the site now, I get a blank page. Using the developer console (in Chrome) and checking the network tab, I discover that all requests now return the content of index.html, including script files and css files.
So the rewriting clearly works, just a little too much.
The problem seems to be the (negated) IsFile and IsDirectory conditions. These use the REQUEST_FILENAME server variable. From this page I understand that this is supposed to be the mapped path on the server file system.
As all requests are rewritten, it must be because the conditions fail to recognize the paths as valid file or directory paths.
Edit:
I have used failed request tracing to debug the url rewriting, and have learned some more. It turns out that when the rewrite module runs, the REQUEST_FILENAME variable is incorrect.
In the azure web app, the content root is at D:\site\wwwroot. In this folder is my app's wwwroot folder. Some googling reveals that this is as expected. Also, the mapping works correctly - with no url rewriting enabled, an url path like /img/logo.png will return the static file at D:\site\wwwroot\wwwroot\img\logo.png.
But when the url rewrite module runs, REQUEST_FILENAME will have the value D:\site\wwwroot\img\logo.png. That file does not exist, and so the IsFile condition fails.
The question is then: why does the url rewrite module and the static file provider disagree on the path mapping?
I have finally figured this out.
Although ASP.NET Core apps use IIS, they no longer let IIS serve static files. Static files are kept in a subdirectory (wwwroot) and are served by the static files middleware, configured in Startup.cs like this
app.UseDefaultFiles();
app.UseStaticFiles();
The static files middleware knows where the static files are located (this can be configured otherwise if you like), and will serve requests that point to any file in that directory.
In Azure Web Apps, for historical reasons, the content root, where your .NET Core assemblies and web.config reside, is at D:\home\site\wwwroot by default, so for a request to:
http:/myapp.com/img/dang.png
the middleware will check if the file:
D:\home\site\wwwroot\wwwroot\img\dang.png
exists (yes, that's two wwwroots).
IIS, however, is unaware of this. So as far as IIS is concerned, the url:
http:/myapp.com/img/dang.png
resides at:
D:\home\site\wwwroot\img\dang.png
That is why the {REQUEST_FILENAME} IIS variable misses its mark, and why IsFile and IsDirectory will fail.
So, to sum up: IIS URL rewriting still works in Azure Web Apps, but any matching depending on url-to-file mapping will fail.
Happily (I have now discovered), there exists an alternative - the URL Rewriting Middleware. This is an ASP.NET Core component available "out of the box" as part of the Microsoft.AspNetCore.App metapackage. It lets you do all the things IIS URL Rewriting lets you do (and more, as it allows for extensions with arbitrary coded logic), but gets it right with static files. The documentation is here.
Here is the rewrite rule I created:
public class Html5HistoryRewriteRule : IRule
{
private readonly string _rewriteTo;
private readonly Regex _ignore;
public Html5HistoryRewriteRule(string rewriteTo = null, string ignore = null)
{
_rewriteTo = rewriteTo ?? "/";
_ignore = ignore != null ? new Regex(ignore) : null;
}
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
var path = request.Path.Value;
if (string.Equals(path, _rewriteTo, StringComparison.OrdinalIgnoreCase))
return;
if (_ignore != null && _ignore.IsMatch(path))
return;
var fileInfo = context.StaticFileProvider.GetFileInfo(path);
if (!fileInfo.Exists)
{
request.Path = _rewriteTo;
context.Result = RuleResult.SkipRemainingRules;
}
}
}
I think it is a more convenient way to deploy SPA using static web app service, see more about routes here: https://learn.microsoft.com/en-us/azure/static-web-apps/routes

Redirect Response always redirects to the same domain

I'm writing a suite of ASP.NET Core web applications that occasionally have to redirect to one another. When testing locally, everything works fine. However, when I publish them on our staging server, the redirects always "stay" in the same host. For example, if I am on http://app1.test/ and redirect to http://app2.test/somepath, what I actually get in the Location HTTP header i http://app1.test/somepath: any URL I specify is transformed so that it "stays" in the current host name.
This doesn't happen locally, however. I've deployed the apps as Kestrel processes, and they are exposed via IIS working as a reverse proxy. May this be the cause? What should I do to fix the issue?
UPDATE
Here is the full web.config for the reverse proxy of app1.test:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://localhost:5000/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
<system.web>
<sessionState mode="InProc" />
<customErrors mode="RemoteOnly" />
</system.web>
</configuration>
app2.test's web.config is virtually the same (apart, of course, for the port numbers).
UPDATE 2
I'll try to explain better. I noticed that the target site doesn't really matter, so I'll keep things simpler: I have an action in my application that I want to redirect the user to Google. This is the action, in the Home controller:
public IActionResult ToGoogle()
{
return Redirect("https://www.google.com?q=Hi");
}
If I launch the web app locally and request http://localhost:1234/Home/ToGoogle, everything is fine: the response is a 302 Found, with the correct URL (www.google.com etc.) in the Location header.
Once I publish the app to the staging server (Kestrel app on port 5000, behind an IIS reverse proxy with the rewrite rule posted above), this is what happens instead:
What is the cause of that?
I found the solution myself. It was indeed a problem with reverse proxy.
IIS has an option to rewrite the host in response headers. The solution is described in this answer (there are addenda in other answers to that same question if your version of IIS or Windows Server is not the one specified).

DotNetNuke 404 custom page not working

I created a new page, and set permissions to all users. On site settings set it as 404 page, but still get the system page for 404 errors.
Only works if Friendly Url Provider mode is set 'advanced' on web.config:
<friendlyUrl defaultProvider="DNNFriendlyUrl">
<providers>
<clear />
<add name="DNNFriendlyUrl" type="DotNetNuke.Services.Url.FriendlyUrl.DNNFriendlyUrlProvider, DotNetNuke.HttpModules" includePageName="true" regexMatch="[^a-zA-Z0-9 _-]" urlFormat="advanced" />
</providers>
</friendlyUrl>
Urls will be changed. Spaces will be replaced by "-" and aspx extension is removed.

How to add a json file as content to a MVC 4 Web Application

I have a json file that I would like to include as content to my MVC 4 Web Application. I add the file to my Content folder and mark 'Build Action' as 'Content' and 'Copy to Output Directory' as 'Do not copy'. Now if I browse to http://[my site]/content/myjsonfile.json I get an HTTP 404. I mess around with all of the build actions and copy to output selections and still doesn't work. I change the json file extension to txt and then it works. Is this a bug? My site is published using Windows Azure. Thanks for any feedback.
The problem is that your webserver doesn't recognize the filetype. Add this line to your web.config:
<system.webServer>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json; charset=UTF-8" />
</staticContent>
</system.webServer>

IIS6 Virtual Directory not accessible as an app

I have a WinSrv2k3 box with IIS6 hosting a series of sites, one of which is a VB/.NET2 site. Inside this I have created a virtual directory and pointed it at a very simple C#/.NET3.5 site's directory. I was expecting the site to allow me to view the pages as a normal site (there is only one ASMX in the virtual directory) but when accessing the page from a browser, I get:
Server Error in '/TestVbSite' Application.
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: Could not load file or assembly 'IMSControls' or one of its dependencies. The system cannot find the file specified. (D:\sites\TestVbSite\web.config line 211)
Source Error:
Line 209: </httpHandlers>
Line 210: <httpModules>
Line 211: <add name="UrlRewritingModule" type="IMS.Controls.HttpModules.UrlRewritingModule, IMSControls" />
Line 212: </httpModules>
Line 213: </system.web>
Source File: D:\sites\TestVbSite\web.config Line: 211
The issue I see there, is that the web.config throwing the exception appears to be the parent web site's .config, not the web.config in the virtual directory. But I don't understand why.
When accessing regular pages within the website (not under the virtual directory) they render and perform as normal, indicating that the IMSControls DLL is unable to load from the virtual directory, but again, I don't understand why this would even be involved in the process.
Ok, well, after some false starts, heavy googling gave me the correct thing to look for: web.config inheritance.
Basically, to stop a virtual directory from inheriting the attributes of it's parent site's web.config (and therefore any problems from it) the parent site's web.config needs to have its <system.web> element wrapped in a new (to me) tag:
<location path="." inheritInChildApplications="false">
<system.web>
...
</system.web>
</location>
Useful links:
http://forums.asp.net/t/1164283.aspx
http://dotnetslackers.com/Security/re-55457_Stopping_ASP_NET_web_config_inheritance.aspx
http://msdn.microsoft.com/en-us/library/ms178685.aspx