MVC 6 404 Not Found - asp.net-mvc

Instead of getting a HTTP 404 response, I'd like to have a generic 404 Not Found page (HTTP 200). I know you can set that up in MVC 5 with
<customErrors mode="On">
<error statusCode="404" redirect="~/Error/NotFound" />
</customErrors>
But I can't seem to figure out how to do this in MVC 6. I'm guessing it should be in either the UseMvc routing table, or a custom middleware.

Startup.cs:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// PICK YOUR FLAVOR.
// app.UseErrorPage(ErrorPageOptions.ShowAll);
app.UseStatusCodePages(); // There is a default response but any of the following can be used to change the behavior.
// app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
// app.UseStatusCodePages("text/plain", "Response, status code: {0}");
// app.UseStatusCodePagesWithRedirects("~/errors/{0}"); // PathBase relative
// app.UseStatusCodePagesWithRedirects("/base/errors/{0}"); // Absolute
// app.UseStatusCodePages(builder => builder.UseWelcomePage());
// app.UseStatusCodePagesWithReExecute("/errors/{0}");
}
}
project.json:
"dependencies": {
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
// ....
}
note that, if you are hosting on IIS (or azure), the web.config does still works and it's big maybe needed in some hosting scenarios. (it should be placed inside wwwroot folder

You can handle 404 errors with the UseStatusCodePagesWithReExecute
but you need to set the status code at the end of your configure pipeline first.
public void Configure(IApplicationBuilder app)
{
app.UseIISPlatformHandler();
if (string.Equals(_env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
{
app.UseDeveloperExceptionPage();
app.UseRuntimeInfoPage();
} else
{
app.UseExceptionHandler("/Error");
}
app.UseStatusCodePagesWithReExecute("/Error/Status/{0}");
app.UseStaticFiles();
app.UseMvc();
app.Use((context, next) =>
{
context.Response.StatusCode = 404;
return next();
});
}
This will set the status code to 404 if nothing in the pipeline can handle it (which is what you want).
Then you can easily capture and handle the response any way you want in your controller.
[Route("error")]
public class ErrorController : Controller
{
[Route("Status/{statusCode}")]
public IActionResult StatusCode(int statusCode)
{
//logic to generate status code response
return View("Status", model);
}
}

If anyone is looking to create custom 404 pages with HTML, you can do this:
Startup.cs
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePagesWithReExecute("/errors/{0}");
}
}
project.json
"dependencies": {
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
// ....
}
ErrorsController.cs
public class ErrorsController : Controller
{
// ....
[Route("/Error/{statusCode}")]
public IActionResult ErrorRoute(string statusCode)
{
if (statusCode == "500" | statusCode == "404")
{
return View(statusCode);
}
return View("~/Views/Errors/Default.cshtml");
}
}
All that's left after this is to create a 404.cshtml, 500.cshtml, and Default.cshtml in your Errors view folder and customize the HTML to your liking.

In the Configure function of the Startup class, call:
app.UseErrorPage(ErrorPageOptions.ShowAll);
more production ready:
if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
{
app.UseBrowserLink();
app.UseErrorPage(ErrorPageOptions.ShowAll);
app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
}
else
{
// Add Error handling middleware which catches all application specific errors and
// send the request to the following path or controller action.
app.UseErrorHandler("/Home/Error");
}

There is a middleware called StatusCodePagesMiddleware which I believe was added post-Beta3.
Sample: https://github.com/aspnet/Diagnostics/blob/dev/samples/StatusCodePagesSample/Startup.cs

