Create/Get DefaultHtmlGenerator from MVC Controller - asp.net

I am trying to create(Or get an instance of it somehow) for Microsoft.AspNet.Mvc.Rendering.DefaultHtmlGenerator inside my MVC6 controller method
I wanted to generate the html for validation for my Model my self inside my controller of asp.net mvc. My issue is where to get the constructor data for DefaultHtmlGenerator like antiforgery, metadataProvider..etc
[HttpGet]
public IActionResult GetMarkup()
{
// IHtmlGenerator ge = this.CurrentGenerator();
IHtmlGenerator ge = new DefaultHtmlGenerator(params);
var tag= ge.GetClientValidationRules(params)
}
here is the a link about the HtmlGenerator class
DefaultHtmlGenerator

Since MVC 6 is based on dependency injection, all you have to do is require IHtmlGenerator in your constructor, and the DI container will automatically fill in all of the dependencies of DefaultHtmlGenerator (provided that is what is setup in your DI configuration).
public class HomeController : Controller
{
private readonly IHtmlGenerator htmlGenerator;
public HomeController(IHtmlGenerator htmlGenerator)
{
if (htmlGenerator == null)
throw new ArgumentNullException("htmlGenerator");
this.htmlGenerator = htmlGenerator;
}
public IActionResult GetMarkup()
{
// Use the HtmlGenerator as required.
var tag = this.htmlGenerator.GetClientValidationRules(params);
return View();
}
}
That said, it appears that the GetClientValidationRules method is only designed to work within a view, since it accepts ViewContext as a parameter. But this does answer the question that you asked.

Related

Autofac InstancePerRequest returns new isntance

I have an ASP.NET Web API, .Net 4.6.1 project where I need to capture some info in the middleware and then retrieve it in a code that will be called from controller. In .Net core it is very easy with registering my custom context class as Scoped and resolving it in different stages of message processing. In .Net Framework, what looked similar to it was Autofac's InstancePerRequest so I tried but it does not work as I expected. Apparently every time I do BeginScope() it returns a new instance even if I am within same reuest? I am implementing the IAutofacContinuationActionFilter interface where I resolve my service, registered with InstancePerRequest, then later in controller I try to resolve it again and get new instance. What am I missing here?
Oh and in the controller below, both instances of IHomeService: injected via constructor and resolved manually are creating new instances.
UPDATE:
The code above is oversimplification of the real situation. The call where I need the information passed from filter is in a separate class and call happens through a series of autogenerated code. The constructor injection is not an option for me so I was hoping to have a solution similar to .Net Core DI.
my WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = MyContainerBuilder.Build(config);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
my container builder class:
public class MyContainerBuilder
{
public static IContainer Build(HttpConfiguration config)
{
var builder = new ContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
builder
.Register(c => new MyCustomFilter())
.AsWebApiActionFilterForAllControllers()
.InstancePerRequest();
// var assembly = typeof(IHomeService).Assembly;
// builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
builder
.RegisterType<HomeService>()
.As<IHomeService>()
.InstancePerRequest();
return builder.Build();
}
}
filter:
public class MyCustomFilter : IAutofacContinuationActionFilter
{
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> next)
{
using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())
{
var hs = scope.Resolve<IHomeService>();
++hs.Counter;
var hs1 = scope.Resolve<IHomeService>();
++hs1.Counter;
var r = next().Result;
return await Task.FromResult(r);
}
}
}
controller:
[Route("home")]
public class HomeController : ApiController
{
public IHomeService HomeService { get; set; }
public HomeController(IHomeService homeService)
{
HomeService = homeService;
}
[HttpGet]
[Route("")]
public string Index()
{
var dr = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
using (var scope = dr.GetRequestLifetimeScope())
// This does not work either, returns new instance:
// using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope().GetRequestLifetimeScope())
{
var hs = scope.Resolve<IHomeService>();
++hs.Counter;
}
return "Home";
}
}
service class I try to resolve:
public interface IHomeService
{
int Counter { get; set; }
}
public class HomeService : IHomeService
{
public HomeService()
{
Console.WriteLine("Yet another instance of HomeService!!!");
}
public int Counter { get; set; }
}
Thanks in advance
You can't create your own request scope, you need to get it from the request message. In the filter, that's like:
var scope = actionContext.Request.GetDependencyScope();
An example filter showing this is in the Autofac docs
However, since you're using Autofac interfaces, they're injected by Autofac for each request - if your filter needs a per-request service, it's better to make it a constructor parameter on the filter. You only need to do service location if you're not using Autofac filter interfaces.
If you seriously need service location in the filter, you can still use the constructor to make things easier - add an ILifetimeScope parameter to the filter constructor and you'll get the request scope as a parameter.
For the controller, same thing: inject what you need in the constructor rather than using service location. If you need the request scope because you can't escape service location, either inject an ILifetimeScope into the controller constructor or get the request lifetime off the HttpRequestMessage.

