How to invoke a controller method just when condition happens - asp.net-web-api2

I want to invoke controller method, in case a specific condition happens.
Can you give me an example how to implement it?
i guess it can intercepted with webapi filters..
Thanks.

I'm assuming that you are talking about invoking a method from a controller/api from another method/controller. It's actually pretty easy.
First, in your startup class you'll need to find the ConfigureServices method and add:
services.AddMvc().AddControllersAsServices();
Then, in whichever controller you need it, just create a new instance of that controller and you can call those methods. Assume you have api1 and you want to call a method from api2. I'd create the api via dependency injection:
public class api1
{
public _api2 {get;set;}
public api1()
{
_api2 = new api2();
}
}
And now you can call your methods however you'd like from api2:
public IActionResult SomeCondition()
{
if(!someCondition)
{
return _api2.YourMethod1();
}
else
{
return _api2.YourMethod2();
}
}

Related

ASP NET Core Define API Controller within class

I'm currently switching from .net framework to .net core 3.1.
Defining Api Controllers inside the namespace is all fine and works.
Now I have the case, that I need to declare the Api Controllers within another class, like this:
namespace Api.Controllers
{
public class MainClass : BaseClass
{
public MainClass()
{
}
[ApiController]
[Route("Test")]
public class TestController : ControllerBase
{
[HttpGet]
public int GetResult()
{
return 0;
}
}
}
}
The result is, that the Api Controller can't be found after calling the "AddControllers" method inside the "ConfigureServices" method of the startup class.
The MainClass is instantiated before the Startup class will be called.
I've tried to change the global route and defining an area like "{area=Test}/{controller=Test}", or set the ApiController attribute above the MainClass, but none of them worked.
Is there a way to get this working?
Looks like the default ControllerFeatureProvider does not treat nested controller types as controller. You can add (don't need to replace) your custom provider to change that behavior, like this:
public class NestedControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
if(!typeInfo.IsClass) return false;
if(typeInfo.IsAbstract) return false;
var isNestedType = typeInfo.DeclaringType != null;
var isPublic = true;
var parentType = typeInfo.DeclaringType;
while(parentType != null){
isPublic = parentType.IsPublic;
parentType = parentType.DeclaringType; ​
​ }
​ return isNestedType && isPublic; ​
}
}
Then add that provider to the ApplicationPartManager in Startup.ConfigureServices like this:
​​services
.AddMvc()
​​ .ConfigureApplicationPartManager(appPart => {
​​appPart.FeatureProviders.Add(new NestedControllerFeatureProvider());
});
If you want to replace the default ControllerFeatureProvider, just find it in the FeatureProviders and remove it. Of course then you need to ensure that your custom one should handle everything just like what done by the default logic, something like this:
​​//for IsController
return base.IsController(typeInfo) || <...your-custom-logic...>;
NOTE: You can refer to the default implementation of ControllerFeatureProvider to learn some standard logic to implement your own logic correctly. The code above is just a basic example. To me, as long as the classes inherits from ControllerBase and not abstract, they can work fine as a controller to serve requests. There would be no serious troubles except some weird conventions (e.g: class name not ending with Controller is still a controller or some standard attributes applied on the controller class are not working ...).
We should not use nested controller classes. Each controller class should be put in a separate file (as a good practice). However the point of this answer (the most interesting part that I'm pretty sure not many know about, is the use of ControllerFeatureProvider which can help you customize the features set in other scenarios). And really if you really have to stick with your design somehow, you of course have to use this solution, no other way.

Access to container of Simple Injector MVC views

In a Sitecore project I've integrated Simple Injector using this article
It uses sitecore pipelines and then uses a method in App_start
namespace BBC.App_Start
{
public class SimpleInjector : IPackage
{
public void RegisterServices(Container container)
{
GetContainer.RegisterServices(container);
container.Register(() => new SitecoreContext(), Lifestyle.Scoped);
container.Register(() => new Container(), Lifestyle.Singleton);
}
}
}
Simply I can inject container into controller constructor but can't have container in View files.
I tried to declare a static property in App-start and save container to it. but still I'm getting no registration type in Views
What is the best way to have container object in views?
As Stephen suggests in his comment, the literal answer to your question is "you shouldn't do that - because it's not really the way MVC and DI are supposed to work". The more detailed answer goes something like this:
The job of your view is to present data that it has been passed via the Model. Views should not really contain logic. Very simple stuff like "if flag is false, hide this block of mark-up" is ok, but the more complex code to work out what the value of the flag is shouldn't be in the view.
MVC tries to make our website code better by encouraging you to separate presentation (the View) from data (the Model) and logic (the Controller). This should make our code easier to work with - So if you have processing that needs doing, then it should really be happening when your controller method runs.
If your view requires some special data, best practice suggests it should work it out in the controller method and pass it to the view in the model. The code might look more like this:
public class MyModel
{
public string SpecialData { get; set; }
}
public class MyController : Controller
{
public ActionResult DoSomething()
{
// do whatever processing is needed
var somethingCalculate = resultFromYourOtherObject();
// do other stuff
var model = new MyModel() { SpecialData = somethingCalculated };
return View(model);
}
}
And then the View just needs to accept the MyModel class as its model, and render the SpecialData property - no logic required.
I think also it's considered a bad idea to have calls to fetch objects from your DI container spread about your codebase. For MVC apps, generally your DI container gets wired in to the process of creating a controller for a request when the app starts up. Rather than passing about a DI Container into your controllers, the DI framework extends the Controller-creation process, and the container isn't exposed outside of this. When the MVC runtime needs to create a controller, the controller-creation logic uses the DI framework to fetch objects for all the controller's dependencies.
Without more detail about what you actually want to achieve, it's difficult to say what the "right" approach to creating your object(s) here is, but the two most common patterns are probably:
1) Constructor injection: Your controller has a parameter which accepts the object required. The DI container creates this object for you at the point where it creates the controller, so your controller gets all its dependencies when it is created. Good for: scenarios where you know how to create the object at the beginning of the request.
public interface IMySpecialObject
{
string DoSomething();
}
public class MyController : Controller
{
private IMySpecialObject _specialObject;
public MyController(IMySpecialObject specialObject)
{
_specialObject = specialObject;
}
public ActionResult RenderAView()
{
// do some stuff
var data = _specialObject.DoSomething();
return View(data);
}
}
As long as IMySpecialObject and a concrete implementation for it are registered with your DI container when your app starts up, all is well.
2) Factory classes: Sometimes, however, the object in question might be optional, or it might require data that's not available at controller-creation time to create it. In that case, your DI framework could pass in a Factory object to your controller, and this is used to do the construction of the special object later.
public interface ISpecialFactory
{
ISpecialObject CreateSpecialObject(object data);
}
public class MyController : Controller
{
private IMySpecialFactory _specialFactory;
public MyController(IMySpecialFactory specialFactory)
{
_specialFactory = specialFactory;
}
public ActionResult RenderAView()
{
// do some stuff
if( requireSpecialObject )
{
var data = getSomeData();
var specialObject = _specialFactory.CreateSpecialObject(data);
var data = _specialObject.DoSomething();
return View(data);
}
return View("someOtherView");
}
}
But a good book on using DI may suggest other approaches that fit your specific problem better.

