How can I end a response and send HTTP code 404 back?
Asked Answered
A

2

10

I am using a castle windsor factory to instantiate an object based on the request url.

Something like:

    public FooViewModel Get()
    {
        if (HttpContext.Current == null)
        {
            return new FooViewModel();
        }

        var currentContext = new HttpContextWrapper(HttpContext.Current);

        // resolve actual view model.

In some cases, I actually want to throw a 404 and stop the request, currently like:

        throw new HttpException(404, "HTTP/1.1 404 Not Found");
        currentContext.Response.End();

However the request doesn't end and it still hits the Action and tries to resolve the view?

My Controller would look something like this:

public class HomeController : Controller
{
    public FooViewModel Foo { get; set; }

    public ActionResult Index()
    {
        ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

        return View();
    }

Am I thinking about this all wrong? Or is there a way I can achieve this?

The alternative I was thinking of is an attribute on the action to check the state of the Foo property?

Avlona answered 11/9, 2012 at 23:44 Comment(0)
B
12

I think the approach of using an action filter could achieve what you are wanting:

public class RequiresModelAttribute : ActionFilterAttribute
{
    private readonly string _modelName;

    public RequiresModelAttribute(string modelName)
    {
        _modelName = modelName;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var property = filterContext.Controller.GetType().GetProperty(_modelName);
        var model = property.GetValue(filterContext.Controller);
        if (model == null)
        {
            filterContext.Result = new HttpStatusCodeResult(404);
        }
    }
}

Then, on your controller you could do:

public class HomeController : Controller
{
    public FooViewModel Foo { get; set; }

    [RequiresModel("Foo")]
    public ActionResult Index()
    {
        ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";

        return View();
    }
}

Edit: Perhaps using a global filter to honor any thrown HttpExceptions?

public class HonorHttpExceptionAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var httpException = filterContext.HttpContext.AllErrors.FirstOrDefault(x => x is HttpException) as HttpException;
        if (httpException != null)
        {
            filterContext.Result = new HttpStatusCodeResult(httpException.GetHttpCode());
        }
    }
}

Then in Global.asax:

  public static void RegisterGlobalFilters(GlobalFilterCollection filters)
  {
      filters.Add(new HandleErrorAttribute());
      filters.Add(new HonorHttpExceptionAttribute());
  }
Behn answered 12/9, 2012 at 0:31 Comment(2)
yup, I've changed it to this already, lets see if there are any other suggestions.Avlona
@Avlona I've updated my response with another approach, maybe that will work better in your scenario?Behn
F
2

Another option is overriding OnException on Controller.

 public ActionResult Index()
    {
        ViewBag.Message = "Welcome to ASP.NET MVC!";
        Get();
        return View();
    }

    public int Get()
    {
        throw new HttpException(404, "HTTP/1.1 404 Not Found");
        // we don't need end the response here, we need go to result step
        // currentContext.Response.End();

    }

    protected override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);
        if (filterContext.Exception is HttpException)
        {
            filterContext.Result = this.HttpNotFound(filterContext.Exception.Message);
        }
    }
Fokos answered 12/9, 2012 at 1:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.