How to rewrite code to use IAuthorizationFilter with dependency injection instead of AuthorizeAttribute with service location in Asp Net Web Api?

I have the custom AuthorizeAttribute where I need to use one of the business layer services to validate some data in the database before giving user a permission to view the resource. In order to be able to allocate this service within the my AuthorizeAttribute I decided to use service location "anti-pattern", this is the code:
internal class AuthorizeGetGroupByIdAttribute : AuthorizeAttribute
{
private readonly IUserGroupService _userGroupService;
public AuthorizeGetGroupByIdAttribute()
{
_userGroupService = ServiceLocator.Instance.Resolve<IUserGroupService>();
}
//In this method I'm validating whether the user is a member of a group.
//If they are not they won't get a permission to view the resource, which is decorated with this attribute.
protected override bool IsAuthorized(HttpActionContext actionContext)
{
Dictionary<string, string> parameters = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
int groupId = int.Parse(parameters["groupId"]);
int currentUserId = HttpContext.Current.User.Identity.GetUserId();
return _userGroupService.IsUserInGroup(currentUserId, groupId);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContex)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(actionContex);
}
else
{
actionContex.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
}
I have couple of other attributes like this in my application. Using service locator is probably not a good approach. After searching the web a little bit I found some people suggesting to use IAuthorizationFilter with dependency injection instead. But I don't know how to write this kind of IAuthorizationFilter. Can you help me writing IAuthorizationFilter that will do the same thing that the AuthorizeAttribute above?
So after struggling for a while I think I managed to resolve this issue. Here are the steps you have to do in order to that:
1) First you have to make GetGroupByIdAttribute passive, and by passive I mean an empty attribute without any logic within it (it will be used strictly for decoration purposes)
public class GetGroupByIdAttribute : Attribute
{
}
2) Then you have to mark a controller method, for which you want to add authorization, with this attribute.
[HttpPost]
[GetGroupById]
public IHttpActionResult GetGroupById(int groupId)
{
//Some code
}
3) In order to write your own IAuthorizationFilter you have to implement its method ExecuteAuthorizationFilterAsync. Here is the full class (I included comments to guide you through the code):
public class GetGroupByIdAuthorizationFilter : IAuthorizationFilter
{
public bool AllowMultiple { get; set; }
private readonly IUserGroupService _userGroupService;
//As you can see I'm using a constructor injection here
public GetGroupByIdAuthorizationFilter(IUserGroupService userGroupService)
{
_userGroupService = userGroupService;
}
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
//First I check whether the method is marked with the attribute, if it is then check whether the current user has a permission to use this method
if (actionContext.ActionDescriptor.GetCustomAttributes<GetGroupByIdAttribute>().SingleOrDefault() != null)
{
Dictionary<string, string> parameters = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
int groupId = int.Parse(parameters["groupId"]);
int currentUserId = HttpContext.Current.User.Identity.GetUserId();
//If the user is not allowed to view view the resource, then return 403 status code forbidden
if (!_userGroupService.IsUserInGroup(currentUserId, groupId))
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.Forbidden));
}
}
//If this line was reached it means the user is allowed to use this method, so just return continuation() which basically means continue processing
return continuation();
}
}
4) The last step is to register your filter in the WebApiConfig.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Here I am registering Dependency Resolver
config.DependencyResolver = ServiceLocator.Instance.DependencyResolver;
//Then I resolve the service I want to use (which should be fine because this is basically the start of the application)
var userGroupService = ServiceLocator.Instance.Resolve<IUserGroupService>();
//And finally I'm registering the IAuthorizationFilter I created
config.Filters.Add(new GetGroupByIdAuthorizationFilter(userGroupService));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Now, if needed, I can create additional IActionFilters that use IUserGroupService and then inject this service at the start of the application, from WebApiConfig class, into all filters.
Perhaps try it like shown here:
Add the following public method to your class.
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// gets the dependecies from the serviceProvider
// and creates an instance of the filter
return new GetGroupByIdAuthorizationFilter(
(IUserGroupService )serviceProvider.GetService(typeof(IUserGroupService )));
}
Also Add interface IFilterMetadata to your class.
Now when your class is to be created the DI notices that there is a CreateInstance method and will use that rather then the constructor.
Alternatively you can get the interface directly from the DI in your method by calling
context.HttpContext.Features.Get<IUserGroupService>()

