WebAPI controller inheritance and attribute routing - c#

I have few controllers that inherit from the same base class. Among the different actions that they don't share with each other, they do have a few that are completely identical. I would like to have these on my base class because they all work completely the same it's just that they're accessed through different routes.
How should I define these actions with several different routes?
My inherited classes also have a RoutePrefixAttribute set on them so each of them is pointing to a different route.
Example
I have base abstract class called Vehicle and then inherited Car, Bike, Bus etc. All of them would have common action Move()
/bus/move
/car/move
/bike/move
How can I define action Move() on my base class Vehicle so that it will be executed on each subclass route?

Check the answer I gave here WebApi2 attribute routing inherited controllers, which references the answer from this post .NET WebAPI Attribute Routing and inheritance.
What you need to do is overwrite the DefaultDirectRouteProvider:
public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider {
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor) {
// inherit route attributes decorated on base class controller's actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
}
}
With that done you then need to configure it in your web API configuration:
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
.....
// Attribute routing (with inheritance).
config.MapHttpAttributeRoutes(new WebApiCustomDirectRouteProvider());
....
}
}
You will then be able to do what you described like this:
public abstract class VehicleControllerBase : ApiController {
[Route("move")] // All inheriting classes will now have a `{controller}/move` route
public virtual HttpResponseMessage Move() {
...
}
}
[RoutePrefix("car")] // will have a `car/move` route
public class CarController : VehicleControllerBase {
public virtual HttpResponseMessage CarSpecificAction() {
...
}
}
[RoutePrefix("bike")] // will have a `bike/move` route
public class BikeController : VehicleControllerBase {
public virtual HttpResponseMessage BikeSpecificAction() {
...
}
}
[RoutePrefix("bus")] // will have a `bus/move` route
public class BusController : VehicleControllerBase {
public virtual HttpResponseMessage BusSpecificAction() {
...
}
}

This is what I did and it worked the way you mentioned in your question.
I created base ApiController class and inherited all my API controllers from it. I defined Delete operation in my Base class (which returns string "Not Supported") and didn't define delete on any of my child controller. Now when I do a delete on any of my controller I get the message "Not Supported" i.e. Base class's delete is called. ( I am doing Delete request on Child, and not on base i.e. /Bike/move)
But if I define a Delete on any of the controller it gives me warning of Hiding base implementation, but on doing Delete request for api I get - "An error has occurred."
I haven't tried doing RoutePrefix way.

Related

Inject object to the constructor of controller using unity container