Phalcon Initialize() is not working

I have 2 Controllers, TEST1Controller and TEST2Controller
In TEST2Controller I have a initialize() function setting value of a property.
If I try to access TEST2Controller directly from the browser, everything works perfectly.
But when I call a TEST2Controller method from TEST1Controller, it seems that initialize() function is not being called in TEST2Controller.
TEST1Controller:
namespace Modcont\Controller;
use Modcont\Controller\Test2Controller;
class Test1Controller extends BaseController
{
function gettestAction()
{
$t = new Test2Controller(); // calling TEST2 Controller Method Within TEST1 Controller
echo $t->dotestAction(" MYAPP ");
}
}
TEST2Controller:
namespace Modcont\Controller;
class Test2Controller extends BaseController
{
public $prefix;
function initialize()
{
$this->prefix = 'your name is';
}
function dotestAction($name)
{
return $this->prefix.' : '.$name;
}
}
Phalcon offers two ways for controller initialization, thy are the initialize and onContruct methods. The basic difference between these two methods is that initialize is called only when a controller is created by the framework to proceed with the execution of an action. Since you instantiating a controller object ad-hoc, initialize will not be called, only onConstruct will. So you'll need to put your initialization logic there:
function onConstruct()
{
$this->prefix = 'your name is';
}
Also, implementing native constructors in controller is discouraged, but if you do so, make sure to call the parent constructor in your own constructor: parent::__construct();.
All this information can be found in the Docs.

