now I have a web application that will be published on multiple domains and I want to support different favicon based on domain
what I had done is :
** Added a handler in web.config called “favicon”, for any request for a file called “favicon.ico”
<system.webServer>
<handlers>
<add name="favicon" verb="*" path="favicon.ico" type="namespace.FaviconHandler, MyApplication" />
// other handlers
</handlers>
</system.webServer>
** then added class that supports the IHttpHandler interface
public class FaviconHandler : IHttpHandler
{
public void ProcessRequest(System.Web.HttpContext ctx)
{
string path = getFavIconPath(ctx.Request.Url.Host.ToLower());
string contentType = "image/x-icon";
path = ctx.Server.MapPath(path);
if (!File.Exists(path))
{
ctx.Response.StatusCode = 404;
ctx.Response.StatusDescription = "File not found";
}
else
{
ctx.Response.StatusCode = 200;
ctx.Response.ContentType = contentType;
ctx.Response.WriteFile(path);
}
}
private string getFavIconPath(string domain)
{
if (!string.IsNullOrEmpty(domain))
{
if (domain.Contains("abc.com")))
return "favicon.ico";
else
return "favicon2.ico";
}
return "favicon.ico";
}
}
The problem is .. it's not working well..
What I miss??
Thanks in advance
Another way could be to keep all icon files named with domain names like -
images
- abc.com.ico
- def.com.ico
Make a basecontroller and set a ViewBag property in its OnActionExecuting (override it) with hostname -
public override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
string host = HttpContext.Request.Host.Value;
ViewBag.Host = host;
}
And in your master layout set favicon link like -
<link rel="icon" href="~/images/#(ViewBag.Host).ico"/>
Related
I want to always redirect to my home page if someone puts something like www.example/bla/qwerty.hm (it doesnt have controller and it doesnt exist) to www.example.com.
I want to redirect all urls, which have not controller, to my home page and not show error. For example: www.example.com/auto (auto is not controller) and it will be redirect to my home page. How do I do that?
I tried Route configuration
routes.MapRoute(
name: "MyRedirect"
, url: "{contextRedirect}/{*tasks}"
, defaults: new { controller = "Redirect", action = "TotalRedirect", contextRedirect = "" }
);
...
public ActionResult TotalRedirect(string contextRedirect)
{
return RedirectToAction("Index", "Home");
}
but this is called every time and it makes an infinity loop (it is called every time when redirectToAction is called)
The problem disappears if I write all RouteMaps for all existing controllers before this MapRoute, but I have lot of controllers, and I want avoid writing RouteMaps for all controllers.
Instead of MapRoute I tried Web.config and change errors
<customErrors mode="On">
<error redirect="/Error" statusCode="404" />
<error redirect="/Error" statusCode="500" />
</customErrors>
Error is controller which return RedirectToAction and I got same result as point 1 (infinity loop). But when I change /Error to /Home it is working (because Home return View), but on Url path is saved error text Home?aspxerrorpath=/auto. After redirect I do not want show text, that means if page will be redirected in to www.example.com, it not show www.example/Home?aspxerrorpath=/auto.
I am new to Asp.net MVC, and I don't know proper way how to do it.
Update
After some research I think there are two ways how to do it.
(Thanks to KevinLamb) Redirect from error with Application_Error and Web.Confing httpError. This is working for me:
This settings is put in Web.Confing on Project level, that means first Web.Config you see in Project Explorer (there is second Web.Config on View folder). Then you must create Controller named Error with ActionResult ErrorHandle
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
<remove statusCode="400"/>
<error statusCode="400" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
<remove statusCode="500"/>
<error statusCode="500" responseMode="ExecuteURL" path="/Error/ErrorHandle"/>
</httpErrors>
...
// Error Controller .cs
namespace MyWebApp.Controllers
{
public class ErrorController : Controller
{
// GET: Error
public ActionResult Index()
{
return RedirectToAction("Home", "Index");
}
public ActionResult ErrorHandle()
{
return RedirectToAction("Index", "Home");
}
}
}
...
// code inside Global.asax.cs MvcApplication class
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//Add some logging here
if(ex.GetType().IsAssignableFrom(typeof(HttpException)))
{
//Possibly log that you're redirecting the user
Response.Clear();
Response.Redirect("~/");
}
}
This is easy part.
Another way I discover is create HttpHandler or HttpModule. I am new in MVC and Asp.Net world and HttpHandler is not working allways for me, because it works only once, then only when application change page, but it not detect Url created by user (only first time). HttpModule work allways for me, but I don't know if it is good or bad. It is little harder then 1. point but you don't need Application_Error and httpErrors in Web.Config.
If you have httpErrors and Application_Error, delete it and create Module (Right click on Project > Add new Item > In search put "module" > and select Asp.Net Module. It create module class with Init and Dispose methods. Then create your own method and register it to BeginRequest.
And here is code for my HttpModule
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using System.Linq;
namespace MyWebApp
{
public class ErrorHttpModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
// Below is an example of how you can handle LogRequest event and provide
// custom logging implementation for it
// context.LogRequest += new EventHandler(OnLogRequest);
context.BeginRequest += new EventHandler(BR); // register your own method in to Event where you check Url
}
#endregion
private HttpContext context = null;
public void BR(Object source, EventArgs e)
{
context = System.Web.HttpContext.Current;
// collect all controllers in web application
Assembly asm = Assembly.GetAssembly(typeof(MyWebApp.MvcApplication)); // need using System.Reflection;
var controlleractionlist = asm.GetTypes()
.Where(type => typeof(System.Web.Mvc.Controller).IsAssignableFrom(type)) // need using System.Linq;
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), true).Any())
.Select(x => new { Controller = x.DeclaringType.Name, Action = x.Name, ReturnType = x.ReturnType.Name, Attributes = String.Join(",", x.GetCustomAttributes().Select(a => a.GetType().Name.Replace("Attribute", ""))) })
.OrderBy(x => x.Controller).ThenBy(x => x.Action).ToList();
// Get Url
string page = "";
if (context != null)
{
page = context.Request.Url.PathAndQuery;
}
string newUrl;
if (!String.IsNullOrEmpty(page))
{
bool continute = true;
// does url contain controller or action?
foreach (var controller in controlleractionlist)
{
string cpath = "/" + controller.Controller.Replace("Controller", "") + (controller.Action == "Index" ? "" : "/" + controller.Action);
if (cpath == page)
{
// Yes, don't continue to redirect
continute = false;
break;
}
else if (page == ("/" + controller.Action))
{
// Yes, don't continue to redirect
continute = false;
break;
}
}
// does page load your content, script etc.. ?
if (page.Contains("Content/") == true
|| page.Contains("Scripts/") == true
|| page.Contains(".ico") == true
|| page == "/"
)
{
// Yes, don't redirect.
continute = false;
}
if (continute)
{
// anything else will redirect to Home page
var urlHelper = new UrlHelper(context.Request.RequestContext); // nned using System.Web.Mvc;
newUrl = urlHelper.Action("About", "Home");
context.Response.Status = "301 Moved Permanently";
context.Response.AddHeader("Location", newUrl);
context.Response.End();
}
}
}
public void OnLogRequest(Object source, EventArgs e)
{
//custom logging logic can go here
}
}
}
And finnaly add module in to Web.Config (on Project level (ProjectName > Web.Config), not inside folder under Project (ProjectName > View > Web.Config))
<system.webServer>
<modules>
<add name="MyHttpErrorModule" type="MyWebApp.ErrorHttpModule, MyWebApp"/>
</modules>
</system.webServer>
Extended question
Either first or second point, I have problem with both, when in url is put characters like that /abc=45%$#r. It leads to Bad Request - Invalid URL HTTP Error 400. The request URL is invalid. and this don't detect Application_Error, httpErrors in Web.Config or my HttpModule with BeginRequest. Therefore I think this is on IIS settings but what I need to set up? It looks like those characters make IIS mess %$.
First you'll need to set errors to pass through in IIS so that your application can handle the file/parameter links instead of IIS. Add the below to your Web.config:
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
If you want to redirect to your home page (without any extra parameters in the URL) when you get an issue with a user going to the wrong controller/action, add the following code to your Global.asax.cs.
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//Add some logging here
if(ex.GetType().IsAssignableFrom(typeof(HttpException)))
{
//Possibly log that you're redirecting the user
Response.Clear();
Response.Redirect("~/");
}
}
Basically, what this is is doing is making all errors in your ASP.NET MVC website go through this method. Then it checks to see if the exception is a HttpException. If it is, it will redirect to your home controller and action. If it is not a HttpException it will continue doing the error handling process. This way you can keep handling the important errors properly.
I am trying to follow some instructions I found online in order to implement output caching for episerver.
In my web.config I have set up the following:
<caching>
<outputCache enableOutputCache="true">
</outputCache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="ClientResourceCache" enabled="true" duration="3600" varyByParam="*" varyByContentEncoding="gzip;deflate" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
As a test, I selected StartPageController.cs and added the [ContentOutputCache] tag like so:
[ContentOutputCache]
public class StartPageController : PageControllerBase<StartPage>
{
public ActionResult Index(StartPage currentPage)
{
var model = PageViewModel.Create(currentPage);
if (SiteDefinition.Current.StartPage.CompareToIgnoreWorkID(currentPage.ContentLink))
{
var editHints = ViewData.GetEditHints<PageViewModel<StartPage>, StartPage>();
editHints.AddConnection(m => m.Layout.Logotype, p => p.SiteLogotype);
editHints.AddConnection(m => m.Layout.Footer, p => p.FooterBlock);
}
return View(model);
}
}
}
Then the instructions say:
At some point on a page load you need to add the following code:
public void SetResposneHeaders()
{
HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.AddMinutes(2.0));
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
}
Due to my limited knowledge of .NET, MVC etc.. this part confuses me as I am not sure in which file to place it and where? Does this go in StartPageController.cs? Or somewhere else?
Any pointers would be appreciated.
The instructions I am trying to follow are here.
I created a new project in Microsoft Visual Studio Express 2013 for Web.
It is an ASP.NET MVC 5 - .NET Framework 4.5 project.
I wanted to handle (The resource cannot be found):
I did handle it using the code below.
This code will work if I do something like (/Home/kddiede/ddiij) or (/djdied/djie/djs), this will result in showing my custom Error page.
However, when I try to do something like (/Home/kddiede/ddiij/dfd/sdfds/dsf/dsfds/fd), or any long non-existing URL, It will show me this:
Code from: http://www.codeproject.com/Articles/635324/Another-set-of-ASP-NET-MVC-4-tips
Tip 16: Customizing error screens
Error Page is in the /View/Shared/Error.cshtml
Web.config
<system.web>
<customErrors mode="RemoteOnly" />
</system.web>
Global.asax
protected void Application_EndRequest(Object sender, EventArgs e)
{
ErrorConfig.Handle(Context);
}
ErrorConfig Class
public class ErrorConfig
{
public static void Handle(HttpContext context)
{
switch (context.Response.StatusCode)
{
//Not authorized
case 401:
Show(context, 401);
break;
//Not found
case 404:
Show(context, 404);
break;
}
}
static void Show(HttpContext context, Int32 code)
{
context.Response.Clear();
var w = new HttpContextWrapper(context);
var c = new ErrorController() as IController;
var rd = new RouteData();
rd.Values["controller"] = "Error";
rd.Values["action"] = "Index";
rd.Values["id"] = code.ToString();
c.Execute(new RequestContext(w, rd));
}
}
ErrorController
internal class ErrorController : Controller
{
[HttpGet]
public ViewResult Index(Int32? id)
{
var statusCode = id.HasValue ? id.Value : 500;
var error = new HandleErrorInfo(new Exception("An exception with error " + statusCode + " occurred!"), "Error", "Index");
return View("Error", error);
}
}
This last bit of code from the website I mentioned above was not added in Global.asax because It is already in FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
Anyone knows how to fix it?
Thanks in advance.
Solved. To point all non-existing urls to your error page, do the following:
Add the code below at the end of your RouteConfig.cs file:
public static void RegisterRoutes(RouteCollection routes)
{
// Default
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
// Add this code to handle non-existing urls
routes.MapRoute(
name: "404-PageNotFound",
// This will handle any non-existing urls
url: "{*url}",
// "Shared" is the name of your error controller, and "Error" is the action/page
// that handles all your custom errors
defaults: new { controller = "Shared", action = "Error" }
);
}
Add the code below to your Web.config file:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"></modules>
</system.webServer>
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true" />
</system.web>
</configuration>
That should point all the non-existing urls such as (/ad/asd/sa/das,d/asd,asd.asd+dpwd'=12=2e-21) to your error page.
Another approach would be to add this to your web.config inside of the system.web element
<system.web>
<!-- ... -->
<!--Handle application exceptions-->
<customErrors mode="On">
<!--Avoid YSOD on 404/403 errors like this because [HandleErrors] does not catch them-->
<error statusCode="404" redirect="Error/Index" />
<error statusCode="403" redirect="Error/Index" />
</customErrors>
</system.web>
I have a custom IHttpModule that I would like to only work on a specific route.
For example : http://example.com/HandleAzureTask
I want this module to only be invoked/handled on the /HandleAzureTask route.
Since this is not a controller, I can't really set the [Authorize] attribute on it; how can I force it only to be invoked/handled if user is authenticated?
I am on ASP.NET MVC 4 and currently have my module added to web.config as such:
<modules>
<remove name="AzureWebDAVModule" />
<add name="AzureWebDAVModule" type="VMC.WebDAV.Azure.Module.AzureWebDAVModule, VMC.WebDAV.Azure.Module" />
</modules>
HttpModules are called on every request (HttpHandlers, instead, can be filtered). If you just want to perform your task only on the selected route, you can do the following:
Set up a route like this:
routes.MapRoute(
name: "AzureWebDAVRoute",
url: "HandleAzureTask",
// notice the enableHandler parameter
defaults: new { controller = "YourController", action = "YourAction", enableHandler = true }
);
On your module:
public class AzureWebDAVModule : IHttpModule
{
public void Init(HttpApplication context)
{
// you can't directly access Request object here, it will throw an exception
context.PostAcquireRequestState += new EventHandler(context_PostAcquireRequestState);
}
void context_PostAcquireRequestState(object sender, EventArgs e)
{
HttpApplication context = (HttpApplication)sender;
RouteData routeData = context.Request.RequestContext.RouteData;
if (routeData != null && routeData.Values["enableHandler"] != null)
{
// do your stuff
}
}
public void Dispose()
{
//
}
}
Now your task will be performed on the selected route only. Please note that you need the parameter since you can't find the current route by name.
Why not just create a ordinary folder namned /HandleAzureTask and put a seperate web.config inside that folder with the module registration.
Then the module will run for all request in that folder.
To get the authorization to work you can also set the authorization element in the web.config to disallow *
I would like to use the asp.net mvc client / server validation coming from a configurable source.
Some like a .config file where I could place infos:
Type, Member, ValidationType
<validations>
<add type="Customer" member="Name" validator="Required" />
<add type="Customer" member="Age" validator="Range" mimimum="18" maximum="100" />
</validations>
With this plan, would be possible to enable/disable validations.
Any idea?
If you need this, consider some more advanced validation framework, for example Enterprise Library Validation Block.
If you want to do it yourself, i would suggest creating custom attribute iniherited from ValidationAttribute like this (partly pseudocode, i am sure you get the idea)
public class ConfigurableValidationAttribute: ValidationAttribute
{
public override bool IsValid(object value)
{
string objectType = value.GetType().FullName;
string objectName = GetMyObjectName(value); // interface? reflection?
var validationRules = GetValidationRulesFor(objectType, name); // from your configuration
foreach (var rule in validationRules)
{
ValidationAttribute attr = null;
switch (rule.ValidatorName)
{
case "Required": attr = new RequiredAttribute();
case "StringLength": attr = // you get the idea
}
if (!attr.IsValid(value)) return false;
}
return true;
}
}