MVC3 - All routing works locally but not remotely - asp.net

I have a new MVC3 project with one Controller called PublicController.cs which contains 4 identical methods for testing out how routing works. The only difference between them is their name, and that they each point to a different view ...
public class PublicController : Controller
{
//
// GET: /Public/
public ActionResult Index()
{
return View();
}
//
// GET: /Public/App
public ActionResult App()
{
return View();
}
//
// GET: /Public/Press
public ActionResult Press()
{
return View();
}
//
// GET: /Public/Contact
public ActionResult Contact()
{
return View();
}
}
I can get to all of them when running in the development server by visiting these URLs...
http://localhost:53367/Public/
or its equivalent
http://localhost:53367/Public/Index
and then
http://localhost:53367/Public/App
http://localhost:53367/Public/Press
http://localhost:53367/Public/Contact
However, once it's deployed to my remote ASP.NET 4.0 server, the only two that work are:
http://localhost:53367/Public
http://localhost:53367/Public/Index
... all others give me a 404 Resource cannot be found error.
My web-server is shared hosting with netcetera, using a sub-domain for this deployment (previously had problems with MVC in virtual directories, but have full blown MVC2 apps running in sub-domains no problem). I've deployed by using the "Publish to file system" option, then copying over the files aswell as just copying the entire source project over. Both give identical results.
Any ideas why?
Thanks,
Steven

Did you make sure MVC 3 Framework is installed to the web server, and that your site's app pool is set to 4.x ASP.net?

Related

Unnecessary Dotnet Core MVC "Home" and "Index" URL components?

The default routing and URL generation for Dotnet Core 2.0 MVC produces very weird URLs. I don't remember them being like this in ASP.Net MVC.
i.e. when creating creating a new site using the Dotnet Core MVC template, we see the following URLs generated:
/Home/About « I could have sworn this used to just be /About
/Manage/Index « This definitely used to be /Manage
In the first example this is the same in the current ASP.NET MVC (.NET Template) template. I don't remember seeing the "Home" in the URL. That seems weird. Why not just /About?
In the second example there's a difference between the ASP.NET MVC (.NET Template) and the Dotnet Core one. The "Index" on the end is obviously superfluous.
Anyone know what's up here? Is there a simple way to change the default route definition to clean this up?
Take a look at attribute routing which you could use to declare your routes in this fashion:
public class HomeController : Controller
{
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
return View();
}
[Route("Home/About")]
public IActionResult About()
{
return View();
}
[Route("Home/Contact")]
public IActionResult Contact()
{
return View();
}
}
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing#attribute-routing

Exclude documentation when deploying to production

I have a Web API project in ASP .NET where I have some web services.
I use the comments of the methods to generate the help pages created by default in Visual Studio.
// GET api/version
/// <summary>
/// Test
/// </summary>
/// <returns></returns>
public string Get()
{
return "Last version : 74";
}
It is usefull for a development environment but we don't want these pages to appear on the production environment.
Is there any way to exclude them from deployment on production?
I tried to delete the files and make sure the web services calls still works, they do, but I am not sure of which files I can delete and I am wondering if there is any safer/automatic way to do it?
Here is the list of files I have at the moment :
ApplicationInsights.config
App_Data
Areas
bin
Content
favicon.ico
fonts
Global.asax
Scripts
Views
Web.config
WsCommon.wpp.targets
Publish the web site to a local folder and deploy those contents, they will only contain the essential files.
Deploy that folder, or if possible, just publish to your target site.
I ended up doing the following :
File App_Start/RouteConfig.cs
Ignore the route to the HelpPages in DEBUG Configuration
public static void RegisterRoutes(RouteCollection routes)
{
[...]
#if DEBUG
routes.IgnoreRoute("Help");
#endif
[...]
}
File Global.asax.cs
protected void Application_Start()
{
[...]
#if DEBUG
AreaRegistration.RegisterAllAreas();
#endif
[...]
}
File Controllers/HomeController.cs
Should return a 404 error instead of an index in production :
public ActionResult Index()
{
ViewBag.Title = "Home Page";
#if DEBUG
return View();
#else
return HttpNotFound();
#endif
}
This way, the help pages url return the help pages in Debug and 404 errors in Release. No need to do any further manipulation with the files from

How can I get the baseurl of my site in ASP.NET Core?

