using filters with autofac in webapi2 - autofac

I have a actionfilter something as below.. The filter basically adds a few attributes to the header of the response..
public class myHeaderAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response != null)
//my code goes here
base.OnActionExecuted(actionExecutedContext);
}
}
I would normally call this in WebApiConfig.Register as config.Filters.Add(new myHeaderAttribute());
I wish to use Autofac in my project..
There is a page in autofac site (http://docs.autofac.org/en/latest/integration/webapi.html)which speaks of implementing IAutofacActionFilter.
But, I'm not very clear as to what I'm supposed to do.
I can create another class which implements IAutofacActionFilter and add the onActionExecuted method.
But do I also keep my present class or remove it along with the line in WebApiConfig.Register.
Also the page speaks of registering the Autofac filter provider as well as the class which implements IAutofacActionFilter. But no complete example exists.
Also, it speaks of using 'service location' in case we need per-request or instance-per-dependency services in our filters.
The whole thing seems a little too confusing to me. I would sincerely appreciate if someone who understands these concepts and has used Autofac in a web api2 project could guide me.
Thanks

Remove it. It explains exactly in the docs you reference yourself that it uses its own action filter resolver. See section "Provide filters via dependency injection".
Update
First register the filter provider:
var builder = new ContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
Then register your actionfilter like so:
builder.Register(c => new myHeaderAttribute())
.AsWebApiActionFilterFor<YourController>(c => c.YourMethod(default(int)))
.InstancePerApiRequest();
So complete code:
var builder = new ContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
builder.Register(c => new myHeaderAttribute())
.AsWebApiActionFilterFor<YourController>(c => c.YourMethod(default(int)))
.InstancePerApiRequest();
It's all right there in the docs. If you have any specific question then you can ask seperately. But this is becoming to be too broad.

Related

API versioning in ASP.NET Web API

I have an ASP.NET Web API I wrote and have published. Now that its out there we are looking at doing some improvements, and these improvements involve changes to certain calls which means we need to version to keep existing clients working.
I have used attribute routing so far in my app. Methods are invoked by: Controller/Action via RoutePrefix and Route attributes.
When I do need to create a V2 of my classes, I only want to recreate the classes that have actually changed, and redirect other routes back to v1 classes because they haven't changed. (Otherwise I just end up with a lot of boilerplate code, or duplicate code).
What I want to do is have the following routes work for my v1 version of classes:
Controller/Action
For V2 I want any new classes to go to V2, and any classes that haven't changed I want to return the HttpControllerDescriptor from V1 class. The route would look like v2/Controller/Action but would be redirected to Controller/Action.
I've implemented a IHttpControllerSelector and return the appropriate HttpControllerDescriptors but its not making the call into the method. I believe its because the routing information doesn't match the action. (When I put in an IHttpActionSelector and trace the exception it says "multiple actions were found that match the request).
So, I'm guess I'm wondering: Is this even possible? Is this the best way to achieve what I'm trying to do?
Here is what I implemented for versioning support in asp.net web api. Important to note I did not use attribute routing but explicit routes in WebApiConfig.cs so if you want to follow this pattern you would need to switch back to explicit routes. Also I do not prefer version information in the actual route, I use a custom (ie. "version") parameter in Accept header. I also set the version per mime type as in the below example. If version number is not set by the client or if the requested version does not exist this will fall back to default controller.
Create a class and inherit from DefaultHttpControllerSelector so you can fallback to base class behavior when you wanted to.
Override SelectController method as such:
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IDictionary controllers = GetControllerMapping();
IHttpRouteData routeData = request.GetRouteData();
string controllerName = (string)routeData.Values["controller"];
HttpControllerDescriptor controllerDescriptor;
if (string.IsNullOrWhiteSpace(controllerName))
{
return base.SelectController(request);
}
if (!controllers.TryGetValue(controllerName, out controllerDescriptor))
{
return null;
}
string version = GetVersionFromAcceptHeader(request);
if (string.Equals(version, "1"))
{
return controllerDescriptor;
}
string newName = string.Concat(controllerName, "V", version);
HttpControllerDescriptor versionedControllerDescriptor;
if (controllers.TryGetValue(newName, out versionedControllerDescriptor))
{
return versionedControllerDescriptor;
}
return controllerDescriptor;
}
Register this controller selector in your webapiconfig Register method:
config.Services.Replace(typeof(IHttpControllerSelector), new YourControllerSelector(config));

MVC 6 Attribute Routing using the new "[controller]/[action]" Tokens and Areas

OK, I know the easiest way to use Attribute Routing in MVC 6 is:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
And here is the controller code using the new Tokens (without Areas):
[Route("[controller]/[action]")]
public class HomeController : Controller
{
}
And here is the controller code using the new Tokens (with Areas):
[Area("MyArea")]
[Route("[controller]/[action]")]
public class HomeController : Controller
{
}
Questions:
Is this how MS wants you to code your controllers using Areas and Tokens?
Or is there a cleaner way?
Could they have somehow created an [area] Token?
Lastly, I know I can play this game, but isn't the 1st convention-based approach - app.UseMvc() - the simplest?
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
});
}
Microsoft gives you two options, each with own pros and cons. You should decide which one is better based on your context/needs.
Convention based routing
Pros:
It is simpler, instead of defining everything on an per-action level you just decide once and for all how your urls will looks like .
Perfect when your urls exactly match controller/action names.
If you want to change and url you need change the name of class/method.
Perfect for projects with clean and predictable url structure.
Perfect for quickly prototyping a new project.
Slightly easier for developers -> by knowing an url you know in which controller/action lies the functionality
Cons:
You loose little bit of control
Attribute-based routing
Pros:
Gives you total control over how the url looks like, for example for SEO purposes.
If you want to change an url you do not need to change the name of class/method.
Perfect when your urls do not match controller and action names or you want to hand-craft them (i.e customer wants that).
Perfect for maintaining backward compatibility, when you have an legacy project and want to have compatible url structure.
Cons:
Requires little bit more work, as you need to define the routes in your code. Please note that adding an attribute to a class/method is a matter of seconds.
How to decide which one to use:
If you have/expect to have very few routes.MapRoute() calls -> use convention routing as its simpler
If you have/expect to have lots of routes.MapRoute() calls -> use attribute routing

