Using GetGridHtml in a controller in Umbraco 10 - asp.net-core

I have made a simple Api controller on my umbraco website, that simply searches some nodes for some properties and returns the content from those properties in json.
One of the properties is an umbraco grid. Technically that is also json, and I could successfully get the content with some elaborate JObject querying. But from the view side there is access to a "GetGridHtml" helper function that does that for you.
So my question is, how do I achieve that in a controller? I got the node as IPublishedContent which incidentally has that helper function, but it wants an IHtmlHelper which isn't available in this context. So what do I do?
[HttpGet]
[Route("api/myapp/version/")]
public IActionResult GetLatestMyAppVersion()
{
var releaseNote = _umbracoHelper.ContentAtXPath("//releaseNoteList").First().Children().OrderByDescending(x => x.Value<DateTime>("releaseDate")).First();
var ver = releaseNote.Value<string>("myAppReleaseVersion");
var date = releaseNote.Value<DateTime>("releaseDate");
var desc = releaseNote.GetGridHtml(?????, "mainContent", "Clean"); // this is where IHtmlHelper is needed
return Ok(new { Application = "MyApp", Version = ver, ReleaseDate = date.ToString("yyyy-MM-dd"), Description = desc });
}

If you want to get the Ihtmlhelper inside the controller method, I suggest you could inject it by inside the controller construct method and then you could use it inside your codes.
More details, you could refer to below codes:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IHtmlGenerator _htmlGenerator;
private readonly IHtmlHelper _helper;
public HomeController(IHtmlHelper helper, IHtmlGenerator htmlGenerator, ILogger<HomeController> logger)
{
_logger = logger;
_helper = helper;
_htmlGenerator = htmlGenerator;
}

Related

In-Memory Cache with .NET EF Core throws error

