Call void method in Controller from another controller

M J 661 Reputation points
2022-09-09T21:36:03.783+00:00

I have a report that the initial view asks for the date range of the report. When the user submits that page, the view then shows the report and at the top of the page I have a link that points to a void method in the Controller that builds and then downloads an Excel Spreadsheet. This works great. Now the hitch, the customer does not want the middle step of seeing the report on the screen, they instead want to submit the dates and have the spreadsheet download from there.

I did not want to skip the ActionResult method in the Controller because that validates that the user actually entered dates (I do have jquery validation on the view but like to also validate on the server because some bozos find ways to get around client side solutions) . However, how do I call the void method from the ActionResult method? is it possible or do I just point the form to the void method in the Controller?

ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,502 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Michael Taylor 54,731 Reputation points
    2022-09-09T21:54:26.187+00:00

    If you need to share code across controllers then promote that code up your layers to the next level. In most apps that would be the service layer. Then modify both controllers to call the same (now shared) code. This is probably the easiest and cleanest refactoring. Any data it needs should be passed into the method. Any data coming back can then be morphed by the controller to produce the final result.

    Alternatively if the code is tightly integrated with your controllers then create a base class for your controllers and move the logic into there. Have each controller derive from the new base type and use the shared code. This is only a good idea if the actual code requires a lot of integration into the ASP.NET runtime. In general this would be a first refactor step to making the code less reliant on ASP.NET.

    Yet another alternative is to have one controller method simply call the other controller's action. They are public methods after all. However the downside to this is that any validation you are doing in the called action has probably already been done so you're wasting some cycles. But it might be OK.

    Finally, ActionResult is just an implementation of getting a result. You can create your own derived type and have it return something different (after optionally calling some code). But this should only really be done in cases where the result is actually different from other results (like returning a custom file format). It is overkill for most other cases.

    0 comments No comments

  2. AgaveJoe 28,536 Reputation points
    2022-09-10T12:45:02.553+00:00

    While you've defined a void method, you also programmatically return an HTTP response. Technically the method is not void in the context of an MVC application.

    Every controller has a File() method which is the standard method for returning a file from an action. I recommend refactoring your code to return a file so the code base is more straight forward.

    The key think concept you must consider is HTTP applications have one request and one response. To accomplish the use case, the action should return a file stream or HTML (View). While I'm not a fan of returning two different types from a single action there is nothing stopping you from doing so. A simple "if" will do the trick.

        public class ReportController : Controller  
        {  
      
            // GET: Report  
            [HttpGet]  
            public ActionResult Index()  
            {  
                return View();  
            }  
      
            [HttpPost]  
            public ActionResult Index(ReportViewModel model)  
            {  
                if(!ModelState.IsValid)  
                {  
                    return View(model);  
                }  
                byte[] data = GetFileContents();  
                return File(data, "application/octet-stream", "myreport.xlsx");  
            }  
      
            private byte[] GetFileContents()  
            {  
                //Mock- create file and return the file contents  
                return System.IO.File.ReadAllBytes(Server.MapPath("~/reports/report.xlsx"));  
            }  
        }  
    

    The View

    @using (Html.BeginForm())   
    {  
        @Html.AntiForgeryToken()  
          
        <div class="form-horizontal">  
            <h4>ReportViewModel</h4>  
            <hr />  
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })  
            <div class="form-group">  
                @Html.LabelFor(model => model.StartDate, htmlAttributes: new { @class = "control-label col-md-2" })  
                <div class="col-md-10">  
                    @Html.EditorFor(model => model.StartDate, new { htmlAttributes = new { @class = "form-control" } })  
                    @Html.ValidationMessageFor(model => model.StartDate, "", new { @class = "text-danger" })  
                </div>  
            </div>  
      
            <div class="form-group">  
                @Html.LabelFor(model => model.EndDate, htmlAttributes: new { @class = "control-label col-md-2" })  
                <div class="col-md-10">  
                    @Html.EditorFor(model => model.EndDate, new { htmlAttributes = new { @class = "form-control" } })  
                    @Html.ValidationMessageFor(model => model.EndDate, "", new { @class = "text-danger" })  
                </div>  
            </div>  
      
            <div class="form-group">  
                <div class="col-md-offset-2 col-md-10">  
                    <input type="submit" value="Create" class="btn btn-default" />  
                </div>  
            </div>  
        </div>  
    }  
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.