Ninject Interception: Service calling intercepted method not being intercepted when called within itself

I have a service class injected with Ninject 3.0. I have set it up so it's proxy is a class proxy rather than an interface proxy. The service has 2 methods, a first one returning broad result and a second one calling the first one and filtering it. I added a interceptor to cache the result of the first method.
The interception works fine when I call the first method from outside the service.
The problem is that when the interceptor invokes the second method, it invokes it through the service itself and not through the proxy, resulting in my call to the first method from my service not being intercepted, therefor not cached.
How can I get this to work?
UPDATE: Added sample code
This is on the top of my head so sorry if anything doesn't seem to compile
Here is a sample the service class
public class Service : IService
{
[CacheMethodOutput]
public virtual object[] GetObjects(int x)
{
...
}
public virtual object GetObject(int x, int y)
{
return GetObjects(x).SingleOrDefault(o => o.y == y);
}
}
The CacheMethodOutputAttribute is a simple attribute class
Here is a sample binding (this is the way I make sure to have a class proxy instead of a interface proxy but actually leave the injected reference by interface)
// Binds by type instead of proxy to create a class proxy
Bind<Service>().ToSelf().InSingletonScope().Intercept().With<CacheAttributeInterceptor>()
Bind<IService>().ToMethod<Service>(r => r.Kernel.Get<Service>());
So when I call GetObjects from any class in which the IService is injected, the interceptor is triggered, but it's not triggered from the GetObject method of the Service itself.
The CacheAttributeInterceptor looks like this (but details of implementation is irrelevant):
public class CacheAttributeInterceptor : SimpleInterceptor
{
public ICacheManager CacheManager {get;set;}
public override void BeforeInvoke(IInvocation invocation)
{
if (Attributes.GetCustomAttribute(invocation.Request.Method, typeof(CacheMethodOutputAttribute) != null)
{
string key = GenerateKey(invocation.Request.Method.Name, invocation.Request.Method.Arguments);
object returnValue;
if (!CacheManager.TryGet(key, out returnValue))
{
invocation.Proceed();
returnValue = invocation.ReturnValue;
CacheManager.Add(key, returnValue);
}
else
invocation.ReturnValue = returnValue;
}
else
base.BeforeInvoke(invocation);
}
}
I found the solution / more detail on the problem.
If I remove the virtual modifier on the GetObject method, it will then not be intercepted anymore and when it will call GetObjects, the incerceptor will trigger.
All this got me thinking that if I want both method to be intercepted, I need to make the interceptor work deeply, if this is even possible.

Authorize Attribute not filtering on DbDataController

I'm trying to use the new MVC4 DbDataController to expose a restful data api.
My problem is trying to secure this. I have created custom authorization attributes that derive from Authorize Attribute
public class AdminOnlyAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!IsAllowed()) {
filterContext.Result = new HttpUnauthorizedResult("Not logged in");
}
...
}
And that works fine when applied to my normal controller actions. I'm trying to use the same thing in my data service like this:
[AdminOnlyAttribute]
public class DataServiceController : DbDataController<AppBuilderDataContext>
{
[AdminOnlyAttribute]
public IQueryable<Thing> GetThings()
{
return DbContext.AllMyThings();
}
}
You can see I've tried my attribute on both the controller and the action, but it's not firing for either one. I've set a breakpoint inside my authorize attribute function, and it's not getting called.
I'm pretty sure Scott Guthrie said this was going to work. Am I doing it wrong, or do I need a completely different method to secure these?
To work with an DataController or any other type derived from ApiController your attribute must derive from System.Web.Http.AuthorizeAttribute