I have a .Net Core application with EF which returns a page called Customer as home page. But I need show an app not available page (AppNotAvialable.cshmtl) when the user tries to access the application on a holiday(holiday list is stored in a table)instead of the Customer page. Instead of querying the holiday list table every time the home page is accessed I am trying to use the In-Memory Cache so I can store the query response in cache and use them in the controller. I am implementing the cache for the 1st time and below is what I tried
public class CustomersController : Controller
{
private readonly SurplusMouseContext _context;
private readonly IMemoryCache memoryCache;
.......................
public async Task<IActionResult> Index(string sortOrder, string searchString,
int? pageNumber, string currentFilter)
{
int holidaycheck;
var timeUtc = DateTime.UtcNow;
var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var todayDt = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, easternZone);
bool isExist = memoryCache.TryGetValue("HolidayWk", out holidaycheck);
if (!isExist)
{
holidaycheck = (from hc in _context.HolidayWeeks
where hc.HolidateDate.Date == todayDt.Date
select hc).Count();
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
memoryCache.Set("HolidayWk", holidaycheck, cacheEntryOptions);
}
if (holidaycheck != 0)
{
return View("/Views/Customers/AppNotAvailable.cshtml");
}
else
{
when I try to debug the application it throws error like below
Can anyone please suggest if I am implementing the in Memory Cache here correct/missing anything and where should I be doing the cacheMemory.Remove() Any help is greatly appreciated
The first step to check:
In your question, it is possible you didn't initialize MemoryCache object in the constructor.
There is an example of MemoryCache.
1 - You should register MemoryCache in services like this:
services.AddMemoryCache();
2 - Inject IMemoryCache interface in your controller and initialize it.
public class YourController : Controller
{
private readonly IMemoryCache _memoryCache;
public YourController(IMemoryCache memoryCache)
{
_memoryCache = memoryCache
}
}
3 - Using memory cache:
public async Task<IActionResult> Index(string sortOrder, string searchString,
int? pageNumber, string currentFilter)
{
bool isExist = _memoryCache.TryGetValue("HolidayWk", out holidaycheck);
}

Using IHtmlHelper with Dependency Injection

I would create a controller that response with the third-party IHtmlContent created through IHtmlHelper interface without using View() method. Actually I'm using a workaround:
I have created an extension function of IHtmlHelper that behaves like a "master" and other static function that behaves like "slaves". The master function call slaves based on data structure that It has received as a parameter. Slaves create the IHtmlContent based on third-party library. When all slave functions are completed, the controller send back the response.
This behaviour is inside cshtml view called by controller.
Is possible to have IHtmlHelper as controller constructor parameter using dependency Injection?
Something like
public class MyTestController
{
private readonly IHtmlHelper _html;
public MyTestController(IHtmlHelper html) {
_html = html;
}
}
If it were possible I would simplify many operations because I'm using this Master/Slave functions to replicate the behaviours of a class
Here you can find a pure example of how my code works now and how I would like to change it
https://gist.github.com/Blackleones/eb0d02b9dd99164271af88e22143d72b#file-example-cs
I need this component because the third-party library is an extension of IHtmlHelper
I thank Itminus for answering me.
However call IHtmlHelper on controller constructor it is not enough, you must contextualize IHtmlHelper before using it.
I want to share another solution that I found and solve my problem, maybe It can be useful for other users.
Please see https://gist.github.com/Blackleones/eb0d02b9dd99164271af88e22143d72b#file-example-cs to remember what my goal is.
To solve my problem I have created a class called HtmlHelperAdapter that contains some instances I need.
public class MyHtmlHelperAdapter
{
public IHtmlHelper _html;
public NubessHtmlHelperAdapter(IHtmlHelper html)
{
_html = html;
}
public IHtmlHelper Html => _html;
public ViewContext ViewContext => _html.ViewContext;
public HttpContext HttpContext => _html.ViewContext.HttpContext;
public ViewDataDictionary ViewData => _html.ViewData;
public IServiceProvider provider => _html.ViewContext.HttpContext.RequestServices;
}
after that I have created an extension method for IHtmlHelper
public static class MyBuilderExtension
{
public static MyBuilder Nubess(this IHtmlHelper html)
{
return new MyBuilder(new MyHtmlHelperAdapter(html));
}
}
where MyBuilder is
public class MyBuilder
{
private readonly MyHtmlHelperAdapter _htmlHelper;
public MyBuilder(MyHtmlHelperAdapter htmlHelper)
{
_htmlHelper = htmlHelper;
}
public FormBuilder<object> Form<T>(IDictionary<string, string> customizeArgs = null, FormBuilder<object> externalForm = null)
{
return new FormBuilder(_htmlHelper, typeof(T), customizeArgs, externalForm).Build();
}
}
now I can create my custom contents for views using class thanks to MyHtmlHelperAdapter
public class FormBuilder
{
private MyHtmlHelperAdapter _htmlHelper;
private readonly IStringLocalizer<SharedResources> _localizer;
private readonly LinkGenerator _linkGenerator;
private readonly IEngine _engine;
private readonly IEngineElement _element;
private readonly Type _typeDescriptor;
private readonly IDictionary<string, string> _descriptorArgs;
/* variabili per semplificare la gestione del builder */
private readonly FormBuilder<object> Form;
private readonly FormConfig Config;
private readonly IList<FormGroupConfig> Groups;
private readonly IList<FormItemConfig> Items;
private readonly IDictionary<string, string> FormOptions;
private readonly string _clientPrefix = "smtForm_{0}_";
public FormBuilder(MyHtmlHelperAdapter htmlHelper, Type typeDescriptor, IDictionary<string, string> ModelCustomizeArgs = null, FormBuilder<object> externalForm = null)
{
_htmlHelper = htmlHelper;
_localizer = _htmlHelper.provider.GetRequiredService<IStringLocalizer<SharedResources>>() ?? throw new ArgumentNullException();
_linkGenerator = _htmlHelper.provider.GetRequiredService<LinkGenerator>() ?? throw new ArgumentNullException();
_engine = _htmlHelper.provider.GetRequiredService<IEngine>() ?? throw new ArgumentNullException();
//code..
}
public FormBuilder<object> Build()
{
//code..
return Form;
}
//methods..
}
Short answer : Yes .
If you look into the source code of MVC, you will find that the IHtmlHelper is registered as a transient service :
internal static void AddViewServices(IServiceCollection services)
{
...
//
// HTML Helper
//
services.TryAddTransient<IHtmlHelper, HtmlHelper>();
services.TryAddTransient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>));
services.TryAddSingleton<IHtmlGenerator, DefaultHtmlGenerator>();
services.TryAddSingleton<ModelExpressionProvider>();
// ModelExpressionProvider caches results. Ensure that it's re-used when the requested type is IModelExpressionProvider.
services.TryAddSingleton<IModelExpressionProvider>(s => s.GetRequiredService<ModelExpressionProvider>());
services.TryAddSingleton<ValidationHtmlAttributeProvider, DefaultValidationHtmlAttributeProvider>();
services.TryAddSingleton<IJsonHelper, DefaultJsonHelper>();
...
}
In other words, you could inject the IHtmlHelper service as you like.
Just as a reminder, we should always prefer to using IHtmlHelper within View Layer.