Performing Explicit Route Mapping based upon Web Api v2 Attributes

I'm upgrading a custom solution where I can dynamically register and unregister Web Api controllers to use the new attribute routing mechanism. However, it seems to recent update to RTM break my solution.
My solution exposes a couple of Web Api controllers for administration purposes. These are registered using the new HttpConfigurationExtensions.MapHttpAttributeRoutes method call.
The solution also allows Web Api controllers to be hosted in third-party assemblies and registered dynamically. At this stage, calling HttpConfigurationExtensions.MapHttAttributeRoutes a second time once the third-party controller is loaded would raise an exception. Therefore, my solution uses reflection to inspect the RoutePrefix and Route attributes and register corresponding routes on the HttpConfiguration object.
Unfortunately, calling the Web Api results in the following error:
"No HTTP resource was found that matches the request URI".
Here is a simple controller that I want to use:
[RoutePrefix("api/ze")]
public sealed class ZeController : ApiController
{
[HttpGet]
[Route("one")]
public string GetOne()
{
return "One";
}
[HttpGet]
[Route("two")]
public string GetTwo()
{
return "Two";
}
[HttpPost]
[Route("one")]
public string SetOne(string value)
{
return String.Empty;
}
}
Here is the first solution I tried:
configuration.Routes.MapHttpRoute("ZeApi", "api/ze/{action}");
Here is the second solution I tried:
var type = typeof(ZeController);
var routeMembers = type.GetMethods().Where(m => m.IsPublic);
foreach (MethodInfo method in routeMembers)
{
var routeAttribute = method.GetCustomAttributes(false).OfType<RouteAttribute>().FirstOrDefault();
if (routeAttribute != null)
{
string controllerName = type.Name.Substring(0, type.Name.LastIndexOf("Controller"));
string routeTemplate = string.Join("/", "api/Ze", routeAttribute.Template);
configuration.Routes.MapHttpRoute(method.Name, routeTemplate);
}
}
I also have tried a third solution, whereby I create custom classes that implement IHttpRoute and trying to register them with the configuration to no avail.
Is it possible to use legacy-style route mapping based upon the information contained in the new routing attributes ?
Update
I have installed my controller in a Web Application in order to troubleshoot the routing selection process with the Web Api Route Debugger. Here is the result of the screenshot:
As you can see, the correct action seems to be selected, but I still get a 404 error.
Update2
After further analysis, and per Kiran Challa's comment below, it seems that the design of Web Api prevents mixing attribute routing and conventional routing, and that what I want to do is not possible using this approach.
I have created a custom attribute [RouteEx] that serves the same purpose of the Web Api [Route] attribute, and now my code works perfectly.
I guess, since this is not possible using the conventional attribute routing, none of the answers on this question could legitimately be consisered valid. So I'm not nominating an answer just yet.
You shouldn't be required to use reflection and inspect the attribute-routing based attributes yourself. Attribute routing uses existing Web API features to get list of controllers to scan through.
Question: Before the switch to attribute routing, how were you loading these assemblies having the
controllers?
If you were doing this by IAssembliesResolver service, then this solution should work even with attribute routing and you should not be needing to do anything extra.
Regarding your Update: are you calling MapHttpAttributeRoutes?

