ASP.NET MVC · Controllers & Views

Action Methods

An action decides what the browser receives: a page, some JSON, a file download, a redirect or an error. Choosing the right ActionResult makes your app behave correctly for users and for search engines.

Beginner 6 min Lesson 7 of 37Covers ASP.NET MVC 5.2 on .NET Framework 4.8.
Before you start:Controllers
0/37 in ASP.NET MVC
On this page · 6 min · Beginner

Learning objectives#

  • Return the right ActionResult for each situation
  • Restrict actions to GET or POST
  • Return JSON safely from MVC 5
  • Send files and status codes

Explanation#

Actions usually return ActionResult (or a more specific type). The helper methods on Controller build the results: View(), PartialView(), Content(), Json(), File(), Redirect(), RedirectToAction(), HttpNotFound() and new HttpStatusCodeResult(code).

[HttpGet] and [HttpPost] restrict which HTTP method an action answers. Two actions can share a name — Create() for GET shows the form and Create(model) for POST saves it.

Json(data) uses JavaScriptSerializer. For GET requests you must pass JsonRequestBehavior.AllowGet; MVC 5 blocks JSON over GET by default because of an old JSON-hijacking attack on arrays.

File(bytes, contentType, fileDownloadName) sends a download with a Content-Disposition header. RedirectToAction sends a 302 to another action’s URL, built from the routes.

[ActionName("delete")] exposes a method under a different name, which helps when GET and POST versions need the same URL but C# needs different signatures.

Syntax#

C# Illustrative
return View(model);                                   // HTML page
return Content("text", "text/plain");                 // raw text
return Json(data, JsonRequestBehavior.AllowGet);      // JSON for GET
return File(bytes, "text/csv", "orders.csv");          // download
return RedirectToAction("Index");                     // 302
return HttpNotFound();                                // 404
return new HttpStatusCodeResult(400, "Bad id");       // any status

Code example: One controller, many kinds of results#

C#CatalogController.cs Illustrative
// Controllers/CatalogController.cs
using System.Net;
using System.Text;
using System.Web.Mvc;


namespace ShopSite.Controllers
{
    public class CatalogController : Controller
    {
        // GET /Catalog/Price/1   → plain text
        public ActionResult Price(int id)
        {
            if (id <= 0) return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "id must be positive");
            if (id > 3) return HttpNotFound();
            return Content(
quot;Product
{id} costs {id * 20} rupees", "text/plain");
} // GET /Catalog/Info/2 → JSON public ActionResult Info(int id) { var product = new { Id = id, Name = "Notebook", Price = 60.5m, Tags = new[] { "paper", "a4" } }; return Json(product, JsonRequestBehavior.AllowGet); } // GET /Catalog/Export → file download public ActionResult Export() { var csv = "Id,Name,Price\n1,Gel Pen,20\n2,Notebook,60.5\n"; return File(Encoding.UTF8.GetBytes(csv), "text/csv", "products.csv"); } // GET /Catalog/Old → permanent redirect public ActionResult Old() { return RedirectToActionPermanent("Price", new { id = 1 }); } // POST /Catalog/Remove/2 only [HttpPost] [ActionName("Remove")] public ActionResult RemoveConfirmed(int id) { return RedirectToAction("Price", new { id }); } } }
Expected output
GET /Catalog/Price/2
200 OK
Product 2 costs 40 rupees

GET /Catalog/Price/9
404 Not Found

GET /Catalog/Price/0
400 id must be positive

GET /Catalog/Info/2
200 OK
Content-Type: application/json; charset=utf-8
{"Id":2,"Name":"Notebook","Price":60.5,"Tags":["paper","a4"]}

GET /Catalog/Export
200 OK
Content-Disposition: attachment; filename=products.csv
Id,Name,Price
1,Gel Pen,20
2,Notebook,60.5

GET /Catalog/Old
301 Moved Permanently → /Catalog/Price/1

GET /Catalog/Remove/2
404 Not Found

POST /Catalog/Remove/2
302 Found → /Catalog/Price/2

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. Price validates the id: 0 gives 400 with a reason, 9 gives 404, and 2 gives plain text.
  2. Info returns Json(...), which serialises the anonymous object with JavaScriptSerializer — decimals stay as numbers and property names keep their PascalCase.
  3. Export sends bytes with Content-Disposition: attachment, so the browser downloads products.csv.
  4. Old answers 301 Moved Permanently, which tells browsers and search engines to update their links.
  5. RemoveConfirmed only accepts POST and is exposed as Remove, so a GET to /Catalog/Remove/2 returns 404 while the POST redirects.

Real-world use case#

An order-management app adds an “Export to Excel (CSV)” button: one action builds the CSV with a StringBuilder and returns File(…), and the browser downloads it with the right file name.

Common mistakes#

Json over GET without AllowGet

return Json(data) in a GET action throws “This request has been blocked because sensitive information could be disclosed…”. Pass JsonRequestBehavior.AllowGet when the data is safe to return to GET requests.

Returning View after a successful POST

Refreshing the page resubmits the form. Redirect with RedirectToAction (the Post/Redirect/Get pattern).

Returning 200 for errors

Content("Not found") returns 200 OK, so caches and search engines treat it as a real page. Use HttpNotFound() or HttpStatusCodeResult.

Best practices#

  • Pick the most specific result: NotFound, BadRequest, Redirect
  • Use [HttpPost] for anything that changes data
  • Use RedirectToActionPermanent for moved pages (SEO)
  • Return view models or DTOs in JSON, not EF entities

Remember this#

ActionResult = what the browser gets.

Json + GET needs JsonRequestBehavior.AllowGet.

Changes data? [HttpPost] and redirect afterwards.

Practice exercise#

Your task: Write a Download action that returns a text file named receipt-{id}.txt containing “Receipt for order {id}”, and returns 404 for ids above 1000.

Need a hint?
Use File(Encoding.UTF8.GetBytes(text), "text/plain",
quot;receipt-{id}.txt").
Show solution
C# Illustrative
// Controllers/ReceiptsController.cs
using System.Text;
using System.Web.Mvc;


namespace ShopSite.Controllers
{
    public class ReceiptsController : Controller
    {
        public ActionResult Download(int id)
        {
            if (id > 1000) return HttpNotFound();
            var text = 
quot;Receipt for order
{id}";
return File(Encoding.UTF8.GetBytes(text), "text/plain",
quot;receipt-
{id}.txt");
} } }
Expected output
GET /Receipts/Download/42
200 OK
Content-Disposition: attachment; filename=receipt-42.txt
Receipt for order 42

GET /Receipts/Download/5000
404 Not Found

Mini quiz#

Action Methods quizMultiple choice Question 1 of 3

Which helper returns a 404 in MVC 5?

Score so far: 0/0

Want more? Practise all ASP.NET MVC MCQs · ASP.NET MVC interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.