HttpContext in Razor Views - asp.net-mvc-3

I tried to port some ASPX markup to Razor, but compiler threw an error.
ASPX (works fine):
<% if (node.IsAccessibleToUser(Context)) { %>
// markup
<% } %>
CSHTML (throws an error):
#if (node.IsAccessibleToUser(Context)) {
// markup
}
Argument 1: cannot convert from 'System.Web.HttpContextBase' to 'System.Web.HttpContext'
How to get reference to HttpContext in Razor view? Is it correct to use HttpContext.Current or I need to check site map node visibility in a different way?

WebViewPage.Context is HttpContextBase instance. WebViewPage.Context.ApplicationInstance.Context is HttpContext instance.
#if (node.IsAccessibleToUser(Context.ApplicationInstance.Context)) {
// markup
}

Yes, you can use HttpContext.Current. This will give you access to the request and response data.

What #Martin means is that you could write some extension methods on your Node class (whatever the type of that is), like:
public static class NodeExtensions
{
public static bool IsAccessibleToUser(this Node node)
{
// access HttpContext through HttpContext.Current here
}
}
and use it in your view like:
#if(node.IsAccessibleToUser()) {}
Thus removing the dependency on HttpContext in your view.

Related

RazorEngine WebApiTemplateBase #Url.Content()

How can I get #Url.Content() working in my _Layout.cshtml when RazorEngine is being used from ASP.NET Web API?
RazorEngine (v.3.7.2) only deals with the Razor syntax and not the additional helper methods like #Html or #Url. These can be added by extending the TemplateBase<> and setting it in the configuration.
There are code examples in some old issues: #26, #29; in an unreleased, incomplete piece of code in MvcTemplateBase.cs; and in the documentation for Extending the Template Syntax.
My problem is I'm using ASP.NET Web API (v.1) which won't have HttpContext.Current (nor should it). I want to provide a UrlHelper as I want to use its Content() method but it needs to be instantiated with the HttpRequestMessage which won't be available.
Perhaps there's no way to get #Url helper methods for my compiled layout. Perhaps I need some other way of getting the absolute path from the virtual path. It seems I'd still need some way of checking the Request though.
A way to get this working is to follow the direction set by Extending the Template Syntax and use VirtualPathUtility.ToAbsolute() in a helper method.
using System.Web;
using RazorEngine.Templating;
namespace MyNamespace.Web
{
public abstract class WebApiTemplateBase<T> : TemplateBase<T>
{
protected WebApiTemplateBase()
{
Url = new UrlHelper();
}
public UrlHelper Url;
}
public class UrlHelper
{
public string Content(string content)
{
return VirtualPathUtility.ToAbsolute(content);
}
}
}
Set up the TemplateService configuration with this extension of the TemplateBase<>.
var config =
new RazorEngine.Configuration.TemplateServiceConfiguration
{
TemplateManager = new TemplateManager(),
BaseTemplateType = typeof(WebApiTemplateBase<>)
};

Access to ViewEngines.Engines.FindView Method in not web context