Set ViewBag property in the constructor of a ASP.NET MVC Core controller

My theme has some sort of breadcrumb. The controller is always the category. To avoid repeat myself, I want to set it in the constructor of the controller for all actions like this:
class MyController:Controller{
public MyController() {
ViewBag.BreadcrumbCategory = "MyCategory";
}
}
When I access ViewBag.BreadcrumbCategory in the layout-view, its null. In a Action it works:
class MyController:Controller{
public IActionResult DoSomething() {
ViewBag.BreadcrumbCategory = "MyCategory";
}
}
I'm wondering that setting a ViewBag property is not possible in a constructor? It would be annoying and no good practice to have a function called on every action which do this work. In another question using the constructor was an accepted answear, but as I said this doesn't work, at least for ASP.NET Core.
There is an GitHub issue about it and it's stated that this is by design. The answer you linked is about ASP.NET MVC3, the old legacy ASP.NET stack.
ASP.NET Core is written from scratch and uses different concepts, designed for both portability (multiple platforms) as well as for performance and modern practices like built-in support for Dependency Injection.
The last one makes it impossible to set ViewBag in the constructor, because certain properties of the Constructor base class must be injected via Property Injection as you may have noticed that you don't have to pass these dependencies in your derived controllers.
This means, when the Controller's constructor is called, the properties for HttpContext, ControllerContext etc. are not set. They are only set after the constructor is called and there is a valid instance/reference to this object.
And as pointed in the GitHub issues, it won't be fixed because this is by design.
As you can see here, ViewBag has a dependency on ViewData and ViewData is populated after the controller is initialized. If you call ViewBag.Something = "something", then you it will create a new instance of the DynamicViewData class, which will be replaced by the one after the constructor gets initialized.
As #SLaks pointed out, you can use an action filter which you configure per controller.
The following example assumes that you always derive your controllers from Controller base class.
public class BreadCrumbAttribute : IActionFilter
{
private readonly string _name;
public BreadCrumbAttribute(string name)
{
_name = name;
}
public void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
var controller = context.Controller as Controller;
if (controller != null)
{
controller.ViewBag.BreadcrumbCategory = _name;
}
}
}
Now you should be able to decorate your controller with it.
[BreadCrumb("MyCategory")]
class MyController:Controller
{
}
I have the same issue and solve it overriding the OnActionExecuted method of the controller:
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
ViewBag.Module = "Production";
}
Here is a better way to do this for .NET Core 3.x, use the ResultFilterAttribute:
Create your own custom filter attribute that inherits from ResultFilterAttribute as shown below:
public class PopulateViewBagAttribute : ResultFilterAttribute
{
public PopulateViewBagAttribute()
{
}
public override void OnResultExecuting(ResultExecutingContext context)
{
// context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
(context.Controller as MyController).SetViewBagItems();
base.OnResultExecuting(context);
}
}
You'll need to implement the method SetViewBagItems to populate your ViewBag
public void SetViewBagItems()
{
ViewBag.Orders = Orders;
}
Then Decorate your Controller class with the new attribute:
[PopulateViewBag]
public class ShippingManifestController : Controller
That's all there is to it! If you are populating ViewBags all over the place from your constructor, then you may consider creating a controller base class with the abstract method SetViewBagItems. Then you only need one ResultFilterAttribute class to do all the work.

MVC 6 #inherit RazorPage