I couldn’t find any similar issue so I’m writing this post. There is sample controller with private field IBaseClass. Sample code looks like follows:
public class TmpController : Controller
{
private IBaseClass _baseClass;
public TmpController()
{
_baseClass = new BaseClass(this);
}
}
public interface IBaseClass
{
//...
}
public class BaseClass : IBaseClass
{
protected TmpController TmpController;
public BaseClass(TmpController tmpController)
{
TmpController = tmpController;
}
//IBaseClass implementation
}
My question is; how to inject BaseClass object to the constructor of TmpController using Unity framework?
I want to make my controller "slimmer". I want to put the logic about validation and preparing dataSource of my controls like comboBox etc. to different class. I try to make some kind of SOC in my .Web project in that very specific case, which will make my controller easier to read and maintain. I'm using approach one controller per one view but I met the case with very complex form. Currently I have controller with more than 3000 lines of code and it's hard to maintain so I want to do something with it.
And yes I'm using Services and Repositories but the problem is about validation of ViewModels, mapping ViewModel objects into DTOs and backwards, preparing data source of given components etc.
#Razem, what you guess from my comment is correct. And the minus point you described is also valid.
What you are asking "Service depending on the controller" can surely be achieved but that would be a bad design.
Currently BaseClass is only dependent on TempController. How would you handle the scenario when you need the BaseClass in some other controller also? The code will start breaking and you will end up adding new dependency to BaseClass.
Also as per the design recommendations Top Layers should be dependent on the Bottom Layers not the vice versa.
Being said that, you can still achieve the feature you are looking for that too by making controller dependent on the IBaseClass.
I am not sure the specific reasons you need to access controller inside BaseClass. I have made certain assumptions while creating following suggestions. One of such assumption is BaseClass, IBaseClass and Controller classes are part of the same assembly.
//Have a BaseController Class with the properties and/or method which you will be using in the `BaseClass` class and make them virtual so that derived controller classes can override them to have specific implementation.
public class BaseController : Controller
{
public virtual string ControllerMethod()
{
return "Controller Method from Base Controller";
}
public virtual string RandomValue
{
get
{
return "Random value from Base Controller";
}
}
}
Create a method in IBaseClass which will Set the Controller for it.
public interface IBaseClass
{
void SetController(BaseController controller);
void Method1();
}
public class BaseClass : IBaseClass
{
private BaseController controller;
public void SetController(BaseController controller)
{
this.controller = controller;
}
public void Method1()
{
var str = this.controller.RandomValue;
}
}
And derive the TempController from the BaseController and make it dependent on IBaseClass. And in the constructor of TempController call SetController method of IBaseClass by passing this argument to it. You also can override method/properties of BaseController here.
After this you can call any method of IBaseClass without passing controller instance to it.
public class TempController : BaseController
{
private IBaseClass baseClass;
public HomeController(IBaseClass productService)
{
this.baseClass = productService;
this.baseClass.SetController(this);
}
public override string RandomValue
{
get
{
return "Random value from Derived Class.";
}
}
public ActionResult Index()
{
this.baseClass.Method1();
ViewBag.Title = "Home Page";
return View();
}
}
Install nuget package Unit.Mvc in your web project. Open file Unity.Config located under App_Start folder and change method RegisterTypes as following.
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IBaseClass, BaseClass>(new PerRequestLifetimeManager());
}
I am sure I don't need to explain how this is going to work.
P.S. : You need to make sure that you calls IBaseClass.SetController method in controller constructor to avoid NullReferenceException when you use controller in BaseClass. This is small overhead you need to take to achieve good and maintainable design.

Unit testing non-public methods in dotnet core controller

Suppose in an MVC5 controller I had a method in my controller that gets called by other methods in the controller, but I don't want it available to a user. If I wanted to be able to mock it, it would look like this:
[ChildActionOnly]
public virtual string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
Or I could have tossed [assembly: InternalsVisibleTo("MyLittleProject.Tests")] and [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] (for Moq) into AssemblyInfo.cs and marked the method as internal instead of public:
internal virtual string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
Now that there is no ChildActionOnly and I don't see an AssemblyInfo.cs file in my new ASP.NET Core project, how would I have methods in my controller class which web users cannot access but can still be mocked?
In ASP.NET Core the attribute is called NonActionAttribute.
[NonAction]
public virtual string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
Imho its better than internal.
You can extract that method to a class , i.e. named SpecialFormatter, and inject to the controller via DI. To test your controller you can mock this class.
class SpecialFormatter
{
public string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
}
Then in your controller
class SomeController : Controller
{
private SpecialFormatter _formatter;
public SomeController(SpecialFormatter formatter)
{
_formatter = formatter;
}
public ActionResult SomeAction(string input)
{
string output = _formatter.DoSpecialFormatting(input);
// stuff
}
}

Get base class Member of controller in OnEntry in OnMethodBoundaryAspect in PostSharp