I have question about access to FindView Method in not web context,
public static bool ViewExists(ControllerContext ctx, string name)
{
var result = ViewEngines.Engines.FindView(ctx, name, null);
return result.View != null;
}
I want to render view to create static files (JavaScript files rendered in Razor),
When I use this metod in web context (in controller action metod everythik is ok, but in test context I get exception
System.Web.HttpException : The application relative virtual path '~/Views/Test/New.aspx' cannot be made absolute, because the path to the application is not known.
How I would use (moq) ControllerContext in not web context to Render Razor View ?

Trying to use F# controllers with Web API

I'm getting a "could not load type MyApp.Api.App from assembly MyApp.Api" runtime error in my c# mvc project that is referencing an f# web api library. The c# project MyApp.Web has a project reference to the F# project MyApp.Api and has no compilation errors. What could be the issue?
App.fs in the project MyApp.Api
namespace MyApp.Api
open System
open System.Web
open System.Web.Mvc
open System.Web.Routing
open System.Web.Http
open System.Data.Entity
open System.Net.Http.Headers
open System.Net.Http.Headers
type Route = { controller : string; action : string; id : UrlParameter }
type ApiRoute = { id : RouteParameter }
type App() =
inherit System.Web.HttpApplication()
static member RegisterGlobalFilters (filters:GlobalFilterCollection) =
filters.Add(new HandleErrorAttribute())
static member RegisterRoutes(routes:RouteCollection) =
routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" )
routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}",
{ id = RouteParameter.Optional } ) |> ignore
routes.MapRoute("Default", "{controller}/{action}/{id}",
{ controller = "Home"; action = "Index"; id = UrlParameter.Optional } ) |> ignore
member this.Start() =
AreaRegistration.RegisterAllAreas()
App.RegisterRoutes RouteTable.Routes
App.RegisterGlobalFilters GlobalFilters.Filters
And my global.asax.cs in MyApp.Web
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using MyApp.Api;
namespace MyApp.Web
{
public class WebApiApplication : MyApp.Api.App// System.Web.HttpApplication
{
protected void Application_Start()
{
base.Start();
}
}
}
You are registering your api route incorrectly. While the APIs look similar, they are not. You need to register your Web API route using the HttpConfiguration instance:
GlobalConfiguration.Configuration.Routes.MapHttpRoute("", "", ...)
You are trying to map a Web API route into the MVC RouteTable. I'm actually surprised you don't get a compilation error.
So the above appears to not be the case. I must not have included an appropriate namespace when I tried before without pulling in Dan Mohl's project template.
You've subclassed your MyApp.Api.App type in Global.asax.cs. Dan's template doesn't include this. Instead, his template modifies the markup in Global.asax as follows:
<%# Application Inherits="MyApp.Api.App" Language="C#" %>
<script Language="C#" RunAt="server">
protected void Application_Start(Object sender, EventArgs e) {
base.Start();
}
</script>
This seems to work just fine. I also got the following to work:
<%# Application Inherits="MyApp.Web.WebApiApplication" Language="C#" %>
<!-- The following seems to be optional; just an extra, duplicate event handler.
I was able to run the app with this script and without. -->
<script Language="C#" RunAt="server">
protected void Application_Start(Object sender, EventArgs e) {
base.Start();
}
</script>
Note that you need the full namespace, not just the type name. If that works correctly, then I think more of the code is necessary, as I can't find anything else that is wrong.

ASP.NET MVC 3 Dependency Injection - Controllers, Views & Action Filters