I am trying to migrate an MVC 5 Application to ASP.NET 5 MVC 6 (Beta 7).
Having problems when using the #inherits and #model directive together.
Works fine when they are used separately.
In my _ViewImports i added the #inherits directive to use a base page with some custom user properties.
public abstract class BaseViewPage<TModel> : RazorPage<TModel>
{
protected MyPrincipal AppUser
{
get
{
return new MyPrincipal(this.User as ClaimsPrincipal);
}
}
}
_ViewImports.cshttml
#inherits CommonWeb.BaseViewPage<TModel>
#addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
And then i can go AppUser. in all my views.
This works if i dont use a strongly typed view. If i add the #model directive in any view the inherited view page goes away.
Help appreciated
Update:
I did this successfully by using a custom pageBaseType in the web.config in prior versions.
Workaround.
public class ViewHelper
{
ViewContext _context;
public ViewHelper(ViewContext context)
{
_context = context;
}
public MyPrincipal AppUser
{
get
{
return new MyPrincipal(_context.HttpContext.User as ClaimsPrincipal);
}
}
public string ControllerName
{
get
{
return _context.RouteData.Values["controller"].ToString();
}
}
}
View:
#{ var viewHelper = new ViewHelper(ViewContext);}
A way to achieve this for all views?
There is a better way in MVC 6, which now supports injecting dependencies on the views with the #inject directive. (The directive #inject IFoo Foo allows you to use in your view a property named Foo of type IFoo)
Create a new interface IAppUserAccessor for getting your app user, for example:
public interface IAppUserAccessor
{
MyPrincipal GetAppUser();
}
Create a class AppUserAccessor implementing it:
public class AppUserAccessor : IAppUserAccessor
{
private IHttpContextAccessor httpContextProvider;
public AppUserAccessor(IHttpContextAccessor httpContextProvider)
{
this.httpContextProvider = httpContextProvider;
}
public MyPrincipal GetAppUser()
{
return new MyPrincipal (
httpContextProvider.HttpContext.User as ClaimsPrincipal);
}
}
Register the new interface in the services container by adding a new entry in the ConfigureServices method of Startup.cs:
services.AddTransient<IAppUserAccessor, AppUserAccessor>();
Finally use the #inject directive to inject the IAppUserAccessor in your views. If you add the directive in ViewImports.cshtml then it will be available on every view.
#inject WebApplication4.Services.IAppUserAccessor AppUserAccessor
With all the pieces above you can now just use it on your view(s):
#AppUserAccessor.GetAppUser()
Update
If you need to inspect the route values, like the controller name, you can inject an IActionContextAccessor into your class and use it as follows:
public AppUserAccessor(IHttpContextAccessor httpContextProvider, IActionContextAccessor actionContextAccessor)
{
this.httpContextProvider = httpContextProvider;
this.actionContextAccessor = actionContextAccessor;
}
...
public string ControllerName
{
get { return actionContextAccessor.ActionContext.RouteData.Values["controller"].ToString(); }
}
Of course, that doesn't look like an AppUserAccessor anymore and smells like it has different responsabilities. At the very least it needs a more appropriate name :)
I would double check what do I need the controller name for. There might be a better way to accomplish your objective. (For example, if you need it for generating new links/urls you might just use an IUrlHelper)
Accessing ViewContext
Looks like beta8 has added support for injecting the ViewContext, although the implementation details may change before RC. See this question

Castle Windsor 3 persists PerWebRequest objects across multiple web requests

I have an MVC 4 project and am trying to inject a PerWebRequest object into my controller. However it appears the object is not being recreated across multiple requests
private static IWindsorContainer InitializeWindsor()
{
var container = new WindsorContainer().Install(FromAssembly.This());
// Add Factory facility
container.AddFacility<TypedFactoryFacility>();
// Register all controllers from this assembly
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
container.Register(
AllTypes.FromAssembly(assembly).BasedOn<Controller>().Configure(c => c.LifestyleTransient())
);
}
// Register HTTP Handlers
container.Register(Component.For<HttpRequestBase>().LifeStyle.PerWebRequest.UsingFactoryMethod(() => new HttpRequestWrapper(HttpContext.Current.Request)));
container.Register(Component.For<HttpContextBase>().LifeStyle.PerWebRequest.UsingFactoryMethod(() => new HttpContextWrapper(HttpContext.Current)));
// Register components
container.Register(Component.For<PerWebRequestObject>().LifeStyle.PerWebRequest);
}
Here's my PerWebRequestObject:
public class PerWebRequestObject
{
public DateTime DateCreated { get; set; }
public PerWebRequestObject()
{
DateCreated = DateTime.UtcNow;
Debug.WriteLine("Created: " + DateCreated.ToLongTimeString());
}
}
Here's my TestController:
public class TestController : BaseController
{
public PerWebRequestObject pwrObject { get; set; }
public ActionResult Test()
{
Debug.WriteLine(pwrObject.DateCreated.ToLongTimeString());
return new ContentResult();
}
}
Why is this not working? Note that I'm using the WindsorControllerFactory and releasing the Controller like so:
public override void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
disposableController.Dispose();
_windsorContainer.Release(controller);
}
Two things:
how do you know the object is not lifecycled properly (not re-created across web-requests)?
One possible explanation is that your dependency looks like this:
controller --> a singleton --> your per-web-request-component
this is a lifestyle mismatch, and that would explain the behaviour you're seing
do not dispose the controller. That's container's job.
Upgrading to Castle Windsor v3.1 fixed the issue for us without needing to change any code.
I assume there must've been an issue with the combination of Windsor v3.0 + MVC 4.

Resources