I want access base class member in our Log Aspect Class. I have one base controller & that controller inherit by Test controller & in Test Controller i implemented AOP Aspect.
In BaseContoller i have a member _userSession. I initializing _userSession when BaseContoller's Constructor is call. And after call TestController first AOP Aspect is called. I want Access _userSession on OnEntry method of AOP.
LogAspect Class
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method)]
public class LogAspect:PostSharp.Aspects.OnMethodBoundaryAspect
{
public object UserData;
public override void OnEntry(PostSharp.Aspects.MethodExecutionArgs args)
{
LogManager.Info(string.Format("Starting - {0}-{0}",args.Instance, new StackTrace().GetFrame(1).GetMethod().Name));
// want access PCX.Portal.Controllers.BaseController._userSession member here its showing in quick watch like this
//((PCX.Portal.Controllers.BaseController)(args.Instance))._userSession
LogManager.Info(string.Format("User data - {0}", FrameworkHelper.Helper.JSONHelper.GetJSON(UserData)));
if(args.Arguments.Count>0)
{
foreach (var item in args.Arguments)
{
LogManager.Info(string.Format("arguments - {0}", FrameworkHelper.Helper.JSONHelper.GetJSON(item)));
}
}
base.OnEntry(args);
}
Base controller
public class BaseController : Controller
{
public UserSession _userSession { set; get; }
AuthenticationManager _authenticationManager = new AuthenticationManager();
public BaseController()
{
//initializing _userSession here
_userSession.userid=4 ;
_userSession.customerId=5 ;
}
}
Test Controller
[LogAspect]
public class TestController : BaseController
{
public ActionResult Index()
{
return View();
}
}
As documentation states:
MethodExecutionArgs class contains property Instance that:
Gets or sets the object instance on which the method is being executed.
as long as your method is not static you will get the object that is the this inside that method. Now you need to just cast it to BaseController as your property is public you will be able to access it.
if(args.Instance != null){
var baseController = (BaseController)args.Instance;
baseController._userSession
}
Although this is what you asked for I feel a need to remark that this approach limits your aspects usability to only instance methods of classes that inherit from BaseController. If you are able to create/retrieve form somewhere the session data in that parameterless constructor you can do it in aspect as well.

WebApi Route with Nested Class

Is it possible to MapHttpRoute to a nested ApiController class? If yes, what RouteTemplate achieves it?
Example nested controller:
public class whatever
{
public class NestedController : ApiController
{
[HttpGet]
public object five()
{
return 5;
}
}
}
I've tried using
GET ApiRoute/whatever+nested/five
as typeof(whatever.NestedController) reports whatever+NestedController but I get
No type was found that matches the controller
It seems that the routing engine looks for concrete classes only. It doesn't matter which assembly they're in or which namespace they're in but they can't be nested in another class and they can't be abstract.
One benefit (although it's a stretch) is this is that you can "hide" controllers in a DLL and activate them in a Web App like this:
DLL:
public class Wrapper { public class MyController : ApiController { /*code goes here*/ } }
Web App:
public class MyController : Wrapper.MyController { }
All that said, I don't recommend it. It's best to use things as designed because you risk opening a security hole or making the application fragile to API updates.

Creating Custom Base controller for capturing query string values. GOOD or BAD?

This question is basically to know and justify whether what I am doing is a good practice or if there are any other ways to achieve the solution for the problem.
Here is the problem....
* I have an MVC application with many controllers and action methods. Customers will be accessing different action methods from different sources (google, bing, third party sites) adding a query string signature at the end.
* Since I dont want to check the querystring value in every action of the controller. I created a new controller "BaseController.cs" which inherits from the MVC Controller class. And implements the following code. All the application controllers will inherit from the BaseController.
public class BaseController : Controller
{
public BaseController():base()
{
string siteReference = HttpContext.Request["ref"];
}
}
I am requesting the experts of MVC to suggest better ways. Thanks in advance.
-KK
There is a less obstrussive way to do it, you can use a global action filter to do the same without having to use a base controller, and you favor composition over inheritance.
public class GetQueryString: IActionFilter, IMvcFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
string siteReference = filterContext.HttpContext.Request["ref"];
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
public bool AllowMultiple
{
get { return false; }
}
public int Order
{
get { return Filter.DefaultOrder; }
}
}

Categories