Using MVC3 childactiononly attribute with ajax - ajax

I am using ajax to load a partial view in order to add/edit a row in a kendo grid. When I press an edit button in the row I want to also not allow the user to directly call the Home/AddModify action from the browser.
If I put [ChildActionOnly] to my "AddModify" action it does not let me load the partial view
because everything is in the ajax call, and I do not want to have it in the view somewhere like a #Html.Action("Home/AddModify",model). I also do not want to load it from the beginning when the page is loaded.
Can I call the partial view so it is only viewed on demand (dynamically)?

What you need is an AJAX only attribte Take a look at this question
Declare it like
public class AjaxOnlyAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
return controllerContext.RequestContext.HttpContext.Request.IsAjaxRequest();
}
}
and use it like
[AjaxOnly]
public ActionResult ajaxMethod()
{
}

You can use the IsAjaxRequest extension method on HttpRequestBase.
It doesn't prevent every user to call it (you cannot prevent them until it is publicly accessible) (because they can modify the headers to whatever), but for general purpose it may be good for you.
You can make an actionfilter:
public class RestrictToAjaxAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
throw new InvalidOperationException("This action is only available via ajax");
}
}
}
and put this attribute on top of your action:
[RestrictToAjax]
public ActionResult YourAction()
{
}

Related

Mvc6 versioned api actions with custom constraints

I'm making a versioned api with mvc6 and to do that I want to be able to specify for an action on which api version it should work.
My api route is: /api/{version}/... and so I want at a certain action to inspect the version route value and to see if this action is available for that version.
I want to be able to specify that as an attribute on the api action, so for example:
// This is the base api controller
[Route("api/{version:regex(^v[[0-9]].[[0-9]]$)}/[controller]")]
public abstract class ApiControllerBase { ... }
// This is an action in one of the sub classes
[HttpGet("foo")]
[ApiVersion("0.1", "0.2")] // Here! (this is params string[])
public object Foo()
{
// return
}
// This is an action in another sub class
[HttpGet("foo")]
[ApiVersion("1.0")]
public object Foo()
{
// return
}
My question is what should ApiVersion implement or extend for this to work? I don't believe action filters work as I want because I don't want to return a 404 when this doesn't match because other actions inside other controllers might be able to handle this (Later I might have HomeController with common actions and Home2Controller with extended actions that work only for 1.0).
Note that I'm not asking for an implementation of ApiVersionAttribute, I just need to know what mvc infrastructure I should hook into (action filters, route constraints, ...) that will let me create an attribute that can look into route values and say if this action is a match.
It took 4 hours analyzing the mvc6 source but it was worth it. I solved this using an action attribute implementing Microsoft.AspNet.Mvc.ActionConstraints.IActionConstraint.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ApiVersionAttribute : Attribute, IActionConstraint
{
public ApiVersionAttribute(string version)
{
Version = version;
}
public string Version { get; }
public int Order => 0;
public bool Accept(ActionConstraintContext context)
{
var routeData = context.RouteContext.RouteData;
// return ...
}
}
And then on a certain action:
[HttpGet("foo")]
[ApiVersion("0.1")]
public object Foo()
{
// return ...
}

Can I reuse a remote validation action in MVC3

