Overwriting UrlHelper with a CustomUrlHelper - ASP.NET CORE 2.0 - asp.net

Is there a way to force my ASP.NET Core 2.0 app to use a custom UrlHelper I've written everywhere?
I have a class with custom logic
public class CustomUrlHelper : UrlHelper { ... }
I want it to be used everywhere, so that urls are generated according to our custom business rules.

Create a CustomUrlHelper and a CustomUrlHelperFactory.
public class CustomUrlHelper : UrlHelper
{
public CustomUrlHelper(ActionContext actionContext)
: base(actionContext) { }
public override string Action(UrlActionContext actionContext)
{
var controller = actionContext.Controller;
var action = actionContext.Action;
return $"You wrote {controller} > {action}.";
}
}
public class CustomUrlHelperFactory : IUrlHelperFactory
{
public IUrlHelper GetUrlHelper(ActionContext context)
{
return new CustomUrlHelper(context);
}
}
Then open your Statup.cs file and register the CustomUrlHelperFactory in the ConfigureServices method.
services.AddMvc();
services.AddSingleton<IUrlHelperFactory, CustomUrlHelperFactory>();
After doing that, your app will use the CustomUrlHelper everywhere. That includes calls to #Url.Action("Index", "Home") from a razor page.

Related

How to inject service into custom ActionFilterAttribute (Web API)?

I tried this answer: [https://stackoverflow.com/questions/18406506/custom-filter-attributes-inject-dependency][1] to implement ActionFilterAttribute (System.Web.Http.Filters) for Web API project (not MVC). But my custom attribute never called in controller. I would be grateful for any advice.
Custom attribute:
public class MyAttribute : FilterAttribute { }
Filter:
public class MyFilter : ActionFilterAttribute
{
private readonly IMyService _myService;
public MyFilter(IMyService myService)
{
_myService = myService;
}
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
//do some with actionContext
throw new Exception("You can`t go here");
}
}
Controller method:
[My] // Not called
[HttpPost]
[Route("/do-some")]
public async Task DoSome(string myString)
{
//do some
}
Register filter:
public partial class Startup
{
protected void ConfigureApi(IAppBuilder app, IContainer container)
{
var configuration = new HttpConfiguration();
//...
var serviceInstance = container.GetInstance<IMyService>();
configuration.Filters.Add(new MyFilter(serviceInstance));
}
}
Is something wrong here?
Almost everything is fine with the your code, but you should register your filter and service in another way.
In Asp Net Core WebAPI there several ways you can register your filter:
Globally - for all controllers, actions, and Razor Pages. More information in Microsoft documentation
For only one controller/method. More information in Microsoft documentation
Example of global registration:
services.AddControllers(options =>
{
options.Filters.Add(typeof(LoggerFilterAttribute));
});
Example of method registration in Controller:
I want notice - in this case you should use ServiceFilter - this helps DI resolve any dependecines for your filter.
[HttpGet]
[ServiceFilter(typeof(LoggerFilterAttribute))]
public IEnumerable<WeatherForecast> Get()
{
}
This is my simple example for this task:
My SimpleService
public interface ISimpleService
{
void Notify(string text);
}
public class SimpleService : ISimpleService
{
public void Notify(string text)
{
Console.WriteLine($"Notify from {nameof(SimpleService)}. {text}");
}
}
ActionFilterAttribute
public class LoggerFilterAttribute : ActionFilterAttribute
{
private readonly ISimpleService _simpleService;
public LoggerFilterAttribute(ISimpleService simpleService)
{
_simpleService = simpleService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
_simpleService.Notify($"Method {nameof(OnActionExecuting)}");
}
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
_simpleService.Notify($"Method {nameof(OnActionExecutionAsync)}");
return base.OnActionExecutionAsync(context, next);
}
}
The main step - you should choose way of registration, because there is main difference between global registration and per controller/method in code.
If you want use this way of registration - you need only register global filter and this is enough. All magic will be do by WebAPI with DI registration.
services.AddControllers(options =>
{
options.Filters.Add(typeof(LoggerFilterAttribute));
});
If you want use registration per controller/method. You need to register your filter in DI. Because without it you will have Exception.
services.AddScoped<LoggerFilterAttribute>();
[HttpGet]
[ServiceFilter(typeof(LoggerFilterAttribute))]
public IEnumerable<WeatherForecast> Get()
{
}
The last step register my service
services.AddTransient<ISimpleService, SimpleService>();
Results

