I have a solution with two projects in it. A portable areas project, and a web site that references the portable areas project, and Both reference MvcContrib.
The problem I am have is with the embedded resources, they are giving me a 404 error when I try to get to them. It seems like it's trying to access a physical path not the dll. The partial view works fine.
The file I'm trying to access looks like this inside my visual studio solution explorer
AdHocReporting/Areas/AdHocReportBuilder/Content/adhoc.css (the build action is embedded)
Here is the routing for the portable area:
using System.Web.Mvc;
using MvcContrib.PortableAreas;
namespace AdHocReporting.Areas.AdHocReportBuilder
{
public class AdHocReportBuilderAreaRegistration : PortableAreaRegistration
{
public override string AreaName
{
get { return "AdHocReportBuilder"; }
}
public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
{
RegisterRoutes(context);
RegisterAreaEmbeddedResources();
}
private void RegisterRoutes(AreaRegistrationContext context)
{
context.MapRoute(
AreaName + "_content",
base.AreaRoutePrefix + "/Content/{resourceName}",
new { controller = "EmbeddedResource", action = "Index", resourcePath = "Content" },
new[] { "MvcContrib.PortableAreas" }
);
context.MapRoute(
AreaName + "_default",
base.AreaRoutePrefix + "/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
}
Here is the Web sites Global.aspx that has the reference to the portable area
namespace AdHocReportingSite
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
PortableAreaRegistration.RegisterEmbeddedViewEngine();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
}
here is an image of what my solution explore looks like:
Could not reproduce the issue once I created a new project and redid everything. I'm not sure if it was an error or I just missed a step in setting up my portable areas.
In the Web.Config add for each file type:
<system.webServer>
<handlers>
<add name="js" path="*.js" verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="File" preCondition="integratedMode" />
</handlers>
</system.webServer>
This will make IIS try to use the defined routes instead of searching for the static file.
Related
I am in search of the correct way to use serilog with aspnet webapi2.
As for now I initialize the global Log.Logger property like that :
public static void Register(HttpConfiguration config)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
{
IndexFormat = IndexFormat,
BufferBaseFilename = outputLogPath,
AutoRegisterTemplate = true,
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6,
CustomFormatter = new ElasticsearchJsonFormatter(renderMessageTemplate: false),
BufferFileCountLimit = NbDaysRetention
})
.MinimumLevel.ControlledBy(new LoggingLevelSwitch() { MinimumLevel = LogEventLevel.Information})
.Enrich.FromLogContext()
.Enrich.WithWebApiRouteTemplate()
.Enrich.WithWebApiActionName()
.CreateLogger();
//Trace all requests
SerilogWebClassic.Configure(cfg => cfg.LogAtLevel(LogEventLevel.Information));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Is there a more cleaner way to do it? I am wondering, if this might be a problem if I have to put some test in place for my controllers.
I have used the following code organization quite a lot for apps with Web API (and/or MVC). (note that it may be a bit outdated, as it is based on old versions of some packages, but you should be able to adapt it without too much work ...)
You will need quite a few packages, that you should be able to guess from the namespaces, but most importantly, install the WebActivatorEx package which provides a way to have code running at different moment of the app lifecycle
Our Global.asax.cs ended up looking like this :
public class WebApiApplication : System.Web.HttpApplication
{
// rely on the fact that AppPreStart is called before Application_Start
private static readonly ILogger Logger = Log.ForContext<WebApiApplication>();
public override void Init()
{
base.Init();
}
protected void Application_Start()
{
// WARNING : Some code runs even before this method ... see AppPreStart
Logger.Debug("In Application_Start");
// Mvc (must be before)
AreaRegistration.RegisterAllAreas();
// Web API
// ... snip ...
// some DependencyInjection config ...
// ... snip ...
// MVC
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Logger.Information("App started !");
}
protected void Application_End(object sender, EventArgs e)
{
Logger.Debug("In Application_End");
ApplicationShutdownReason shutdownReason = System.Web.Hosting.HostingEnvironment.ShutdownReason;
Logger.Information("App is shutting down (reason = {#shutdownReason})", shutdownReason);
// WARNING : Some code runs AFTER Application_End ... see AppPostShutDown
}
}
and then several classes under the App_Start folder (some of which you can just ignore :P ) :
AppPreStart.cs
using XXX;
using Serilog;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(AppPreStart), nameof(AppPreStart.PreApplicationStart))]
namespace XXX
{
/// <summary>
/// This runs even before global.asax Application_Start (see WebActivatorConfig)
/// </summary>
public class AppPreStart
{
public static void PreApplicationStart()
{
LogConfig.Configure();
var logger = Log.ForContext<AppPreStart>();
logger.Information("App is starting ...");
// ... snip ...
// very early things like IoC config, AutoMapper config ...
// ... snip ...
logger.Debug("Done with AppPreStart");
}
}
}
AppPostShutDown.cs
using XXX;
using Serilog;
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(AppPostShutDown), nameof(AppPostShutDown.PostApplicationShutDown))]
namespace XXX
{
/// <summary>
/// This runs even before global.asax Application_Start (see WebActivatorConfig)
/// </summary>
public class AppPostShutDown
{
private static ILogger Logger = Log.ForContext<AppPostShutDown>();
public static void PostApplicationShutDown()
{
Logger.Debug("PostApplicationShutDown");
// ... snip ...
// very late things like IoC dispose ....
// ... snip ...
// force flushing the last "not logged" events
Logger.Debug("Closing the logger! ");
Log.CloseAndFlush();
}
}
}
LogConfig.cs
using Serilog;
using Serilog.Events;
using SerilogWeb.Classic;
using SerilogWeb.Classic.Enrichers;
using SerilogWeb.Classic.WebApi.Enrichers;
namespace XXX
{
public class LogConfig
{
static public void Configure()
{
ApplicationLifecycleModule.LogPostedFormData = LogPostedFormDataOption.OnlyOnError;
ApplicationLifecycleModule.FormDataLoggingLevel = LogEventLevel.Debug;
ApplicationLifecycleModule.RequestLoggingLevel = LogEventLevel.Debug;
var loggerConfiguration = new LoggerConfiguration().ReadFrom.AppSettings()
.Enrich.FromLogContext()
.Enrich.With<HttpRequestIdEnricher>()
.Enrich.With<UserNameEnricher>()
.Enrich.With<HttpRequestUrlEnricher>()
.Enrich.With<WebApiRouteTemplateEnricher>()
.Enrich.With<WebApiControllerNameEnricher>()
.Enrich.With<WebApiRouteDataEnricher>()
.Enrich.With<WebApiActionNameEnricher>()
;
Log.Logger = loggerConfiguration.CreateLogger();
}
}
}
and then read the variable parts of the Log configuration from Web.config that has the following keys in AppSettings :
<!-- SeriLog-->
<add key="serilog:level-switch:$controlSwitch" value="Information" />
<add key="serilog:minimum-level:controlled-by" value="$controlSwitch" />
<add key="serilog:enrich:with-property:AppName" value="XXXApp" />
<add key="serilog:enrich:with-property:AppComponent" value="XXXComponent" />
<add key="serilog:enrich:with-property:Environment" value="Dev" />
<add key="serilog:enrich:with-property:MachineName" value="%COMPUTERNAME%" />
<add key="serilog:using:Seq" value="Serilog.Sinks.Seq" />
<add key="serilog:write-to:Seq.serverUrl" value="http://localhost:5341" />
<add key="serilog:write-to:Seq.apiKey" value="xxxxxxxxxxx" />
<add key="serilog:write-to:Seq.controlLevelSwitch" value="$controlSwitch" />
(and then we had Web.config transforms to turn it into a "tokenized" file for production
Web.Release.config
<!-- SeriLog-->
<add key="serilog:enrich:with-property:Environment" value="__Release_EnvironmentName__"
xdt:Transform="Replace" xdt:Locator="Match(key)"/>
<add key="serilog:write-to:Seq.serverUrl" value="__app_serilogSeqUrl__"
xdt:Transform="Replace" xdt:Locator="Match(key)"/>
<add key="serilog:write-to:Seq.apiKey" value="__app_serilogApiKey__"
xdt:Transform="Replace" xdt:Locator="Match(key)"/>
Some of the most important parts of this configuration is :
configure your logger as soon as possible
call Log.CloseAndFlush(); at the very end to be sure all your log events are stored/pushed
add Enrich.FromLogContext() from Serilog, and some enrichers from SerilogWeb.Classic and SerilogWeb.WebApi, they can turn out to be super useful.
logging to a log server that properly supports structured logging (writing to files just has too many drawbacks) ... we used Seq and were very very happy about it (installed locally on every dev machine, and then a centralized instance in production). It supports searching/querying and dashboards and also dynamic log level control.
I want to use area as separate project.Whenever I try to navigate to this link :
http://localhost:100/module/home
I get the following error:
My solution contains two projects : entity_framework and module
My solution looks like:
module is the project that I wanted to use as an area.My main project:
My Route.config in entity_framework looks :
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "User", action = "Index", id = UrlParameter.Optional },
namespaces:new string[] {"entity_framework.Controllers"}
);
}
}
My moduleAreaRegistration.cs :
namespace entity_framework
{
public class moduleAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "module";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"module_default",
"module/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new string[] {"entity_framework.Controllers"}
);
}
}
}
I have added Index action in home controller in both projects and Index view too but I am stuck in this error. Thanks in advance.
I changed the namespace to
new string[] { "module.Controllers" }
in moduleAreaRegistration.cs .
Then I added this inside web.config inside main project:
<appSettings>
<add key="owin:AutomaticAppStartup" value="false" />
</appSettings>
That worked fine for me.
I'm having trouble getting ASP.net MVC to serve up the default controllers index view for the root site url http://mysite:8080/. All I get back is a 404 with The resource cannot be found. It works fine if I specify the full route path in the url : http://mysite:8080/Default/Index, however, I want the default route to apply even if the user doesn't specify the route path though. It seems that this should just work out of the gate. This is a fresh project from the VS2013 MVC 4 template.
I've tried both route mappings below and neither seems to work. How can this be achieved?
routes.MapRoute(
"Root",
"",
new { controller = "DefaultController", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "DefaultController", action = "Index", id = UrlParameter.Optional }
);
Here is a solution to this problem. It's disappointing that the default route's defaults do not apply for the root site url though.
routes.Add(new Route("", new SiteRootRouteHandler("~/Default/Index")));
public class SiteRootRouteHandler : IRouteHandler
{
private readonly string _redirectUrl;
public SiteRootRouteHandler(string redirectUrl)
{
_redirectUrl = redirectUrl;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new SiteRootHandler(_redirectUrl);
}
}
public class SiteRootHandler: IHttpHandler
{
private readonly string _redirectUrl;
public SiteRootHandler(string redirectUrl)
{
_redirectUrl = redirectUrl;
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.RedirectPermanent(_redirectUrl);
}
}
I want to go to http://myserver and be able to get Help Pages as the default home page, so the first thing a guest to http://myserver should see is the Help Page.
I have a default route set up like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Then I have my Help Page Area registration set up like this:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"HelpPage_Default",
"doc/{action}/{apiId}",
new { controller = "Help", action = "Index", apiId = UrlParameter.Optional });
HelpPageConfig.Register(GlobalConfiguration.Configuration);
}
When I change RouteConfig's controller to "Help" I get:
The view 'Index' or its master was not found or no view engine
supports the searched locations
When I change Help Page route to "{controller}/{action}/{apiId}" my AttributeRoutes stop working.
Is there some easy way to make ASP.NET Help Pages default home page?
I accomplished this with the following RouteConfig. I am also using ASP.Net Help Pages to auto-generate my documentation from the inline XML comments:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// By default route the user to the Help area if accessing the base URI.
routes.MapRoute(
"Help Area",
"",
new { controller = "Help", action = "Index" }
).DataTokens = new RouteValueDictionary(new { area = "HelpPage" });
}
}
I should also mention that I don't have any other routing in this class since I am using Attribute Routing on API methods individually.
For those who search where to add the route, with the current version of the WebApi and of the NuGet package you have to search for the file named "HelpPageAreaRegistration" in the Area folder added by NuGet.
Here is mine once it was coded to have the help page with WebApi has default web page.
public class HelpPageAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "HelpPage";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"HelpPage_Default",
"Help/{action}/{apiId}",
new { controller = "Help", action = "Index", apiId = UrlParameter.Optional });
context.MapRoute(
"Help Area",
"",
new { controller = "Help", action = "Index" }
);
HelpPageConfig.Register(GlobalConfiguration.Configuration);
}
}
I'm trying to remove 'home' form my URL, so in other words:
www.domain.com/home/about/ becomes www.domain.com/aboutus
The problem is, the home is not being removed and I can't work out why. I can see others have identical questions with near identical answers as to mine here on on SO so I am at a loss why this won't work.
My Global.asax is
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
namespace Company.Ui
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("RemoveHomeUrl", // Route name
"{action}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
}
}
My ActionLink code is:
#Html.ActionLink("About us", "AboutUs", "Home", null, new { #class = "mega" })
When I hover over a link and when I click on the link, it still returns www.domain.com\home\aboutus
I'm running this in debug mode in Visual Studio 2012.
I am at a loss, can any one help?
I think you are working with your routes in the wrong place,
from the shown code it seems the registered routes are defined in RouteConfig class
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes); //routes are registered here
}
Try replacing
RouteConfig.RegisterRoutes(RouteTable.Routes);
with
RegisterRoutes(RouteTable.Routes);
or edit in the RouteConfig class
hope this helps.