I'm trying to get dependency injection working in an ASP.NET MVC 3 application using Microsoft Unity. First i have implemented my own IDependencyResolver and activated it in my Global.asax file like so:
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
I found that i don't need to do anything else to get controller injection (via both the constructor and [Dependency] attribute) to work. With the default view engine i also found i could get the [Dependency] attribute to work in the standard views but not the Layout views. Is it possible to get this to work for the Layout Views aswell?
However i have implemented my own view engine which inherits from VirtualPathProviderViewEngine that overrides the CreateView/CreatePartialView methods and returns my own custom view (implements IView). See the Render method of the custom view below:
public void Render(ViewContext viewContext, TextWriter writer) {
var webViewPage = DependencyResolver.Current.GetService(_type) as WebViewPage;
//var webViewPage = Activator.CreateInstance(_type) as WebViewPage;
if (webViewPage == null)
throw new InvalidOperationException("Invalid view type");
webViewPage.VirtualPath = _virtualPath;
webViewPage.ViewContext = viewContext;
webViewPage.ViewData = viewContext.ViewData;
webViewPage.InitHelpers();
WebPageRenderingBase startPage = null;
if (_runViewStartPages)
startPage = StartPage.GetStartPage(webViewPage, "_ViewStart", _viewStartFileExtensions);
var pageContext = new WebPageContext(viewContext.HttpContext, webViewPage, null);
webViewPage.ExecutePageHierarchy(pageContext, writer, startPage);
}
With the commented out line i completely lost dependency injection within my views so i changed it to the line above which again works fine for the standard views but not for the Layout views. I'd appreciate it if you could show me how the above could be modified to work for the Layout views aswell?
Finally i'm trying to get action filter injection working aswell. I have found two different cases:
Apply the filter to the action via an attribute.
Defining it as a global filter, e.g.:
GlobalFilters.Filters.Add(new TestAttribute());
Neither seem to use the dependency resolver. Therefore i need to do some extra work. Please correct me if there's a better way. To enable the first scenario i did the following:
public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider {
private IUnityContainer _container;
protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
var attributes = base.GetControllerAttributes(controllerContext, actionDescriptor);
foreach (var attribute in attributes) {
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
var attributes = base.GetActionAttributes(controllerContext, actionDescriptor);
foreach (var attribute in attributes) {
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
}
And then defined this within my Global.asax file like so:
FilterProviders.Providers.Remove(FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider));
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
This works fine but i was wondering if this is the correct way to do it? To resolve the second case i simply changed where i defined my global filter to the following:
GlobalFilters.Filters.Add(DependencyResolver.Current.GetService<TestAttribute>());
This again now works but is this the correct way to do it?
I'd appreciate the help. Thanks
It's been a while since I originally asked this but I thought I would share what I ended up doing.
In cases where I could not use constructor or attribute injection I solved it by using the DependencyResolver (service locator pattern). For example if I require the service IService I would simply inject it like so:
public IService Service => DependencyResolver.Current.GetService<IService>();
While some may consider this an anti-pattern I have found this performs well, leads to less problems and with new advances in C# I don't think it looks too bad.
However if you are using ASP.NET Core you should never have to use the service locator pattern as it has been rebuilt with dependency injection at the heart of it.

Asp.Net MVC : Execution of the child request failed. Please examine the InnerException for more information

I'm recieving the following error message,
A public action method 'RenderMenu'
was not found on controller
'Web.Controllers.SiteController'.
However this action DOES exist and the controller does exist (As it work everywhere on the site) I looked at the inner exception.
Execution of the child request failed.
Please examine the InnerException for
more information.
(This is the inner exception...)
Stack Trace
at
System.Web.Mvc.HttpHandlerUtil.ServerExecuteHttpHandlerWrapper.Wrap[TResult](Func`1
func) at
System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler
handler, TextWriter writer, Boolean
preserveForm, Boolean setPreviousPage,
VirtualPath path, VirtualPath
filePath, String physPath, Exception
error, String queryStringOverride)
Now, we have a website set-up with a dynamic menu system so we are using RenderAction() on a generic controller to build this menu system up.
<% Html.RenderAction("RenderMenu", "Site"); %>
This call is made from the MasterPage and it works fine until there was a validation error like so,
[HttpPost]
public ActionResult Register(UserModel UserToAdd)
{
if(!ModelState.IsValid)
{
return View(UserToAdd);
}
//Run some validation
if (_UserService.DoesEmailExist(UserToAdd.EMail))
{
TempData["error"] = "Email Address Already in use!";
return View(UserToAdd);
}
//Add the user
TempData["info"] = "User Added - " + UserO.ID;
return View("Success");
}
It works fine when there this is a new user, but if someone enters an email that already exist we get the above error. THis RenderAction Method works all over the site (This is the first form we have added)
Any suggestions?
Fixed:
The RenderAction() Method is below
[HttpGet]
public ActionResult RenderMenu()
{
//Do Stuff
}
Removing the HttpGet Attribute has resolved the issue.
public ActionResult RenderMenu()
{
//Do Stuff
}
Would love to know why?
This is because your parent request is an [HttpPost], and the child request operates in the same verb as the parent. If your method is marked as [HttpGet], it will not respond to [HttpPost] requests. Hitting the action directly through your browser works because that is a GET. Hitting the action as a child action in the context of a POST will not work.

Resources