How can I make an ASP Core Claims Principal available in a non-controller object?

I am working on an ASP Core 2 project using JWT authentication and the Dapper ORM.
Like all ASP projects, I have a lot of controllers, each instantiating its associated data objects. Each data object inherits from an abstract DbObject class that provides database access services. I also have an AuthenticatedUser object that abstracts the JWT to make it's properties easier to use.
What I want is to do is create the AuthenticatedUser object in the constructor of DbObject. Of course, one method is to create it in the controller and pass it to every concrete data object but this is messy as it would have to be passed hundreds of times (and it just feels wrong).
Is there a way to use the ASP Core middleware to get the token after authentication and make it available through dependency injection in the DbObject?
Edit
Hopefully, this clarifies my intentions. I would like the controller to create data objects and use their properties and methods without regard to implementation (i.e. DbObject). But queries executed by DbObject will be filtered by information in the token of the logged in user.
public class ManufacturerController : Controller {
[HttpGet]
public async Task<IActionResult> Get() {
var manufacturers = await new Manufacturer().SelectMany();
return Ok(manufacturers);
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id) {
var manufacturer = await new Manufacturer().SelectOne(id);
return Ok(manufacturer);
}...
public class Manufacturer : DbObject<Manufacturer> {
protected override string QrySelectOne => #"
Select *
From org.fn_Manufacturers ({0})
Where Id = {1}";
protected override string QrySelectMany => #"
Select *
From org.fn_Manufacturers ({0})";
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }...
public abstract class DbObject<T> {
protected readonly AuthenticatedUser authenticatedUser;
public DbObject(IHttpContextAccessor contextAccessor) {
authenticatedUser = new
AuthenticatedUser(contextAccessor.HttpContext.User);
}
protected abstract string QrySelectOne { get; }
protected abstract string QrySelectMany { get; }
public async Task<T> SelectOne (int id) {...}
public async Task<T> SelectOne(params object[] ids) {...}
public async Task<IEnumerable<T>> SelectMany () {...}
public async Task<IEnumerable<T>> SelectMany (params object[] ids) {...}
I suppose one solution may be to create a static data object factory which has the IHttpContextAccessor injected??
ASP.NET Core provides IHttpContextAccessor interface for accessing HttpContext from non-controller objects.
The usage is fair simple. Inject IHttpContextAccessor into DbObject and access HttpContext by calling IHttpContextAccessor.HttpContext:
public abstract class DbObject
{
protected DbObject(IHttpContextAccessor contextAccessor)
{
var context = contextAccessor.HttpContext;
// Create instance of AuthenticatedUser based on context.User or other request data
}
}
EDIT
Your controllers instantiate data objects directly (with new operator), that's why you can't have IHttpContextAccessor injected out of the box. Here are possible solutions. I list them in order of my preference (from best to worst).
If each controller uses only one (or just several) types of data objects, the best options will be to avoid direct instantiation and move toward normal Dependency Injection.
So if ManufacturerController requires only Manufacturer like in your sample then, it's better to inject Manufacturer instance to controller, not to create it inside:
public class Manufacturer1Controller : Controller
{
private readonly Manufacturer manufacturer;
public Manufacturer1Controller(Manufacturer manufacturer)
{
this.manufacturer = manufacturer ?? throw new ArgumentNullException(nameof(manufacturer));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
// ...
}
IHttpContextAccessor will be injected into Manufacturer and passed to base DbObject:
public class Manufacturer : DbObject<Manufacturer>
{
public Manufacturer(IHttpContextAccessor contextAccessor) : base(contextAccessor)
{
}
}
It's the cleanest solution in the list. You use DI in classic way and utilize all benefits DI provides.
If one controller could use dozens of different data objects, you could inject the factory object that creates instances of data objects. It could be simple implementation based on IServiceProvider:
public interface IDbObjectFactory
{
TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>;
}
public class DbObjectFactory : IDbObjectFactory
{
private readonly IServiceProvider serviceProvider;
public DbObjectFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>
{
return serviceProvider.GetRequiredService<TDbObject>();
}
}
public class Manufacturer2Controller : Controller
{
private readonly IDbObjectFactory dbObjectFactory;
public Manufacturer2Controller(IDbObjectFactory dbObjectFactory)
{
this.dbObjectFactory = dbObjectFactory ?? throw new ArgumentNullException(nameof(dbObjectFactory));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = dbObjectFactory.Create<Manufacturer>();
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
}
The code for Manufacturer and DbObject does not change comparing to the first option.
I don't see any reason not to use option #1 or #2. However just to complete the picture, I'll describe another two options.
Inject IHttpContextAccessor into conroller and pass this instance (or IHttpContextAccessor.HttpContext.User) to Data Object constructor invoked with operator new:
public class Manufacturer3Controller : Controller
{
private readonly IHttpContextAccessor contextAccessor;
public Manufacturer3Controller(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = await new Manufacturer(contextAccessor).SelectMany();
// or
// var manufacturer = await new Manufacturer(contextAccessor.HttpContext.User).SelectMany();
return Ok(manufacturer);
}
}
It's a bad solution, because you don't use Dependency Injection for Manufacturer here and loose many advantages that DI provides.
And the worst option would be using of static object factory with injected IHttpContextAccessor. With this approach you also loose benefits of DI. In addition you get ugly code somewhere in Startup that initializes static instance of IHttpContextAccessor. When you come to this approach, you'll discover that theere is no quite elegant way to do this.
My advice: use option #1 untill you have good reasons against it. Then use option #2.
Here is Sample Project on GitHub with samples for approaches ##1-3.

How to inject HttpHeader value in controller?

I have Web API developed using ASP.NET Core API. Every incoming request has a custom header value inserted. eg x-correlationid. The controller use this value for logging and tracing the request.
Currently I'm reading the value in each controller as below
[Route("api/[controller]")]
public class DocumentController : Controller
{
private ILogger<TransformController> _logger;
private string _correlationid = null;
public DocumentController(ILogger<DocumentController > logger)
{
_logger = logger;
_correlationid = HttpContext.Request.Headers["x-correlationid"];
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation("Start task. CorrelationId:{0}", _correlationid);
// do something here
_logger.LogInformation("End task. CorrelationId:{0}", _correlationid);
return result;
}
}
I think this is against DI rules.
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
Or
Can middleware read the x-correlationid and *somehow* make it available to all the controllers so we don't have to inject it in any controller?
What would be a better option here?
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
You can't inject the value itself into the constructor of the api controller, because at the time of construction the HttpContext is going to be null.
One "injection-style" option would be to use the FromHeaderAttribute in your actions:
[HttpPost]
public async Task<int> Transform(
[FromBody]RequestWrapper request,
[FromHeader(Name="x-correlationid")] string correlationId)
{
return result;
}
Can middleware read the x-correlationid and somehow make it available to all the controllers so we don't have to inject it in any controller?
I think a middleware solution would probably be overkill for what you need. Instead, you can create a custom base class that derives from Controller and have all your Api controllers derive from that.
public class MyControllerBase : Controller
{
protected string CorrelationId =>
HttpContext?.Request.Headers["x-correlationid"] ?? string.Empty;
}
[Route("api/[controller]")]
public class DocumentController : MyControllerBase
{
private ILogger<TransformController> _logger;
public DocumentController(ILogger<DocumentController> logger)
{
_logger = logger;
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation($"Start task. CorrelationId:{CorrelationId}");
// do something here
_logger.LogInformation($"End task. CorrelationId:{CorrelationId}");
return result;
}
}
This is what I came up with. I think i can also unit test it.
public interface IRequestContext
{
string CorrelationId { get; }
}
public sealed class RequestContextAdapter : IRequestContext
{
private readonly IHttpContextAccessor _accessor;
public RequestContextAdapter(IHttpContextAccessor accessor)
{
this._accessor = accessor;
}
public string CorrelationId
{
get
{
return this._accessor.HttpContext.Request.Headers[Constants.CORRELATIONID_KEY];
}
}
}
then in startup's configureservice method register the adapter
services.AddSingleton<IRequestContext, RequestContextAdapter>();
and inject it in controller
[Route("api/[controller]")]
public class DocumentController : Controller
{
private ILogger<TransformController> _logger;
private IRequestContext _requestContext = null;
public DocumentController(ILogger<DocumentController > logger,IRequestContext requestContext)
{
_logger = logger;
_requestContext = requestContext;
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation("Start task. CorrelationId:{0}", _requestContext.CorrelationId);
// do something here
_logger.LogInformation("End task. CorrelationId:{0}", _requestContext.CorrelationId);
return result;
}
}
Depending on your needs one of following is suitable:
If you need your header values at action level, then using FromHeaderAttribute sounds better (lighter and easier).
If you need to use this header value in lower layers like Repository or DAL, which will be instantiated before Controller has been initialized, then consider to use middleware to get header values initialized and available for other components.

Query the authenticated user object too often within a http request

I am using asp.net core mvc with asp.net identity.
My ApplicationUser has a property: Country => 'GB' or 'FR' etc...
I want to set the System.Threading.Thread.CurrentThread.CurrentCulture to the value read from the applicationUser.Country. to display all datetime/number values correctly in my views as set by the user.
Thus I created this action filter:
public class LanguageActionFilter : ActionFilterAttribute
{
private readonly ILogger _logger;
private UserManager<ApplicationUser> _userManager;
public LanguageActionFilter(ILoggerFactory loggerFactory, UserManager<ApplicationUser> userManager)
{
_logger = loggerFactory.CreateLogger("LanguageActionFilter");
_userManager = userManager;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var user = await _userManager.GetUserAsync(context.HttpContext.User);
Thread.CurrentThread.CurrentCulture = new CultureInfo(user.Country);
await base.OnActionExecutionAsync(context, next);
}
}
On the action/controller where this actionfilter is set I also run this code:
public async Task<IActionResult> Index()
{
var user = await this.userManager.GetUserAsync(User);
var todos = await service.GetTodosAsync(user.Id);
return View(todos);
}
At the moment I call the this.userManager.GetUserAsync(User);
TWO TIMES !
Then I thought about passing the user object to the
context.HttpContext.Items.Add("applicationUser", user);
and grab this user object again within the Index() action method:
var user = (ApplicationUser)base.HttpContext.Items["applicationUser"];
That worked, but is that a good idea? Do you see any problems with that approach?
What you did is fine...
I'm quoting from the definition of the HttpContext class :
/// <summary>
/// **Gets or sets a key/value collection that can be used to share data within the scope of this request.**
/// </summary>
public abstract IDictionary<object, object> Items { get; set; }
Edit : It seems ef core doesn't support first level cache. So my idea went for nothing
Your code works, but i think you won't gain considerable performance.
You probably use aspnet identity with entity framework. Since ef supports first level cache. , if you call this.userManager.GetUserAsync(User); more than one, ef retrieves user entity from database only once. As a result two ways approximately have same effect for performance. I wouldn't use HttpContext.Items for your case.