46

I want to use partial views with AJAX calls in ASP.NET MVC, and this is the first time I'm using it. I just searched to see if there is anything special I should know beforehand, and one of'em that I'm curious about, is to see if there is any special attribute that should be set or is related to AJAX calls? Something like [ChildActionOnly] or [HttpGet]

Saeed Neamati
  • 35,341
  • 41
  • 136
  • 188

6 Answers6

85

I don't think there is built in attribute for ajax, but you can create your own AjaxOnly filter like this:

public class AjaxOnlyAttribute : ActionMethodSelectorAttribute 
{
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return controllerContext.RequestContext.HttpContext.Request.IsAjaxRequest();
    }
}

And decorate your action methods like this:

[AjaxOnly]
public ActionResult AjaxMethod()
{
   
}

See Also: ASP.NET MVC Action Filter – Ajax Only Attribute for another way of implementing this

KyleMit
  • 30,350
  • 66
  • 462
  • 664
Muhammad Adeel Zahid
  • 17,474
  • 14
  • 90
  • 155
19

ASP.NET MVC provides an extension method to check if an Request is an Ajax Request. You can use it to decide if you want to return a partial view or json result instead of a normal view.

if (Request.IsAjaxRequest())
{
    return PartialView("name");
}
return View();

To limit an action method to Ajax calls only you can write a custom attribute. In case of a normal request this filter will return a 404 not found http exception.

[AttributeUsage(AttributeTargets.Method)]
public class AjaxOnlyAttribute : ActionFilterAttribute
{
     public override void OnActionExecuting(ActionExecutingContext filterContext)
     {
        if (!filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.HttpContext.Response.StatusCode = 404;
            filterContext.Result = new HttpNotFoundResult();
        }
        else
        {
            base.OnActionExecuting(filterContext);
        }
     }
}

you can use it like that:

[AjaxOnly]
public ActionResult Index() {
    // do something awesome
}
David Murdoch
  • 87,823
  • 39
  • 148
  • 191
tehshin
  • 866
  • 4
  • 7
4

A spinoff of Muhammad's answer letting you specify that it mustn't be an ajax request as well:

using System.Web.Mvc;
public class AjaxAttribute : ActionMethodSelectorAttribute
{
    public bool ajax { get; set; }
    public AjaxAttribute() { ajax = true; }
    public AjaxAttribute(bool a) { ajax = a; }
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return ajax == controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}

This lets you do things like...

[Ajax]
public PartialViewResult AjaxUpdatingPage() {
    return PartialView();
}

[Ajax(false)]
public ViewResult NotAjaxUpdatingPage() {
    return View();
}

Update for ASP.NET Core:

You will need to replace the usings and method signature/body with the following...

using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Routing;
using System.Linq;

public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
{
    // Using ASP.NET 6 strongly typed header:
    return ajax == routeContext.HttpContext.Request.Headers.XRequestedWith.Contains("XMLHttpRequest");
    // Older versions:
    return ajax == routeContext.HttpContext.Request.Headers.Any(h => h.Key == "X-Requested-With" && h.Value.Contains("XMLHttpRequest"));
}
Pluto
  • 2,900
  • 27
  • 38
3

For those looking for a .NET Core solution it's a little bit more involved, as IsAjaxRequest() is no longer available.

Below is the code I've used in production on several projects to great effect.

public class AjaxOnlyAttribute : ActionMethodSelectorAttribute
{
  public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor actionDescriptor)
  {
    if(routeContext.HttpContext.Request.Headers != null &&
      routeContext.HttpContext.Request.Headers.ContainsKey("X-Requested-With") &&
      routeContext.HttpContext.Request.Headers.TryGetValue("X-Requested-With", out StringValues requestedWithHeader))
    {
      if(requestedWithHeader.Contains("XMLHttpRequest"))
      {
        return true;
      }
    }

    return false;
  }
}
pim
  • 12,019
  • 6
  • 66
  • 69
3

There is an [AjaxOnly] attribute provided in the ASP.NET MVC 3 Futures collection. It's a part of the official ASP.NET MVC Codeplex site that provides features before they are officially included in a future version of ASP.NET MVC.

You can download it here. To use it, add a reference to the Microsoft.Web.Mvc assembly included in the release package.

There is an explanation of the attribute on this page, along with all the other great features you can use.

Xcalibur
  • 3,613
  • 2
  • 32
  • 26
2

my solution follows the [ChildActionOnly] implementation:

public class AjaxOnlyAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
            throw new ArgumentNullException("filterContext");
        if (!filterContext.HttpContext.Request.IsAjaxRequest())
            throw new InvalidOperationException(string.Format(
                CultureInfo.CurrentCulture, 
                "The action '{0}' is accessible only by an ajax request.", 
                filterContext.ActionDescriptor.ActionName
            ));
    }
}
sanjuro
  • 1,611
  • 1
  • 21
  • 29
  • +1 for providing an implementation that can be applied to an entire controller (instead of applying it to each and every action-method separately which can be quite a strain). – XDS Feb 23 '17 at 22:11