.NET Core 3.1 GetService return null

I am new to .NET Core and I got stuck on this.
In my startup.cs, I have
services.AddScoped<IEmailService, EmailService>();
and I have created a IHostedService:
services.AddHostedService<CooldownExpiredService>();
inside my CooldownExpiredService, I have the following code
using (var emailScope = _serviceScopeFactory.CreateScope())
{
var _emailService = emailScope.ServiceProvider.GetService<EmailService>();
}
but the object I get is null. How can I get the EmailService inside from HostedService?
i dont knwo why i should have use the interface.
emailScope.ServiceProvider.GetService<IEmailService>()
You can add Extension Method for all Services
first : add class =>
public static class ServiceTool
{
private static IServiceProvider ServiceProvider { get; set; }
public static IServiceCollection Create(IServiceCollection servicesProvider)
{
ServiceProvider = servicesProvider.BuildServiceProvider();
return servicesProvider;
}
public static T Resolve<T>()
{
return ServiceProvider.GetService<T>();
}
}
And go to the Startup and add it
ServiceTool.Create(services);
For use in controller write on constructor controller
example:
_logRepository = ServiceTool.Resolve<ILogRepository>();

Set custom path prefix in API method using Swagger in .Net Core

I would like add my custom path prefix using swagger in .Net Core API methods.
For example, my API methods are declared like this:
[Route("api/v1/Customer")]
[HttpGet]
public async Task<IActionResult> Customer()
{
// some implementation
return Ok();
}
So currently, If I invoke the API using http://localhost:50523/api/v1/Customer it works perfectly fine.
Now, I want to add some custom path prefix. E.g. /some/custom/path/ before the actual API method path. Which means that-- if I invoke the API using http://localhost:50523/some/custom/path/api/v1/Customer it should work.
I want to achieve this using Swagger in .Net core, and I do not want to change the API path on action method level since I have hundred of API method written and I do not want to change the URL on each action method.
Any help will be greatly appreciated.
In .Net 5.0
public class PathPrefixInsertDocumentFilter : IDocumentFilter
{
private readonly string _pathPrefix;
public PathPrefixInsertDocumentFilter(string prefix)
{
this._pathPrefix = prefix;
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var paths = swaggerDoc.Paths.Keys.ToList();
foreach (var path in paths)
{
var pathToChange = swaggerDoc.Paths[path];
swaggerDoc.Paths.Remove(path);
swaggerDoc.Paths.Add($"{_pathPrefix}{path}", pathToChange);
}
}
}
To apply the filter
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info {Title = "MyApp", Version = "v1"});
... other setup
options.DocumentFilter<PathPrefixInsertDocumentFilter>("api");
});
If you add a DocumentFilter you can add the prefix to all the paths you want to change.
public class PathPrefixInsertDocumentFilter : IDocumentFilter
{
private readonly string _pathPrefix;
public PathPrefixInsertDocumentFilter(string prefix)
{
this._pathPrefix = prefix;
}
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
var paths = swaggerDoc.Paths.Keys.ToList();
foreach (var path in paths)
{
var pathToChange = swaggerDoc.Paths[path];
swaggerDoc.Paths.Remove(path);
swaggerDoc.Paths.Add(new KeyValuePair<string, PathItem>("/" + _pathPrefix + path, pathToChange));
}
}
}
You then add the filter in your swagger set up:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info {Title = "MyApp", Version = "v1"});
... other setup
options.DocumentFilter<PathPrefixInsertDocumentFilter>("api");
});
This doesn't change your API - we use this for working with a reverse proxy in production where we use the prefix to route the request to the appropriate container but strip it out.
Maybe You can using Route attribute in your Controller class like:
[Route("/some/custom/path/")]
public class CustomerController
{
[Route("api/v1/Customer")]
[HttpGet]
public async Task<IActionResult> Customer()
{
// some implementation
return Ok();
}
}
Hope it works for you
you can use [Route("prefix/[controller]")] top of your api controller
[Route("prefix/[controller]")]
public class MyController : ControllerBase
{
[Route("api/v1/Customer")]
[HttpGet]
public async Task<IActionResult> Customer()
{
// some implementation
return Ok();
}
}