If you have a standard ASP.NET 5 Web application template project you can get a really nice solution that will handle both keeping the error status code at the same time as you serve the cshtml file of your choice.
Startup.cs, in the Configure method
Replace
app.UseExceptionHandler("/Error");
with
app.UseStatusCodePagesWithReExecute("/Home/Errors/{0}");
Then remove the following catchall:
app.Run(async (context) =>
{
var logger = loggerFactory.CreateLogger("Catchall Endpoint");
logger.LogInformation("No endpoint found for request {path}", context.Request.Path);
await context.Response.WriteAsync("No endpoint found - try /api/todo.");
});
In HomeController.cs
Replace the current Error method with the following one:
public IActionResult Errors(string id)
{
if (id == "500" | id == "404")
{
return View($"~/Views/Home/Error/{id}.cshtml");
}
return View("~/Views/Shared/Error.cshtml");
}
That way you can add error files you feel the users can benefit from (while search engines and such still get the right status codes)
Now add a folder named Error in ~Views/Home folder
Create two .cshtml files. Name the first one 404.cshtml and the second one 500.cshtml.
Give them some useful content and the start the application and write something like http://localhost:44306/idonthaveanendpointhere and confirm the setup works.
Also press F12 developer tools and confirm the status code is 404.

Related

fHow to have separate login/logout screen with Azure AD authentication

I have previously integrated with Okta and they have a sign in widget you can put into a page to log in with and call a controller to initiate the authentication/claims process. However, Azure AD does not seem to have anything like this so I wanted to use my razor page with our logo on it located in /pages/login/index to be the only folder for anonymous viewing. Basically I would like it to be the first screen anyone sees when they come to the site when not logged in and it will have a button which calls a controller post action to take you to Microsofts Azure AD Login screen. I have not seen any examples of this online so I am curious if this is even possible?
My current solution takes you straight to microsofts login:
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using Project.Models;
using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
//authentication pipeline
var initialScopes = builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
builder.Services.AddControllers(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
var RootPath = builder.Environment.ContentRootPath;
var WebPath = builder.Environment.WebRootPath;
var fileDirectory = Path.Combine(Directory.GetParent(RootPath).Parent.ToString(), "armsfiles");
IFileProvider physicalProvider = new PhysicalFileProvider(fileDirectory);
builder.Services.AddSingleton<IFileProvider>(physicalProvider);
//Not needed. We are not using this level of abstraction but may move towards it one day so possibly keep.
var connectionString = builder.Configuration.GetConnectionString("DBContext");
builder.Services.AddDbContext<DBContext>(options => options.UseSqlServer(connectionString));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
//We are making it so armsfiles are not accessible outside of arms so if we move to box or onedrive then the parameter may need to be removed.
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = physicalProvider,
RequestPath = "/files"
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
app.Run();
I was thinking I could add this:
// Add services to the container.
builder.Services.AddRazorPages().AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToFolder("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizeFolder("/files");
});
But since there is no redirect for non logged in users that I can find I do not know if this option even makes a difference. So basically it is unclear how to setup the Program file to ensure not logged in users are redirected to this razor page (/login/index) and also what a controller for a sign in and sign out button would even look like! I am surprised I have struggled to find an example of this for ASP.NET Core 6.
**EDIT 11/22/2022
I have since got every component of my question solved except when a user goes to the site, if they are not authenticated I want to redirect them to "/login/" which hosts a page with a button that OnPost() does the following challenge:
public class IndexModel : PageModel
{
private string SignedOutRedirectUri;
public IndexModel(IConfiguration configuration)
{
SignedInRedirectUri = configuration["AzureAd:SignedInRedirectUri"];
}
public void OnGet()
{
}
public IActionResult OnPost()
{
return Challenge(
new AuthenticationProperties { RedirectUri = SignedInRedirectUri },
OpenIdConnectDefaults.AuthenticationScheme);
}
}
Is there a way to redirect users to a login page similar to how ASP.NET Core allows a redirect for their cookie options? ie,
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(cookieOptions =>
{
cookieOptions.Cookie.Name = "UserLoginCookie";
cookieOptions.LoginPath = "/Login/";
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(30);
cookieOptions.SlidingExpiration = true;
});
Create HomePageRouteModelConvention.
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace WebApplication3
{
public class HomePageRouteModelConvention : IPageRouteModelConvention
{
public void Apply(PageRouteModel model)
{
if (model.RelativePath == "/Pages/Index.cshtml")
{
var currentHomePage = model.Selectors.Single(s => s.AttributeRouteModel.Template == string.Empty);
model.Selectors.Remove(currentHomePage);
}
if (model.RelativePath == "/Pages/Login/Index.cshtml")
{
model.Selectors.Add(new SelectorModel()
{
AttributeRouteModel = new AttributeRouteModel
{
Template = string.Empty
}
});
}
}
}
}
Refer my startup.cs file.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
namespace WebApplication3
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI().AddRazorPagesOptions(options =>
{
options.Conventions.Add(new HomePageRouteModelConvention());
options.Conventions.AllowAnonymousToFolder("/Login");
options.Conventions.AuthorizeFolder("/");
options.Conventions.AuthorizeFolder("/files");
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.Run();
}
}
}
Others
Test Result:

How to redirect all url which not related to controllers (Asp.Net MVC)

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.

Request validation in asp.net mvc

I'm designing 2 websites. The first to upload images and the second to store the images as an image hosting (both of them are using asp.net mvc 5).
The domain name of the first website is: vinachannel.com.
In the first website, I wanna send some images to the hosting via ajax:
var f = new FormData();
$.ajax({
url: 'https://myimagehosting.com/home/upload',
type: 'POST',
data: f,
processData: false,
contentType: false
}).done(function (data) {
// logic...
})
Action Upload in Home controller of the hosting:
[HttpPost]
public JsonResult Upload()
{
if (Request.Files.Count > 0)
{
// start uploading...
}
}
Now, my problem is: I wanna the image hosting accepts only the requests which are sent from vinachannel.com. Just like:
[HttpPost]
public JsonResult Upload()
{
if (Request.Files.Count > 0 && Request.Url.AbsoluteUri.StartsWith("vinachannel.com"))
{
// start uploading...
}
}
or using regex:
var reg = new Regex(#"^(https://)?(www\.)?(vinachannel\.com)(.+)$");
if (Request.Files.Count > 0 && reg.IsMatch(Request.Url.AbsoluteUri))
{
// start uploading...
}
My question: How can I custom an attribute to validate all requests for action Upload?
[VinaChannel] // only requests from site vinachannel.com
[HttpPost]
public JsonResult Upload()
{
// ...
}
UPDATE: (based on #David's comment and following the article)
public class VinaChannelFilter : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
var reg = new Regex(#"^(https://)?(www\.)?(vinachannel\.com)(.+)$");
if (reg.IsMatch(HttpContext.Current.Request.Url.AbsoluteUri))
{
// what's next here...?
}
this.OnActionExecuting(filterContext);
}
}
You may create a custom action filter which inspects the request headers and see where the request is coming from and use that values to determine, whether to allow /deny further processing. the Referer header is the one you might want to use.
public class VerifyDomain : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var expectedHost = "domainnameyouwanttocheck";
var headers = filterContext.HttpContext.Request.Headers;
if (!String.IsNullOrEmpty(headers["Referer"])
&& new Uri(headers["Referer"]).Host == expectedHost)
{
base.OnActionExecuting(filterContext);
}
else
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
And decorate your action method/controller with the action filter
[VerifyDomain]
[HttpPost]
public ActionResult Upload()
{
// to do : return something
}
When this endpoint is being accessed from anywhere other than the value you have in the expectedHost variable, the caller will get 401 Unauthorized response. You may update the if condition to check for a list of expectedHost name's to support your local development environment as well.
Upload() is on myimagehosting.com, right? Then HttpContext.Current.Request.Url.AbsoluteUri will return Uri on myimagehosting.com domain, to get source address you need to get referrer: HttpContext.Current.Request.UrlReferrer.AbsolutePath. But the problem is, it can be easily forged, so depending on your needs, you probably will have to implement more complex Authentication/Authorization logic

Best Method to run Subdomains in MVC4

I have a website that is in a pre-integration phase. In other words, I have ensured that the site runs fine on my local Development Server (VS2012) utilizing the dynamically generated ASP.NET Development Server that runs at the time of debug executions; and I have now created a sub-domain of my domain on the Web Host Server and deployed my site there.
My decision to do this was because I obviously don't want users accessing the site until it has undergone thorough testing on the actual Host. My problem is though most of the site functions without issue (including URL's), there are a few links that produce the HTTP 404 error.
"The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly."
It then references the View/Controller path in the context of the Error that I know exists.
Why it only happens for certain Uri requests and not others is somewhat puzzling to me.
So I am strongly suspecting that it has something to do with the Default Routing Configuration for MVC and I believe if I were to move the site to the main domain, the issue will likely resolve but then again, it would defeat the purpose of setting it up in the SubDomain for testing before public access.
I need some viable options here but don't no where to start.
Should I address the issue from the perspective of the Routing Configuration and create 2 separate Global.asax.cs files? One for the domain and the other for the subdomain testing? And if so, How should I modify the file to accomodate for the Subdomain.
Or is there a more elegant solution for approaching the Integration process?
----------------- UPDATE ---------------------
So I've been troubleshooting the problem and it appears as though the 404 Error is only being generated for a method in my Controller that is returning a string.
I have a function that is being called in my View that looks like this:
<script>
function Subscribe(slvl) {
$.ajax({
type: 'POST',
datatype: "text",
data: { level: slvl },
url: '#Url.Action("Upgrade", "Profile")',
success: function (result) {
if (result) {
window.location = result;
}
},
error: function onError(result) {
alert(result.responseText);
}
});
}
</script>
I cannot post the full details of the Controller but it simply returns a string and whether I were to post the entire method or a simple one, the results would be the same. So for illustration only it looks something like this.
[HttpPost]
public string Upgrade(string level)
{
var uri = "http://www.someUri.com?Upgrade=" + level;
return uri;
}
This code is producing a HTTP 404 Error complaining that the path Profile/Upgrade cannot be found.
But I've found that if I use reference to a different method being called in the same Controller with the only exception being that it returns an ActionResult to a different View, the Error goes away and I'm redirected to the alternate view.
Any Ideas? So maybe it has nothing to do with the Subdomain???
First of all you need to create your own Custom Route
public class SubDomainRoute : Route
{
private readonly string[] namespaces;
public SubDomainRoute(string url, object defaults, string[] namespaces)
: base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
{
this.namespaces = namespaces;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
if (the subdomain is the expected one, then update your route data)
{
var routeData = base.GetRouteData(httpContext);
if (this.namespaces != null && this.namespaces.Length > 0)
{
routeData.DataTokens["Namespaces"] = this.namespaces;
}
routeData.DataTokens["Area"] = "Your Subdomain Area";
routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString;
return routeData;
}
return null;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
if (is your expected subdomain)
{
return base.GetVirtualPath(requestContext, values);
}
return null;
}
}
Then you have to use the custom route in the area registration like this:
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.Add("YourAreaName_default", new CustomSubDomainRoute(
"{controller}/{action}/{id}",
new { area = "Your Area", controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { typeof(Controllers.HomeController).Namespace } // Namespaces defaults
));
}
And finally, my advise is to use redirect to route, because the routeData Area is lost between the different requests.
Use your global property route name when redirecting.
[Authorize]
public class YourController : Controller
{
protected virtual string RouteName
{
get
{
return "YourRouteName";
}
}
public ActionResult Test(params here)
{
return RedirectToRoute(this.RouteName, new { action = "your action" });
}
}
Ref: http://forums.asp.net/t/1967197.aspx?Subdomain+in+mvc4+can+not+redirect+to+controller+in+area+

How can I redirect to "page not found" with MVC3?

I have some links in Google that I now no longer expect to work. All of the links look like this:
www.abc.com/xx/que=xxxxxxxxxxx
Where x can be anything.
Can someone tell me how I can set up a route and controller action that will return a 404 to google? I guess I need to set it up with a mask that includes "que" but I am not so sure how to do this.
Add a new route to the top of your global.asax. This will catch requests of the form xx/que={anything} using a regular expression to define the "que" argument.
routes.MapRoute(
"PageNotFound",
"xx/{que}",
new { controller = "Error", action = "NotFound" },
new { que = "que=.*" });
This would also assume you have an ErrorController with action NotFound and corresponding view named NotFound.aspx in your /Views/Error/ directory.
public class ErrorController : Controller
{
public ActionResult NotFound()
{
Response.StatusCode = 404;
return View();
}
}

Resources