I am using a Remote validation attribute on my view model to validate a Bank Account that is specified for my Company:
ViewModel:
[Remote("CheckDefaultBank", "Company")]
public string DefaultBank
{
This in the controller I have:
[HttpGet]
public JsonResult CheckDefaultBank(string defaultBank)
{
bool result = BankExists(defaultBank);
return Json(result, JsonRequestBehavior.AllowGet);
}
That all works well. But, I have two other banks related to my company as well. However, when the remote validation js calls the action it uses a parameter mactching the field name of "DefaultBank"... so I use that as a parameter in my action.
Is there some attribute I can add in the view so that it will use a parameter of say "bankId" on the ajax get so I don't need an action for each field which are basically exactly the same?
The goal here is to eliminate now having to have this in my controller:
[HttpGet]
public JsonResult CheckRefundBank(string refundBank)
{
bool result = BankExists(defaultBank);
return Json(result, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public JsonResult CheckPayrollBank(string payrollBank)
{
bool result = BankExists(defaultBank);
return Json(result, JsonRequestBehavior.AllowGet);
}
I was hoping I could do something like this in the view:
#Html.EditorFor(model => model.DefaultBank, new { data-validate-parameter: bankId })
This way I could just use the same action for all of the Bank entries like:
[HttpGet]
public JsonResult CheckValidBank(string bankId)
{
bool result = BankExists(bankId);
return Json(result, JsonRequestBehavior.AllowGet);
}
Possible?
For just such a situation, I wrote a RemoteReusableAttribute, which may be helpful to you. Here is a link to it: Custom remote Validation in MVC 3
Since MVC uses the default model binder for this, just like a normal action method. You could take a FormsCollection as your parameter and lookup the value. However, I personally would find it much easier to just use several parameters to the function, unless you start having dozens of different parameters.
You could also write a custom model binder, that would translate the passed parameter to a generic one.
Consider encapsulating the logic, "BankExists" in this case into a ValidationAttribute (Data Annotations Validator). This allows other scenarios as well.
Then use a wrapper ActionResult like the one below, which lets you pass in any validator.
[HttpGet]
public ActionResult CheckRefundBank(string refundBank)
{
var validation = BankExistsAttribute();
return new RemoteValidationResult(validation, defaultBank);
}
Here is the code for the ActionResult that works generically with Validators.
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class RemoteValidationResult : ActionResult
{
public RemoteValidationResult(ValidationAttribute validation, object value)
{
this.Validation = validation;
this.Value = value;
}
public ValidationAttribute Validation { get; set; }
public object Value { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var json = new JsonResult();
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
if (Validation.IsValid(Value))
{
json.Data = true;
}
else
{
json.Data = Validation.FormatErrorMessage(Value.ToString());
}
json.ExecuteResult(context);
}
}
As an extra enhancement consider creating a Controller Extension method to dry up your return call even more.

MVC3 Return View By Default

Basically, I was wondering if anyone knows of a way that you can set up MVC3 in a way that it will first look for an action, and if none exists, it will automatically return the view at that location. Otherwise each time I make a page, I will have to rebuild it after adding the action.
It isn't something that's stopping the project from working nor is it an issue, it would just be a very nice thing to include in the code to help with speed of testing more than anything.
EDIT:
Just for clarity purposes, this is what I do every time I create a view that doesn't have any logic inside it:
public ActionResult ActionX()
{
return View();
}
Sometimes I will want some logic inside the action, but majority of the time for blank pages I will just want the above code.
I would like it if there was any way to always return the above code for every Controller/Action combination, UNLESS I have already made an action, then it should use the Action that I have specified.
Thanks,
Jake
Why not just create a single action for this. This will look for a view with the specified name and return a 404 if it doesn't exist.
[HttpGet]
public ActionResult Page(string page)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, page, null);
if (result == null)
{
return HttpNotFound();
}
return View(page);
}
Then make your default route fall back to this:
routes.MapRoute("", "{page}", new { controller = "Home", action = "Page" });
So a request to http://yoursite.com/somepage will invoke Page("somepage")
I'm not altogether sure how useful this will be (or whether its really a good idea) but I guess if you have pages which are purely static content (but maybe use a layout or something so you can't use static html) it could be useful
This is how it could be done though anyway (as a base class, but it doesn't have to be)
public abstract class BaseController : Controller
{
public ActionResult Default()
{
return View();
}
protected override IActionInvoker CreateActionInvoker()
{
return new DefaultActionInvoker();
}
private class DefaultActionInvoker : ControllerActionInvoker
{
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var actionDescriptor = base.FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor == null)
actionDescriptor = base.FindAction(controllerContext, controllerDescriptor, "Default");
return actionDescriptor;
}
}
}

Partial ViewModels, Controllers, in Razor

