views:

608

answers:

5

Recently I started working with MVC, before that I used "classic" ASP.NET.

After using Ruby on Rails (RoR), I wonder how to implement POST request handling in MVC similar to how RoR operates. In RoR you use the Post method, so you need only one function for a view.

In ASP.NET MVC I need to use 2 separate functions for GET and for POST, so I need to initialize the same data twice, and I don't like to repeat something in my code.

How can I check if the request is POST in one method?

Update:

Solution is found: I have to use Request.HttpMethod.

Thank you!

A: 

You may take a look at the Request.HttpMethod property.

Darin Dimitrov
+2  A: 

You don't check in ASP.NET MVC. You decorate your method with the [AcceptVerbs(HttpVerbs.Post)] attribute to indicate that the method applies to post only, and accept the model in the method used to handle the post.

I'd strongly suggest doing the walkthrough for NerdDinner to understand more about the ASP.NET MVC framework.

David Morton
I know that. That's what I was talking about. I don't want to have two separate methods for GET and POST, I want to have only 1.
Alex
asmaster, I use both RoR and ASP.NET MVC, and I can't help but feel that the method-decoration approach of the latter is far, far superior. You don't HAVE to use it, but you really should. The resulting code will be much cleaner, easier to read and maintain and less bug-prone.
Adam Crossland
A: 

Correct way to do is using ModelBinding during Post request.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(EmployeeViewModel model)
{
 //validate data, save employee, handle validation errors...
}

This way you will not have to init your data again.

Amitabh
+2  A: 

You only need separate methods for GET and POST if their method signatures differ, there's no reason why one action method can't handle GET and POST methods.

If you need to know whether it was a GET or POST, you could check using Request.HttpMethod in your action, but I would advise using a separate method decorated with the [AcceptVerbs(HttpVerbs.Post)] attribute as suggested by the other posters.

JonoW
Exactly what I was gonna say. The two methods do different things as well. One returns the GET view, the other does processing and then returns an appropriate view.
Daniel T.
Thanks! That's exactly what I was looking for.
Alex
+1  A: 

I came across this question wanting to know the same thing. Below is a detailed description of my situation and the solution that I used (which utilizes the other answers provided here). I originally tried to use the two separate method approach, but I ran into a problem when the method signatures of these methods became identical.

I have a page that displays report data. At the top of the page there is a form with some fields, which allow the user to specify report parameters such as start date, end date, etc.

I originally approached this by creating two separate methods to handle the Get and the Post methods. The post method would redirect the browser to the get method so that any parameters that were specified would be added to the query string and so that the browser would not prompt the user with a dialog saying that it is going to resend the data that they entered if they refresh. Note: I realized later that I could accomplish this by setting the method attribute of my form element to "Get", but I think ideally a controller shouldn't have knowledge of how a view is implemented, so in my opinion that is irrelevant.

As I developed these two methods I eventually found myself in a situation where the method signatures became identical. Furthermore, my code for these two methods became nearly identical, so I decided to merge them into a single method and to just check the request verb so that I could do something slightly different when the request is not a "Get". A distilled example of my two methods is shown below:

    // this will not compile because the method signatures are the same

    public ActionResult MyReport(DateRangeReportItem report)
    {
        // if there are no validation errors and the required report parameters are completed
        if (ModelState.IsValid && report.ParametersAreComplete)
        {
            // retrieve report data and populate it on the report model
            report.Result = GetReportData(report.CreateReportParameters());
        }

        return View(report);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult MyReport(DateRangeReportItem report)
    {
        if (ModelState.IsValid && report.ParametersAreComplete)
        {
            // redirect to the same action so that if the user refreshes the browser it will submit a get request instead of a post request
            // this avoids the browser prompting the user with a dialog saying that their data will be resubmitted
            return RedirectToAction("MyReport", new { StartDate = report.StartDate, EndDate = report.EndDate });
        }
        else
        {
            // there were validation errors, or the report parameters are not yet complete
            return View(report);
        }
    }

Why am I accepting a model object as the parameter to my get method? The reason is that I wanted to take advantage of the validation logic already built into the model object. If someone navigates to my page directly with all parameters already specified in the query string, then I want to go ahead and retrieve the report data and display it on the page. However, if the parameters specified in the query string are invalid then I also want validation errors to appear on the page. By putting my model object as the parameter, the MVC framework will automatically attempt to populate it and will capture any validation errors without any additional work on my part.

I used the other answers posted for this question to create a RequestHttpVerb property on a base controller class in my project:

    public HttpVerbs RequestHttpVerb
    {
        get { return (HttpVerbs)Enum.Parse(typeof(HttpVerbs), this.Request.HttpMethod, true); }
    }

So finally my consolidated method looks like the following:

    [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
    public ActionResult MyReport(DateRangeReportItem report)
    {
        // check if there are any validation errors in the model
        // and whether all required report parameters have been completed
        if (ModelState.IsValid && report.ParametersAreComplete)
        {
            // this is unnecessary if the form method is set to "Get"
            // but within the controller I do not know for sure if that will be the case in the view
            if (HttpVerbs.Get != this.RequestHttpVerb)
            {
                // redirect to the same action so that if the user refreshes the browser it will submit a get request instead of a post request
                // this avoids the browser prompting the user with a dialog saying that their data will be resubmitted
                return RedirectToAction("MyReport", new { StartDate = report.StartDate, EndDate = report.EndDate });
            }

            // there were no validation errors and all required report parameters are complete
            // retrieve report data and populate that data on the model
            report.Result = GetReportData(report.CreateReportParameters());
        }

        // display the view with the report object
        // Any model state errors that occurred while populating the model will result in validation errors being displayed
        return View(report);
    }

That's my current solution to the problem. I would prefer not to have to check the Request.HttpMethod property in order to determine whether I needed to perform the redirect, but I didn't see another solution to my problem. I would have been fine with keeping two separate methods to handle Get and Post requests, but the identical method signature prevented this. I would have preferred to rename my Post action handler method to avoid the method signature conflict and to use some mechanism to indicate to the MVC framework that my renamed method should still handle the "MyReport" action, but I am not aware of any such mechanism in the MVC framework.

Dr. Wily's Apprentice