Say my website is hosted in the mywebsite folder of www.example.com and I visit https://www.example.com/mywebsite/home/about.
How do I get the base url part in an MVC controller? The part that I am looking for is https://www.example.com/mywebsite
The example listed here doesn't work as we don't have access to Request.Url in ASP.NET Core
You should still be able to piece together what you need. You have access to the request object if your controller inherits from Controller.
If you are using VS2017, fire up a new ASPNet Core MVC app and replace the homecontroller with:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
ViewData["Message"] = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public IActionResult Error()
{
return View();
}
}
I just put in some of the stuff that might interest you in the "About" method, but you should explore the rest of the request class so you know what else is available.
As #Tseng pointed out, you might have a problem when running Kestrel behind IIS or Azure App Service, but if you use the IISIntegration package or AzureAppServices package (by installing the Nuget package and adding it in Program.cs to your WebHostBuilder), it should forward those headers to you. It works great for me in Azure, because I sometimes have to make decisions based on which hostname they hit. The IIS/Azure packages also forward the original remote IP address, which I log.
If you need this anywhere in your app than you should create a class and add it as a service.
Define your static class and your extension method for adding it to the service pipeline like this.
public class MyHttpContext
{
private static IHttpContextAccessor m_httpContextAccessor;
public static HttpContext Current => m_httpContextAccessor.HttpContext;
public static string AppBaseUrl => $"{Current.Request.Scheme}://{Current.Request.Host}{Current.Request.PathBase}";
internal static void Configure(IHttpContextAccessor contextAccessor)
{
m_httpContextAccessor = contextAccessor;
}
}
public static class HttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseHttpContext(this IApplicationBuilder app)
{
MyHttpContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
return app;
}
}
It might be a little redundant to expose the HttpContext in this case but I find it very helpful.
You would than add it to the pipeline in your Configfure method which is located in Startup.cs
app.UseHttpContext()
From there it is simple to use it anywhere in your code.
var appBaseUrl = MyHttpContext.AppBaseUrl;
All of these existing answers depend on an HttpContext object, which is only available during an incoming request. However, I needed to get the URLs in a background service where HttpContext was not available.
This information is also available in the Microsoft.AspNetCore.Hosting.Server.IServer service, as long as the actual host service provides this information. If you're using the default Kestrel server, I've found that it is indeed provided. I have not tested this when hosting IIS in-process or with other hosting models.
You need to get an instance of IServer and then look for the .Features entry of type IServerAddressesFeature.
Here's an extension method to get the URL(s) directly from an IServiceProvider:
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
public static ICollection<string> GetApplicationUrls(this IServiceProvider services)
{
var server = services.GetService<IServer>();
var addresses = server?.Features.Get<IServerAddressesFeature>();
return addresses?.Addresses ?? Array.Empty<string>();
}
You could however accomplish the same thing by injecting IServer if DI services are available.
using Microsoft.AspNetCore.Http;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public AccountController(IHttpContextAccessor httpContextAccessor)
{
var request = httpContextAccessor.HttpContext.Request;
var domain = $"{request.Scheme}://{request.Host}";
//domain => https://varunsoft.in
}
NPNelson answer works if with .Value.ToString()
var baseUrl = $"{this.Request.Scheme}://{this.Request.Host.Value.ToString()}{this.Request.PathBase.Value.ToString()}";
var baseUrl = Request.GetTypedHeaders().Referer.ToString();
This way you can capture the base url information.
This is how I could get it in Asp .Net Core 3.1 version.
You can access the resource from the link below.
Reference
string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"));
you can check for more information here:
How can I get my webapp's base URL in ASP.NET MVC?

Using Html.Action with a controller in a subfolder

I am building a webapge using asp.net mvc4. For organization, I would like to put some of the controllers inside a subfolder in the Controllers folder. For example:
Controllers
AccountController
BlahController
Dashboard (Folder)
ChickenController
BeefController
To use BeefController (which returns a partial view), it seems as though I should use:
#Html.Action("Index", "Dashboard/BeefDashboard")
However this gets me the following error:
The controller for path '/' was not found or does not implement IController.
How would I be able to use BeefController?
There no physical sub folder concepts in the ASP.NET MVC world. What you should do is to have an action method in Dashboard controller, which accepts a parameter and then return specific views according to that.
public class DashBoardController: Controller
{
public ActionMethod Index(string id)
{
if(id=="chicken")
{
return PartialView("Chicken");
}
else if(id=="beef")
{
return PartialView("beef");
}
return View("NotFound");
}
}
Now you can access those like
Dashboard/beef
Dashboard/chicken

ASP.NET_SessionId cookie slows down web server on multiple GET requests

I am testing my ASP.NET MVC2 web application using MS VisualStudio 2010 Express and the ASP.NET Development server on http://localhost. The ASP.NET Framework is version 4.
I have a page with a list of images that are retrieved through an Action method as follows:
[HTML code]
<img src="/images/thumb_79c7b9f0-5939-43e5-a6d0-d5e43f4e8947.jpg" alt="image">
[Routing configuration in Global.asax.cs]
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Image",
"images/{id}",
new { controller = "Image", action = "Picture", id = "" }
);
// [...] other routing settings
}
}
[Image Controller]
public class ImageController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Picture(string id)
{
try
{
return File(ImageBasePath + id, "image/jpeg");
}
catch (Exception)
{
}
}
private string ImageBasePath
{
get { return HttpContext.Request.PhysicalApplicationPath + WebConfigurationManager.AppSettings["dbImageBasePath"]; }
}
}
In practice, the Picture action method performs other checks before returning the image and this is the reason why I return the image through an action method.
The problem here is that when there is no session in place (i.e. the ASP.NET_SessionId cookie doesn't exist for localhost) the time for the browser to get the images is very short (~10ms) while when ASP.NET_SessionId cookie does exist, the time jumps to 500ms-1s. This happens on any browser.
I've done various tests and I saw that if I get the images without passing through the ASP.NET application, the ASP.NET_SessionId cookie doesn't affect the loading time.
It looks like multiple HTTP GET requests with an ASP.NET_SessionId cookie passed to the web application slow down considerably the application itself.
Does anyone have an explanation for such a strange behavior?
Many thanks.
UPDATE
The problem described above occurs on a IIS7 web server as well, so it is not specific to the local ASP.NET Development server.
ASP.NET will only let a single request at a time access the session state. So all your image requests will be serialized and you're seeing long response times.
The workaround is to disable session state or set it to read-only. In ASP.NET this can be done with the SessionState attribute:
[SessionState(SessionStateBehaviour.Disabled)]
public class ImageController : Controller
{
...

Resources