An error occurred when trying to create a controller of type 'XXXXController'. Make sure that the controller has a parameterless public constructor

I have created a asp.net web api project and implemented the below HTTP GET method in AccountController and the related service method & repository method in AccountService & AccountRepository respectively.
// WEB API
public class AccountController : ApiController
{
private readonly IAccountService _accountService;
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
[HttpGet, ActionName("UserProfile")]
public JsonResult<decimal> GetUserSalary(int userID)
{
var account = _accountService.GetUserSalary(userID);
if (account != null)
{
return Json(account.Salary);
}
return Json(0);
}
}
Service / Business Layer
public interface IAccountService
{
decimal GetUserSalary(int userId);
}
public class AccountService : IAccountService
{
readonly IAccountRepository _accountRepository = new AccountRepository();
public decimal GetUserSalary(int userId)
{
return _accountRepository.GetUserSalary(userId);
}
}
Repository / Data Access Layer
public interface IAccountRepository
{
decimal GetUserSalary(int userId);
}
public class AccountRepository : IAccountRepository
{
public decimal GetUserSalary(int userId)
{
using (var db = new AccountEntities())
{
var account = (from b in db.UserAccounts where b.UserID == userId select b).FirstOrDefault();
if (account != null)
{
return account.Salary;
}
}
return 0;
}
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IAccountService, AccountService>();
container.RegisterType<IAccountRepository, AccountRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
But when I invoke the API method GetUserSalary() I get an error saying
An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor.
Check that you did not forget to register Unity IoC container itself:
if you use ASP.NET Framework it could be - Global.asax or Startap.cs (Owin) via UnityConfig.RegisterComponents() method.
if you use ASP.NET Core then in the Startup.cs file (I was unable to find official guides for its configuting)
Your current constructor has parameters (or args if you prefer).
see:
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
All you need to do is add a "Parameter-less Constructor" into the controller as well.
public AccountController()
{
}
Parameter-less constructors are usually above the ones that have params, though as far as I am aware this is only due to standards not any actual effect(s) it may cause.
There is also an already existing issue/question similar to this I will link below that may provide further details.
Make sure that the controller has a parameterless public constructor error

Access Viewbag property on all views

How can I access some ViewBag properties across all my views? I want to have some information like current user name, etc accessible everywhere, but without having to to specifically define the properties in each ActionResult method on my project
The best and straight forward way to accomplish your requirement is to make a Custom Base Controller and inherit your Controller from this Base Controller.
public class MyBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewBag.someThing = "someThing"; //Add whatever
base.OnActionExecuting(filterContext);
}
}
Now instead of inheriting Controller class,inherit MyBaseController in your Controller as shown :-
public class MyOtherController : MyBaseController
{
public ActionResult MyOtherAction()
{
//Your Stuff
return View();
}
//Other ActionResults
}
You can achieve what you want in a number of ways, each one with their pros and cons.
1. With a Base Class
public class BaseController : Controller
{
protected override ViewResult View(IView view, object model)
{
this.ViewBag.MyProperty = "value";
return base.View(view, model);
}
}
PROS: Quite simple to implement, few lines of code, highly reusable, can be opted-out at will (see comments below).
CONS: Being forced to derive all your controllers from a base class might have some impact, especially if you have a lot of controllers already in place and/or you need to derive them from other base classes.
2. With a Module
public class ViewBagPropertyModule: Module
{
protected override void AttachToComponentRegistration(IComponentRegistry cr,
IComponentRegistration reg)
{
Type limitType = reg.Activator.LimitType;
if (typeof(Controller).IsAssignableFrom(limitType))
{
registration.Activated += (s, e) =>
{
dynamic viewBag = ((Controller)e.Instance).ViewBag;
viewBag.MyProperty= "value";
};
}
}
}
PROS: None I’m aware of.
CONS: None I’m aware of (except being a bit counterintuitive).
3. With a RegisterController Hook
builder.RegisterControllers(asm)
.OnActivated(e => {
dynamic viewBag = ((Controller)e.Instance).ViewBag;
viewBag.MyProperty = "value";
});
PROS: Fast, secure, reusable: ideal for any IoC design pattern.
CONS: Not always suited for small project and/or simple websites: if you’re not using IoC you’re often not using RegisterController at all.
4. With an ActionFilter attribute
public class MyPropertyActionFilter : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.Controller.ViewBag.MyProperty = "value";
}
}
and then in your Global.asax.cs file:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new MyPropertyActionFilter(), 0);
}
PROS: Easily the less-obtrusive method amongst those mentioned.
CONS: None I’m aware of.
I also wrote this article on my blog explaining all the above methods.
One way: Create a custom attribute, then you can apply it globally in the FilterConfig. Then you don't have to do anything in your controllers.
public class MyCustomViewActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
dynamic ViewBag = filterContext.Controller.ViewBag;
ViewBag.Id = "123";
ViewBag.Name = "Bob";
}
}
In App_Start/FilterConfig.cs:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyCustomViewActionFilter());
}
Another way if all you need is the User information. You can add the following to the top of your view:
#using Microsoft.AspNet.Identity
Then access your User Name using the following syntax:
#User.Identity.GetUserName()
You can also override the IPrincipal implementation and provide your own properties and methods to add more information you need to render.
UPDATE: looking at MVC 6 in Asp.Net vNext this is actually baked into the framework. http://www.asp.net/vnext/overview/aspnet-vnext/vc#inj
My current solution:
Create a base controller with all needed properties (very useful and advisable).
public abstract class BaseController : Controller {
public string MyProperty { get; set; }
}
Inherits all your controllers, from the base controller.
public class MyController : BaseController {
//you can read your property here
}
In your views, add this line just after the "#model" sentence:
#{ BaseController ctr = ViewContext.Controller as BaseController; }
Now, you can use the property in your view, without populate the ViewBag, without the need of check and cast the ViewBag values, etc.
In the view, you can use an simple inline expression:
#(ctr.MyProperty)
Or do some magic logic...
#{
if(ctr.MyProperty == "whatelse") {
//do ...
}
}
Easy, fast and comfortable.
For Net Core 5 Mvc app:
Create a ActionFilter class first:
public class GlobalSettingFilter : IActionFilter
{
private IConfiguration configuration;
//For example will get data from the configuration object
public GlobalSettingFilter(IConfiguration configuration)
{
this.configuration = configuration;
}
public void OnActionExecuting(ActionExecutingContext context)
{
//Populate the ViewData or ViewBag from your data source
(context.Controller as Controller).ViewData["helpUrl"] = configuration.GetValue<String>("helpUrl");
}
public void OnActionExecuted(ActionExecutedContext context){}
}
Then, on Startup add:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllersWithViews(options =>
{
options.Filters.Add(new GlobalSettingFilter(Configuration));
});
}
Just for the sake of completeness, to get the configuration object use:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
...
}
You can create a base controller that is inherited by all of your controllers, and in this controller (the base one) add:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Fill your global viewbag variables here
}

Resources