I just converted a my controller to asyncController in asp.net mvc 2. (ie: spliting action method About into AboutAsync and AboutCompleted). However, I am getting a resource not found error.
My route table has not change and the only reason I can think of is because I am mvcextension project. Since it does it's own wiring of controllerFactory and creates instance from the IoC container, does it need to wire up the AsyncHttpHandler has well? Can anyone drop some hints?
thanks in advance.
A few notes for things to check for, in order:
Make sure your controller subclasses AsyncController rather than simply Controller.
The URL to hit HomeController::AboutAsync()/AboutCompleted() should be /Home/About (the Async isn't part of the URL)
If you're using a custom invoker, it must subclass AsyncControllerActionInvoker (or implement IAsyncControllerActionInvoker) rather than subclass ControllerActionInvoker directly
Make sure that you're hooking the MvcRouteHandler up to Routing (which should be the default behavior of MapRoute). If you're using a custom IRouteHandler, make sure that its GetHttpHandler() method is returning an MvcHandler. (Note - you should not subclass MvcHandler.)
Related
Is it possible to omit writing [FromBody]?
[HttpPost]
public string SomeMethod([FromBody]SomeModel model)
{
return "OK";
}
Should I apply some global attribute? How to achieve this?
The model binding attributes in MVC 5 specify a "BindingSource" for each parameter on an action and can specify for each property on a controller, too. You can see specifically the code where it picks it up for the FromBody attribute in the BodyModelBinder
Let me say first that you should beware that as of beta6 (and I think some point in beta5), having multiple parameters/properties with BindingSource.Body is not allowed, and you will not be able to use your actions if you have multiple parameters left as the default. This might actually be desirable if you want to annotate more; my guess is that you do not.
You can see in the DefaultApiDescriptionProvider where the default source is set; unfortunately, there is no hook at this point to tie into, and the method itself is private and not virtual; you'll need to re-implement the entire class.
Once you have, though, it's rather easy to register it in your ConfigureServices to use it instead:
services.TryAdd(ServiceDescriptor.Transient<IApiDescriptionProvider, YourApiDescriptionProvider>());
You can see the default being registered in the source, too.
probably more than one way to achieve this.. here is one way: a custom ActionValueBinder..
Write a custom class to extend from DefaultActionValueBinder.
public class FromBodyParameterBindingActionValueBinder : DefaultActionValueBinder
Override the GetParameterBinding method.
protected override HttpParameterBinding
GetParameterBinding(HttpParameterDescriptor parameter)
{
return parameter.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Post)
|| parameter.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Put) ?
parameter.BindWithAttribute(new FromBodyAttribute()) :
base.GetParameterBinding(parameter);
}
basically what we're saying is that if the Web API system gets a POST or PUT call, make FromBody as the default parameter binding methodology.
Once we have our custom binder, register it globally.
GlobalConfiguration.Services.Replace(typeof(IActionValueBinder), new FromBodyParameterBindingActionValueBinder());
what this does is that for every POST/PUT request, Web API will try to create the model from the request Body. you don't need to provide the [FromBody] attribute explicitly.
having said that, you need to understand the implications of what you're requesting..
Web API has some default rules on model binding. (can be changed)
primitive action parameters are first, looked into the query string
then request body.
complex action parameters are first, looked into the request body.
then query string.
also, web api can only read the first [FromBody] action parameter.. subsequent parameters cannot be read from the body.
the reason i am enumerating the above rules, is so that you're aware of the behavior when you expect some model to be created, but web api does not seem to create it from the body.
for me, explicitly providing [FromBody] is a lot more readable and predictable.. but if you're in full control of your APIs in terms of Http Verbs and expect all requests to be via the Body, it is a perfectly valid thing to do.
I have a separate project in my solution that contains some Controllers and compiled views.
I use those controllers for base classes to other controllers in my MVC application and the views are compiled using RazorGenerator.
Lets say B is Base Controller with non abstract action method SomeAction that returns View("_MyView"). _MyView.cshtml is compiled using RazorGenerator.
Lets say controller A inherits B but doesn't override SomeAction.
I've tried to make another view "~/Views/A/_MyView.cshtml" to override the default one, but it doesn't work. My question is how can I accomplish this?
ADDITIONAL INFO
1) I know that the views by default are searched in that order in those paths
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
2) Putting "~/Views/Shared/_MyView.cshtml" does override the view, but not only for controller A, but also for every other controller that inherits
B
3) Overriding SomeAction to return base.SomeAction() doesn't work
UPDATE
I have found similar question here, but doing the suggestion nothing happened
RazorGenerator Issues
I have posted my own issue here
Thank you in advance!
So far my only workaround is to install RazorGenerator on the consumer app and to also set the view _MyView.cshtml as being RazorGenerated. RazorGenator then picks up the correct view.
Another note for other visitors is not to compound the wrong view confusion with the route going to the base controller instead of the the consumer controller. In solving this issue earlier to being able to figure out the actual wrong view was being served by the right controller as the OP and I have an issue with. I have code in my base application_start that removes route duplicates.
Anyone else hitting this issue you need to update the RazorGeneratorMvcStart.cs to set PreemptPhysicalFiles = false in the master project. By default this is not the case and the views in the master project with take priority:
var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly)
{
UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal,
PreemptPhysicalFiles = false
};
ViewEngines.Engines.Add(engine);
This file is App_Start\RazorGeneratorMvcStart.cs. It is also important to Add the engine rather than Insert it. The default is to insert at position 0.
Note: I hit this issue when updating NuGet packages, it seems that the file gets overritten, resetting this back to the default behaviour.
Let's suppose I have a layer of abstract controllers, which delegates the request to its child controller class, until it reaches the implementation.
Think of it like a pipeline of controllers, that the request must go through, and includes caching the responses, authorizing and authenticating the user, validating the input and output data, handling repository access, etc.
My leaf class (the last child of the hierarchy), may have the following signature:
public class SeasonsController : DefaultPersistenceRestController
<int, Season, SeasonPutDTO, SeasonPostDTO, SeasonQueryData> {
/** Controller implementation here **/
}
The base classes have a lot of reusable code located in one module, this is good and has helped me a lot when changing the logic of my controllers at a global level.
Now, suppose SeasonsController need to call EpisodesController, for irrelevant reasons.
The call would be like this:
EpisodesController episodeController = new EpisodesController();
//Do something with EpisodesController
The problem is that I don't want EpisodesController to be accessed from the outside, such as client's request. ASP.NET automatically identifies controllers and creates a public endpoint for them, such as http://localhost:80/episodes.
I created EpisodesController because it uses a lot of logic from the controller's base classes, but I intend to use it internally.
I can desactivate authentication, authorization, cache and all other stuff that will be useless if a controller is used in this way, so that's not a problem.
However, I cannot manage to prevent ASP.NET to ignore my EpisodesController class, and to not consider it like a controller.
Is there an attribute or annotation maybe that will tell the compiler to do this? Maybe some modification in Web.config?.
Also note that I don't want to change EpisodesController's class name to another name, as it is really a controller, but an internal one.
You could try to use the IgnoreRoute extension method. Or you could try the internal as suggested by beautifulcoder. If it's in another assembly (and you can modify it) you could also make it visible to other assemblies with InternalsVisibleToAttribute.
Although to be honest, using one controller within another controller doesn't seem right to me. I would try and refactor you common functionality to services/helpers, then you could probably also make your EpisodesController into a simple service. Composition over inheritance and all that :)
If you make a controller public it will be accessible. From what I understand, you can change it to protected or internal.
In my MVC3 C# project, I have been looking at ways to construct a navigation menu in controller code and pass that to the master page. (i want to initialize the menu in controller so I can do some authorization and roles checking, etc...)
So far, I found several answers on here taht all seem to require overriding the OnActionExecuted method of a BaseController (from which all other controllers extend)
Example 1(see accepted answer): How to create a strongly typed master page using a base controller in ASP.NET MVC
Example 2: "Security aware" action link?
But wy not just put that same code in the Base Controller's constructor?
If all your controllers inherit from the same base controller, there is no reason why you could not do it this way.
The advantage of an ActionFilterAttribute is that it allows you to insert your logic on any controller by simply decorating it with the attribute, allowing you the freedom of inheriting from any base class you choose.
What is the best way to create custom OnActionExecuted code for all HTTP GET actions in a .NET MVC application?
Would you create an ActionFilter, or create a base controller, and in either of these approaches is it possible to fire the action filter only on GET requests?
My initial thinking is a base controller written as follows, but is this the best way, or am I missing something?
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (Request.HttpMethod == "GET")
{
...
}
}
You code is good. I would use:
if (string.Equals(Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
Also if you would like to create few ActionFilters for 'GET' request only you may create common base ActionFilter class and then derive all concrete action filters from it.
Controller itself is a filter so you can use the controller or you can go for a separate class/filter. You have to analyze which one suits for you. By putting the logic in the controller you may lose unit testing so if testing/SOC are important concerns then I may suggest go for a separate class that encapsulates the logic. Also, you can avoid code duplication if you have two different base controllers in an application (rarely).
The best way to do this turned out to be neither using base controller or custom action filter declared on actions. It's best to globally register the action filter using a controller factory, and requires neither inheriting from a base controller nor adding the action filter on ever controller/action. The action filter is assigned to the Controller ActionInvoker in a custom DefaultControllerFactory derivation declared in global.asax.
This blog post was useful in implementing this approach.