Zend_Form submission > Resource 'controller::action' not found

I'm taking over a site that's gone through several developers. The site is using Zend version 1.12.0, according to Zend_Version::VERSION, which is a new framework for me. On the site, there's a form class called App_Form_Customers_Edit, which extends Zend_Form. The form's action is /customers/edit, and when submitted, the method editAction of CustomersController is executed.
So, to create a new form, I created a new class App_Form_Customers_EditAddress in the same directory as App_Form_Customers_Edit, and set it's action to /customers/editaddress, created a function called editaddressAction in the CustomersController class and tested the form.
But I get an error saying "Resource 'customers::editaddress' not found"
The form itself is displaying properly, and as far as I can tell I'm using the exact same pattern as the other form which works, and apart from not using the zf command, the same method prescribed here in the Zend documentation: http://framework.zend.com/manual/1.12/en/learning.quickstart.create-form.html
What do I need to do to get my new form working? Do I need to update .zfproject.xml? I can't see anything in there that's related to the working form.
Here's the code for App_Form_Customers_Edit:
class App_Form_Customers_Edit extends Zend_Form
{
public function init ()
{
$this->addPrefixPath('App_Form', 'App/Form/');
$this->setMethod('post');
// ... The rest is just calls to $this->addElement
}
}
And for EditAddress:
class App_Form_Customers_EditAddress extends Zend_Form
{
public function init ()
{
$this->addPrefixPath('App_Form', 'App/Form/')
->setMethod('post')
->setAction('/customers/editaddress');
$this->addElement('submit', 'active', ['value' => 'Activate']);
$this->addElement('submit', 'remove', ['value' => 'Remove']);
$this->addElement('hidden', 'id');
}
}
Check for acl declarations. If you are using acl and you have not declared rules for the action, you might get this type of error.
Best guess:
Your former developer has implemented a custom route somewhere. Probably in the application.ini or the boostrap.php. This custom route is looking for specific urls and /customers/edit conforms to a valid route but /customers/editaddress does not.
I think this is likely because your error is a missing resource rather then a 'page not found' or a missing controller or missing action message. So it seems as though the router is trying to match an invalid resource to a valid route.
Good Luck

Zend Framework 2: How can I add in RESTful API a custom http method?

I'm trying to create a custom http method in RESTful API. I was reading the documentation and it is said that you can do it buy adding a simple action in controller and then for example conifg your route with child routes with action => action_name but in the code I have spotted addHttpMethodHandler() method in Zend\Mvc\Controller\AbstractRestfulController.php so in controller construct method I have added:
$add = function () {
return new JsonModel(array(
'id' => 2222,
));
};
$this->addHttpMethodHandler('someAction', $add);
var_dump($this->customHttpMethodsMap);
With the var_dump I can see that this new function is added but I just wonder how can I call it or maybe I'm missing the point.
Regards,
I actually wrote a blog post on this because I had so much trouble too.
The problem is that in addition to calling addHttpMethodHandler within the abstract restful controller, you also need to make sure that the Zend Request class knows that your http method exists.
Here is a link to a better explanation: http://richardbrock1.wordpress.com/2013/03/23/custom-http-methods-in-zf2/