Trying to get my ducks in a row with MVC3 + Razor!
I finally understand the concept of a 'View-Model' to wrap my entity classes and tailor them to a View.
Now I'm assembling a page with partial views representing different elements necessary to the page (such as drop down lists, forms, etc.) each of these will be represented by a 'View-Model' that maps to an entity class and back to my database.
First I am trying to create a partial view representing a component that is a drop-down list of elements in the database, that when selected will render another partial view, etc.
I just can't put together why I can't generate this drop-down list, and once I do how the main 'controller' maps all this together?
In short I'm curious - does each partial view need a controller even if it's based on a strongly typed model?
Breaking it down:
My Entity Model-View Wrapper (getting all the elements available from the database
*Updated* - to a working example now, note I don't think I was asking the right question before, but this will give you an idea of what I was trying to accomplish! Next step is to move all these operations 'off' the controller (and populate them in the models default constructor, for ease of use later).
CharactersListViewModel.cs
Going to move avoid the 'View Model' for now until I get a little more comfortable
Creating a partial view that displays a drop down list with the Characters' ID as a value, and name as the text, create strongly-typed view, partial view
controller for main-page in section:
HistoryController.cs
public class HistoryController : Controller
{
public ActionResult Index()
{
var list = new List<SelectListItem>();
using (var _database = new fff_newEntities())
{
foreach(Character c in (from c in _database.Characters select c)){
list.Add(new SelectListItem(){Text = c.CharacterName, Value = c.Id.ToString()});
}
}
ViewBag.Clients = list;
}
}
//
// GET: /History/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /History/Create
public ActionResult Create()
{
return View();
}
//
// POST: /History/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /History/Edit/5
public ActionResult Edit(int id)
{
return View();
}
//
// POST: /History/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
{
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
public ActionResult Delete(int id)
{
return View();
}
//
// POST: /History/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The index to display the whole page including the partial component (my drop down list)
index.cshtml:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.DropDownListFor(x => x.CurrentCharacterId, (IEnumerable<SelectListItem>)ViewBag.Clients);
On the last line here, #Html.Action(...) where do I actually create the drop-down list?
Sorry if this seems trivial but I can't wrap my head around it and I really want to learn MVC3 + Razor correctly!
A partial view is meant to abstract out some HTML/View Logic so that it can be re-used either in multiple places or for repeating (looping).
Though you can have an action that maps to the partial and if the partial in question does some explicit data access this might be the way to go but if you're just passing down all the data it needs from the controller itself then - no, you don't need a Controller/Action for it.
Since you're doing some explicit data access I would probably make an action for it...
[ChildActionOnly]
public ActionResult Characters()
{
using (var _database = new entities())
{
CharactersViewModel viewModel = new CharactersViewModel();
viewModel.Characters = _database.Characters.ToDictionary(c => c.Id, c => c.CharacterName);
return PartialView(viewModel);
}
}
In your view...
#Html.Action("Characters")
Of course there's nothing wrong with the way you're doing it but I find having it map to an action can make things easier down the road if you ever wanted to retrieve the HTML from this rendered partial view via an ajax request or something of the sort.
Notes:
Try to wrap your entity context object in a using so it can dispose of the connection.
You can use ToDictionary to select your dictionary directly from the query scope.

ActionFilterAttribute: Where is the 'Cancel' property?

Whatever happened to the Cancel property on the ActionExecutingContext? How would one abort a RenderAction when using an ActionFilterAttribute or is there another way to skin this cat?
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
return;
}
base.OnActionExecuting(filterContext);
}
The code above continues to execute the Action it has been applied to despite exiting the OnActionExecuting operation?
--- Further To original post:
Thanks for the answers below, however, I don't think I have made the context clear enough, I am trying to invalidate the following call:
<% Html.RenderAction("Menu", "Shared", new { id = Model.OtherUserId }); %>
When a user is not authenticated this action should return nothing, I could easily put an 'if' block on the view, however, I would like to keep the rule in the controller.
This worked great Mattias the result is this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new EmptyResult();
return;
}
base.OnActionExecuting(filterContext);
}
No, you can not cancel a rendering from a action filter. There are many reasons that you should not do that. What would the client see? A error page? Nothing?
I guess you are building a authorize action filter that would render something else if you are not signed in. There is one in the framework already (AuthorizeAttribute) that redirects you to the login page if you are not signed in. The way that they do it in the framework is to change the result that is being executed (filterContext.Result = [[new result]];). If you don't like how it works you can build your own implementation.
If you still need to cancel the rendering or something like that you will need to build your own ActionResult and do whatever logic you need in the Execute method.
-- Update --
If you want to use render action you should just put the logic in the controller and return empty result if you are not signed in (there is a action result called "EmptyResult" in the framework). That kind of logic belongs in the controller action.
Mattias and rjarmstrong already anwswer question. Here is full code for filter and controller:
public class CancelFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//before execution
var id = filterContext.RequestContext.HttpContext.Request.Params["id"];
if (id == "0")
{
filterContext.Result = new EmptyResult();
return;
}
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
//after execution
}
}
[CancelFilter]
public class HomeController : Controller
{
public ActionResult DoSome(string id)
{
return View();
}
...
}

Resources