Controller Responses
Controllers communicate with users by returning different types of responses. This section provides a complete reference for all response types available in Magentrix, from rendering pages to generating files, streaming JSON data, and redirecting users.
Table of Contents
- Response Types Summary
- Response Types Overview
- View Responses
- PartialView Response
- Predefined Views
- Redirect Responses
- RedirectToAction
- Redirecting to Active Pages
- File Responses
- StorageFile Response
- PDF Response
- Data Responses
- Special Responses
- Response Type Decision Guide
- Practical Examples
- Best Practices
- Common Pitfalls
- Summary
- Next Steps
- Quick Reference
Response Types Summary
Response Types Overview
Every controller action must return an ActionResponse object. The type of response determines what the user sees or receives.
Response Type Comparison
| Response Type | Purpose | Common Use Cases |
|---|
| View() | Render Active Page | Display forms, dashboards, detail pages |
| PartialView() | Render page fragment | Widgets, reusable components |
| Redirect() | Navigate to URL | Post-form submission, external links |
| RedirectToAction() | Navigate to action | Navigate between controller actions |
| Json() | Return JSON data | REST APIs, AJAX responses |
| File() | Stream file download | CSV exports, binary files |
| StorageFile() | Stream from cloud | Download files from Magentrix Storage |
| Pdf() | Generate PDF | Dynamic reports, invoices |
| Content() | Return text | Plain text, HTML, XML, CSS, JavaScript |
| UnauthorizedAccessResponse() | Access denied | Security violations |
| PageNotFound() | 404 error | Missing resources |
| RedirectToError() | Error page | Exception handling |
View Responses
View responses render Active Pages to display HTML user interfaces. They are the most common response type for Page Controllers.
Basic View Response
public override ActionResponse Index()
{
return View();
}
This renders the Active Page associated with the controller.
View with Model
Pass data to the Active Page using a model:
public ActionResponse Details(string id)
{
Contact contact = Database.Retrieve(id);
return View(contact);
}
Accessing in Active Page:
<aspx:AspxPage runat='server' title='Contact Details'>
<body>
<h1>{!Model.FirstName} {!Model.LastName}</h1>
<p>Email: {!Model.Email}</p>
</body>
</aspx:AspxPage>
View with Collection Model
public override ActionResponse Index()
{
List<Account> accounts = Database.Query<Account>()
.OrderBy(a => a.Name)
.ToList();
return View(accounts);
}
Displaying in Active Page:
<aspx:Repeater runat='server' value='{!Model}' var='account'>
<body>
<table class='table'>
<thead>
<tr>
<th>Account Name</th>
<th>Type</th>
<th>Industry</th>
</tr>
</thead>
<tbody>
<tr>
<td><aspx:Field runat='server' value='{!account.Name}'/></td>
<td><aspx:Field runat='server' value='{!account.Type}'/></td>
<td><aspx:Field runat='server' value='{!account.Industry}'/></td>
</tr>
</tbody>
</table>
</body>
</aspx:Repeater>
View with Specific Active Page
Render a different Active Page than the default:
public ActionResponse Confirm()
{
return View(ActivePages.ConfirmationPage);
}
View with Active Page and Model
public ActionResponse ShowResults()
{
var results = Database.Query<Opportunity>()
.Where(o => o.Stage == "Closed Won")
.ToList();
return View(ActivePages.ResultsPage, results);
}
Available View() Overloads
// Uses default Active Page for the controller
View()
// Uses default Active Page with model
View(object model)
// Uses specific Active Page
View(ActivePage activePage)
// Uses specific Active Page with model
View(ActivePage activePage, object model)
PartialView Response
PartialView renders page fragments that can be embedded within other Active Pages or used as widgets.
Basic PartialView
public ActionResponse MyWidget()
{
return PartialView();
}
PartialView with Model
public ActionResponse OpportunitySummary(string accountId)
{
var opportunities = Database.Query<Opportunity>()
.Where(o => o.AccountId == accountId)
.ToList();
return PartialView(opportunities);
}
Using PartialView in Active Pages
In the parent Active Page:
<aspx:AspxPage Id='ParentPage' runat='server' title='Account Dashboard'>
<body>
<h1>Account Dashboard</h1>
<!-- Embed the partial view as a widget -->
<aspx:PartialView area='aspx' action='opportunitySummary'
controller='dashboard'
accountId='{!Model.Id}' />
</body>
</aspx:AspxPage>
Practical PartialView Examples
Example 1: Reusable Contact Card Widget
public class WidgetsController : AspxController
{
public ActionResponse ContactCard(string contactId)
{
Contact contact = Database.Retrieve(contactId);
return PartialView(contact);
}
}
Example 2: Real-Time Statistics Widget
public ActionResponse LiveStats()
{
var stats = new
{
ActiveUsers = Database.Query<User>().Where(u => u.IsActive).Count(),
OpenOpportunities = Database.Query<Opportunity>().Where(o => !o.IsClosed).Count(),
TodayRevenue = CalculateTodayRevenue()
};
return PartialView(stats);
}
Predefined Views
Magentrix provides several predefined views for common scenarios.
PageNotFound
Display a user-friendly 404 error:
public ActionResponse ViewResource(string id)
{
var resource = Database.Query<Resource>()
.Where(r => r.Id == id)
.FirstOrDefault();
if (resource == null)
{
return PageNotFound();
}
return View(resource);
}
RecordDeleted
Show message when a record has been deleted or is no longer accessible:
public ActionResponse Details(string id)
{
Contact contact = Database.Query<Contact>()
.Where(c => c.Id == id)
.FirstOrDefault();
if (contact == null)
{
return RecordDeleted();
}
return View(contact);
}
UnderMaintenance
Display maintenance page:
public ActionResponse Index()
{
bool isMaintenanceMode = CheckMaintenanceMode();
if (isMaintenanceMode)
{
return UnderMaintenance();
}
return View();
}
Error View with Custom Message
public ActionResponse ProcessData()
{
try
{
// Processing logic
return View();
}
catch (Exception ex)
{
return RedirectToError("An error occurred while processing your request. Please try again.");
}
}
Redirect Responses
Redirect responses navigate users to different URLs or actions.
Basic Redirect
Redirect to any URL:
public ActionResponse GoHome()
{
return Redirect("~/home/index");
}
The ~ symbol resolves to the application root.
Redirect to External URL
public ActionResponse GoToExternal()
{
return Redirect("https://www.example.com");
}
Permanent Redirect (301)
Use for permanently moved resources:
public ActionResponse OldPage()
{
return RedirectPermanent("/home/newpage");
}
This sends HTTP status code 301, which tells search engines the resource has permanently moved.
RedirectToAction
Redirect to another action in the same or different controller.
Redirect to Action in Same Controller
[HttpPost]
public ActionResponse Create(Contact model)
{
if (ModelState.IsValid)
{
Database.Insert(model);
return RedirectToAction("Index");
}
return View(model);
}
Redirect to Action with Parameters
[HttpPost]
public ActionResponse Create(Contact model)
{
if (ModelState.IsValid)
{
Database.Insert(model);
// Redirect to Details action with the new record's ID
return RedirectToAction("Details", new { id = model.Id });
}
return View(model);
}
Redirect to Action in Different Controller
public ActionResponse ProcessComplete()
{
return RedirectToAction("Edit", "Contact", new { id = "003R000000haXk3IAE" });
}
Parameters:
"Edit" - Action name"Contact" - Controller namenew { id = "..." } - Route values (parameters)
Permanent Redirect to Action
public ActionResponse OldAction()
{
return RedirectToActionPermanent("NewAction", "NewController");
}
Available RedirectToAction() Overloads
// Redirect to action in same controller
RedirectToAction(string actionName)
// Redirect to action with parameters
RedirectToAction(string actionName, object routeValues)
// Redirect to action in different controller
RedirectToAction(string actionName, string controllerName, object routeValues)
// Permanent redirect versions
RedirectToActionPermanent(string actionName, object routeValues)
RedirectToActionPermanent(string actionName, string controllerName, object routeValues)
Redirecting to Active Pages
Use the ActivePages helper to redirect to specific Active Pages with type safety:
public ActionResponse GoToContactPage()
{
return Redirect(ActivePages.ContactManager);
}
Redirect to Active Page with Parameters
public ActionResponse ViewContact(string id)
{
return Redirect(ActivePages.ContactDetails, new { id = id });
}
This approach is recommended because:
- Maintains system dependencies
- Provides compile-time checking
- System notifies you if Active Page is referenced elsewhere
File Responses
File responses stream binary files to the user's browser for download.
Basic File Response
public ActionResponse DownloadFile()
{
byte[] fileBytes = GetFileContent();
return File(fileBytes, "application/pdf", "document.pdf");
}
Parameters:
fileBytes - The file content as byte array"application/pdf" - MIME content type"document.pdf" - Suggested filename for download
Common MIME Types
| File Type | MIME Type |
|---|
| PDF | application/pdf |
| Word (.docx) | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
| Excel (.xlsx) | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| CSV | text/csv |
| Plain Text | text/plain |
| JSON | application/json |
| XML | application/xml |
| ZIP | application/zip |
| PNG | image/png |
| JPEG | image/jpeg |
CSV Export Example
public ActionResponse ExportContactsCsv()
{
var contacts = Database.Query<Contact>().ToList();
var csv = new StringBuilder();
csv.AppendLine("First Name,Last Name,Email,Phone");
foreach (var contact in contacts)
{
csv.AppendLine($"{contact.FirstName},{contact.LastName},{contact.Email},{contact.Phone}");
}
byte[] fileBytes = Encoding.UTF8.GetBytes(csv.ToString());
return File(fileBytes, "text/csv", "contacts.csv");
}
Excel Export Example
public ActionResponse ExportAccountsExcel()
{
var accounts = Database.Query<Account>().ToList();
// Use external libraries generate Excel
byte[] excelBytes = GenerateExcelFile(accounts);
return File(excelBytes,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"accounts.xlsx");
}
Reading Note and Attachment Files
public ActionResponse OpenAttachment(string id)
{
var attachment = Database.Retrieve(id);
// Read file from Magentrix Storage
byte[] fileBytes = Storage.ReadFileBytes(id);
return File(fileBytes, attachment.ContentType, attachment.Name);
}
File Response with Stream
public ActionResponse DownloadLargeFile()
{
var fileStream = GetLargeFileStream();
return File(fileStream, "application/octet-stream", "largefile.dat");
}
Open File Inline (in Browser)
By default, files are downloaded. To open them in the browser:
public ActionResponse ViewPdf(string id)
{
byte[] pdfBytes = GetPdfContent(id);
// Fourth parameter: true = open inline, false = download
return File(pdfBytes, "application/pdf", "document.pdf", true);
}
Available File() Overloads
// Basic file download
File(byte[] fileContents, string contentType)
// File download with filename
File(byte[] fileContents, string contentType, string fileName)
// File with inline/download control
File(byte[] fileContents, string contentType, string fileName, bool openInline)
// Stream-based file download
File(Stream fileStream, string contentType)
// Stream with filename
File(Stream fileStream, string contentType, string fileName)
StorageFile Response
Stream files directly from Magentrix Cloud Storage without loading them into memory.
Basic StorageFile
public ActionResponse DownloadStorageFile(string blobId)
{
return StorageFile(blobId, "application/pdf");
}
StorageFile with Filename
public ActionResponse OpenAttachment(string id)
{
var attachment = Database.Retrieve<NoteAndAttachment>(id);
return StorageFile(id, attachment.ContentType, attachment.Name);
}
StorageFile Opened Inline
public ActionResponse ViewDocument(string id)
{
var document = Database.Retrieve<Document>(id);
// Fourth parameter: true = open inline in browser
return StorageFile(id, document.ContentType, document.Name, true);
}
Available StorageFile() Overloads
// Basic storage file stream
StorageFile(string blobId, string contentType)
// Storage file with filename
StorageFile(string blobId, string contentType, string fileName)
// Storage file with inline/download control
StorageFile(string blobId, string contentType, string fileName, bool inline)
💡
When to use StorageFile vs File:- StorageFile: When file is already stored in Magentrix Cloud Storage
- File: When generating files dynamically or reading from external sources
PDF Response
Generate dynamic PDF files from HTML content or Active Page models.
PDF from Current Active Page
public ActionResponse ExportToPdf()
{
var accounts = Database.Query<Account>().Limit(5).ToList();
// Renders current Active Page as PDF
return Pdf(accounts, "accounts_list.pdf");
}
💡 Note: The Active Page associated with this controller is converted to PDF.
PDF from String Content
public ActionResponse GenerateSimplePdf()
{
string htmlContent = @"
<html>
<head>
<style>
body { font-family: Arial; }
h1 { color: navy; }
</style>
</head>
<body>
<h1>Sales Report</h1>
<p>Total Revenue: $1,234,567</p>
</body>
</html>
";
return Pdf(htmlContent, "sales_report.pdf");
}
PDF from Model with Custom HTML
public ActionResponse InvoicePdf(string opportunityId)
{
var opp = Database.Retrieve<Opportunity>(opportunityId);
var account = Database.Retrieve<Account>(opp.AccountId);
var invoiceHtml = $@"
<html>
<head>
<style>
body {{ font-family: Arial; padding: 20px; }}
.header {{ border-bottom: 2px solid navy; padding-bottom: 10px; }}
.total {{ font-size: 20px; font-weight: bold; }}
</style>
</head>
<body>
<div class='header'>
<h1>Invoice</h1>
<p>Date: {DateTime.Now:MM/dd/yyyy}</p>
</div>
<h2>Bill To:</h2>
<p>{account.Name}</p>
<p>{account.BillingStreet}</p>
<h2>Invoice Details:</h2>
<p>Opportunity: {opp.Name}</p>
<p class='total'>Total: ${opp.Amount:N2}</p>
</body>
</html>
";
return Pdf(invoiceHtml, $"Invoice_{opp.Name}.pdf");
}
PDF with Options
Control PDF generation with PdfOptions:
public ActionResponse CustomPdf()
{
var accounts = Database.Query<Account>().ToList();
var options = new PdfOptions
{
PageSize = PdfPageSize.Letter,
Orientation = PdfOrientation.Landscape,
MarginTop = 20,
MarginBottom = 20,
MarginLeft = 15,
MarginRight = 15,
CompressionLevel = PdfCompressionLevel.High
};
return Pdf(accounts, "accounts.pdf", options);
}
PDF Opened Inline
public ActionResponse ViewPdfInline()
{
var data = GetReportData();
// Fourth parameter: true = open in browser, false = download
return Pdf(data, "report.pdf", null, true);
}
Available Pdf() Overloads
// PDF from model using current Active Page
Pdf(object model, string filename)
// PDF from model with options
Pdf(object model, string filename, PdfOptions exportOptions)
// PDF from model with inline control
Pdf(object model, string filename, PdfOptions exportOptions, bool inline)
// PDF from HTML string
Pdf(string content, string filename)
// PDF from HTML with options
Pdf(string content, string filename, PdfOptions exportOptions)
// PDF from HTML with inline control
Pdf(string content, string filename, PdfOptions exportOptions, bool inline)
Important PDF Notes
⚠ Use Absolute Paths for Resources: When generating PDFs, CSS and images must use absolute URLs:
<!-- ❌ Relative paths don't work in PDFs -->
<img src="/images/logo.png" />
<link rel="stylesheet" href="/css/styles.css" />
<!-- ✅ Use absolute paths -->
<img src="https://yourportal.magentrix.com/images/logo.png" />
<link rel="stylesheet" href="https://yourportal.magentrix.com/css/styles.css" />
Data Responses
Json Response
Return JSON data for APIs and AJAX calls.
Basic JSON Response:
[HandleExceptionsForJson]
public ActionResponse GetContacts()
{
var contacts = Database.Query<Contact>().ToList();
return Json(contacts, JsonRequestBehavior.AllowGet);
}
⚠ Always use [HandleExceptionsForJson] attribute to ensure errors are returned as JSON.
JSON for POST Requests:
[HttpPost]
[HandleExceptionsForJson]
public ActionResponse CreateContact(Contact model)
{
if (ModelState.IsValid)
{
Database.Insert(model);
return Json(new { success = true, id = model.Id });
}
return Json(new { success = false, errors = ModelState });
}
JSON with Custom Status Code:
[HandleExceptionsForJson]
public ActionResponse GetContact(string id)
{
Contact contact = Database.Retrieve<Contact>(id);
if (contact == null)
{
return Json(new { error = "Contact not found" }, 404);
}
return Json(contact, JsonRequestBehavior.AllowGet);
}
Available Json() Overloads:
// POST only (default behavior)
Json(object model)
// POST with custom status code
Json(object model, int statusCode)
// GET or POST based on behavior parameter
Json(object model, JsonRequestBehavior behavior)
// GET or POST with custom status code
Json(object model, int statusCode, JsonRequestBehavior behavior)
// POST with custom content type
Json(object model, string contentType)
// POST with content type and encoding
Json(object model, string contentType, Encoding contentEncoding)
// Simplified overload for GET requests
Json(object model, bool allowGet)
💡 Use JsonRequestBehavior.AllowGet to allow GET requests to return JSON.
Content Response
Return plain text, HTML, XML, CSS, JavaScript, or any text-based content.
Plain Text:
public ActionResponse GetText()
{
return Content("Hello, World!");
}
HTML Content:
public ActionResponse GetHtml()
{
string html = "<h1>Welcome</h1><p>This is dynamic HTML.</p>";
return Content(html, "text/html");
}
XML Content:
public ActionResponse GetXml()
{
string xml = @"
<contact>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>john.doe@example.com</email>
</contact>
";
return Content(xml, "application/xml");
}
JSON as Content (Alternative to Json()):
public ActionResponse GetJsonAsText()
{
var data = new { name = "John", age = 30 };
var json = JsonConvert.SerializeObject(data);
return Content(json, "application/json");
}
CSS Content:
public ActionResponse GetCustomCss()
{
var css = @"
body { background-color: #f0f0f0; }
h1 { color: navy; }
";
return Content(css, "text/css");
}
JavaScript Content:
public ActionResponse GetScript()
{
var js = "alert('Hello from controller!');";
return Content(js, "application/javascript");
}
Available Content() Overloads:
// Plain text with default content type
Content(string content)
// Content with specific MIME type
Content(string content, string contentType)
// Content with MIME type and encoding
Content(string content, string contentType, Encoding contentEncoding)
💡 Note: If your action returns a primitive type (string, int, etc.), Magentrix automatically converts it to a Content response:
public string MyAction()
{
return "<Contact><FirstName>Sam</FirstName></Contact>";
}
This is equivalent to:
public ActionResponse MyAction()
{
return Content("<Contact><FirstName>Sam</FirstName></Contact>");
}
Special Responses
UnauthorizedAccessResponse
Return when a user doesn't have permission to access a resource:
public ActionResponse AdminDashboard()
{
if (SystemInfo.IsGuestUser)
return UnauthorizedAccessResponse();
if (UserInfo.IsPortalUser)
return UnauthorizedAccessResponse();
// Employee-only logic
return View();
}
PageNotFound
Return a user-friendly 404 error:
public ActionResponse ViewResource(string id)
{
var resource = Database.Query<Resource>()
.Where(r => r.Id == id)
.FirstOrDefault();
if (resource == null)
return PageNotFound();
return View(resource);
}
RedirectToError
Redirect to an error page with a custom message:
public ActionResponse ProcessPayment()
{
try
{
// Payment processing logic
return View();
}
catch (PaymentException ex)
{
return RedirectToError("Payment processing failed. Please try again or contact support.");
}
}
Response Type Decision Guide
Use this guide to choose the right response type:
┌─────────────────────────────────────┐
│ What are you trying to do? │
└──────────────┬──────────────────────┘
│
┌──────┴──────┐
│ │
┌─────▼──────┐ ┌──▼───────────────┐
│ Show HTML │ │ Return Data │
│ Page? │ │ or Files? │
└─────┬──────┘ └───┬──────────────┘
│ │
│ │
┌───────▼─────────────▼────────────────┐
│ │
│ HTML Page Options: │
│ • View() - Full page │
│ • PartialView() - Widget/fragment │
│ • Predefined views - Error pages │
│ │
│ Data/File Options: │
│ • Json() - API responses │
│ • File() - Download files │
│ • StorageFile() - Cloud files │
│ • Pdf() - Generate PDFs │
│ • Content() - Text/HTML/XML │
│ │
│ Navigation Options: │
│ • Redirect() - Go to URL │
│ • RedirectToAction() - Go to action │
│ │
│ Error Options: │
│ • UnauthorizedAccessResponse() │
│ • PageNotFound() │
│ • RedirectToError() │
└──────────────────────────────────────┘
Practical Examples
Example 1: Complete CRUD Controller with Mixed Responses
public class ContactManagerController : AspxController
{
// GET: List all contacts (View response)
public override ActionResponse Index()
{
var contacts = Database.Query<Contact>()
.OrderBy(c => c.LastName)
.ToList();
return View(contacts);
}
// GET: Show create form (View response)
public ActionResponse New()
{
return View(new Contact());
}
// POST: Create contact (Redirect response)
[HttpPost]
public ActionResponse New(Contact model)
{
if (ModelState.IsValid)
{
Database.Insert(model);
AspxPage.AddMessage("Contact created successfully!");
return RedirectToAction("Details", new { id = model.Id });
}
return View(model);
}
// GET: Show contact details (View response)
public ActionResponse Details(string id)
{
var contact = Database.Retrieve(id);
if (contact == null)
return PageNotFound();
return View(contact);
}
// GET: Show edit form (View response)
public ActionResponse Edit(string id)
{
Contact contact = Database.Retrieve(id);
if (contact == null)
return PageNotFound();
return View(contact);
}
// POST: Update contact (Redirect response)
[HttpPost]
public ActionResponse Edit(Contact model)
{
if (ModelState.IsValid)
{
Database.Update(model);
AspxPage.AddMessage("Contact updated successfully!");
return RedirectToAction("Details", new { id = model.Id });
}
return View(model);
}
// POST: Delete contact (Redirect response)
[HttpPost]
public ActionResponse Delete(string id)
{
try
{
Database.Delete(id);
AspxPage.AddMessage("Contact deleted successfully.");
return RedirectToAction("Index");
}
catch (Exception ex)
{
return RedirectToError("An error occurred while deleting the contact.");
}
}
// GET: Export to CSV (File response)
public ActionResponse ExportCsv()
{
var contacts = Database.Query<Contact>().ToList();
var csv = new StringBuilder();
csv.AppendLine("First Name,Last Name,Email,Phone");
foreach (var contact in contacts)
csv.AppendLine($"{contact.FirstName},{contact.LastName},{contact.Email},{contact.Phone}");
var bytes = Encoding.UTF8.GetBytes(csv.ToString());
return File(bytes, "text/csv", "contacts.csv");
}
// GET: Export to PDF (PDF response)
public ActionResponse ExportPdf()
{
var contacts = Database.Query<Contact>().ToList();
return Pdf(contacts, "contacts.pdf");
}
}
Example 2: API Controller with JSON Responses
public class ContactApiController : AspxController
{
// GET: /acls/ContactApi/GetAll
[HandleExceptionsForJson]
public ActionResponse GetAll()
{
var contacts = Database.Query<Contact>()
.OrderBy(c => c.LastName)
.ToList();
return Json(contacts, JsonRequestBehavior.AllowGet);
}
// GET: /acls/ContactApi/Get?id=003R000000haXk3IAE
[HandleExceptionsForJson]
public ActionResponse Get(string id)
{
var contact = Database.Retrieve(id);
if (contact == null)
return Json(new { error = "Contact not found" }, 404, JsonRequestBehavior.AllowGet);
return Json(contact, JsonRequestBehavior.AllowGet);
}
// POST: /acls/ContactApi/Create
[HttpPost]
[HandleExceptionsForJson]
public ActionResponse Create(Contact model)
{
if (ModelState.IsValid)
{
Database.Insert(model);
return Json(new { success = true, id = model.Id, message = "Contact created successfully" });
}
return Json(new { success = false, errors = ModelState }, 400);
}
// POST: /acls/ContactApi/Update
[HttpPost]
[HandleExceptionsForJson]
public ActionResponse Update(Contact model)
{
if (ModelState.IsValid)
{
var existing = Database.Retrieve(model.Id);
if (existing == null)
return Json(new { success = false, error = "Contact not found" }, 404);
Database.Update(model);
return Json(new { success = true, message = "Contact updated successfully" });
}
return Json(new { success = false, errors = ModelState }, 400);
}
// POST: /acls/ContactApi/Delete?id=003R000000haXk3IAE
[HttpPost]
[HandleExceptionsForJson]
public ActionResponse Delete(string id)
{
var contact = Database.Retrieve(id);
if (contact == null)
return Json(new { success = false, error = "Contact not found" }, 404);
Database.Delete(id);
return Json(new { success = true, message = "Contact deleted successfully" });
}
// GET: /acls/ContactApi/Search?query=john
[HandleExceptionsForJson]
public ActionResponse Search(string query)
{
if (string.IsNullOrEmpty(query))
return Json(new { success = false, error = "Query parameter is required" }, 400, JsonRequestBehavior.AllowGet);
var contacts = Database.Query<Contact>()
.Where(c => c.FirstName.Contains(query) ||
c.LastName.Contains(query) ||
c.Email.Contains(query))
.OrderBy(c => c.LastName)
.ToList();
return Json(new { success = true, count = contacts.Count, data = contacts },
JsonRequestBehavior.AllowGet);
}
}
Example 3: Report Generator with Multiple Export Formats
public class ReportsController : AspxController
{
// GET: /aspx/Reports - Show report page (View response)
public override ActionResponse Index()
{
var accounts = Database.Query<Account>()
.Where(a => a.Type == "Customer")
.ToList();
return View(accounts);
}
// GET: /acls/Reports/ExportCsv - CSV export (File response)
public ActionResponse ExportCsv(string type)
{
var accounts = Database.Query<Account>()
.Where(a => a.Type == type)
.OrderBy(a => a.Name)
.ToList();
var csv = new StringBuilder();
csv.AppendLine("Account Name,Type,Industry,Annual Revenue");
foreach (var account in accounts)
csv.AppendLine($"{account.Name},{account.Type},{account.Industry},{account.AnnualRevenue}");
byte[] bytes = Encoding.UTF8.GetBytes(csv.ToString());
return File(bytes, "text/csv", $"accounts_{type}.csv");
}
// GET: /acls/Reports/ExportPdf - PDF export (PDF response)
public ActionResponse ExportPdf(string type)
{
var accounts = Database.Query<Account>()
.Where(a => a.Type == type)
.OrderBy(a => a.Name)
.ToList();
var html = GenerateReportHtml(accounts, type);
return Pdf(html, $"accounts_{type}.pdf");
}
// GET: /acls/Reports/ExportJson - JSON export (Json response)
[HandleExceptionsForJson]
public ActionResponse ExportJson(string type)
{
var accounts = Database.Query<Account>()
.Where(a => a.Type == type)
.OrderBy(a => a.Name)
.Select(a => new
{
a.Name,
a.Type,
a.Industry,
a.AnnualRevenue,
a.Phone,
a.Website
})
.ToList();
return Json(new {
reportType = type,
generatedDate = DateTime.Now,
totalCount = accounts.Count,
data = accounts
}, JsonRequestBehavior.AllowGet);
}
// GET: /acls/Reports/ExportXml - XML export (Content response)
public ActionResponse ExportXml(string type)
{
var accounts = Database.Query<Account>()
.Where(a => a.Type == type)
.OrderBy(a => a.Name)
.ToList();
var xml = new StringBuilder();
xml.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
xml.AppendLine("<accounts>");
foreach (var account in accounts)
{
xml.AppendLine(" <account>");
xml.AppendLine($" <name>{SecurityElement.Escape(account.Name)}</name>");
xml.AppendLine($" <type>{SecurityElement.Escape(account.Type)}</type>");
xml.AppendLine($" <industry>{SecurityElement.Escape(account.Industry)}</industry>");
xml.AppendLine($" <revenue>{account.AnnualRevenue}</revenue>");
xml.AppendLine(" </account>");
}
xml.AppendLine("</accounts>");
return Content(xml.ToString(), "application/xml");
}
private string GenerateReportHtml(List<Account> accounts, string type)
{
return $@"
<html>
<head>
<style>
body {{ font-family: Arial; padding: 20px; }}
h1 {{ color: navy; border-bottom: 2px solid navy; }}
table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: navy; color: white; }}
.footer {{ margin-top: 20px; font-size: 12px; color: gray; }}
</style>
</head>
<body>
<h1>{type} Accounts Report</h1>
<p>Generated: {DateTime.Now:MM/dd/yyyy hh:mm tt}</p>
<p>Total Accounts: {accounts.Count}</p>
<table>
<thead>
<tr>
<th>Account Name</th>
<th>Industry</th>
<th>Annual Revenue</th>
</tr>
</thead>
<tbody>
{string.Join("", accounts.Select(a => $@"
<tr>
<td>{a.Name}</td>
<td>{a.Industry}</td>
<td>${a.AnnualRevenue:N2}</td>
</tr>
"))}
</tbody>
</table>
<div class='footer'>
<p>Generated by Magentrix Portal</p>
</div>
</body>
</html>
";
}
}
Example 4: File Management Controller
public class FileManagerController : AspxController
{
// GET: /aspx/FileManager - Show file list (View response)
public override ActionResponse Index()
{
var files = Database.Query<Document>()
.OrderByDescending(d => d.CreatedDate)
.ToList();
return View(files);
}
// GET: /acls/FileManager/Download?id=xxx - Download file (File response)
public ActionResponse Download(string id)
{
try
{
var document = Database.Retrieve(id);
if (document == null)
return PageNotFound();
// Read file from storage
var fileBytes = Storage.ReadFileBytes(document.StorageId);
return File(fileBytes, document.ContentType, document.FileName);
}
catch (Exception ex)
{
SystemInfo.Debug($"Download error: {ex.Message}");
return RedirectToError("An error occurred while downloading the file.");
}
}
// GET: /acls/FileManager/DownloadFromStorage?blobId=xxx - Stream from storage (StorageFile response)
public ActionResponse DownloadFromStorage(string blobId)
{
try
{
var document = Database.Query<Document>()
.Where(d => d.StorageId == blobId)
.FirstOrDefault();
if (document == null)
return PageNotFound();
// Stream directly from cloud storage
return StorageFile(blobId, document.ContentType, document.FileName);
}
catch (Exception ex)
{
SystemInfo.Debug($"Storage download error: {ex.Message}");
return RedirectToError("An error occurred while accessing the file.");
}
}
// GET: /acls/FileManager/ViewPdf?id=xxx - View PDF inline (StorageFile response)
public ActionResponse ViewPdf(string id)
{
var document = Database.Retrieve<Document>(id);
if (document == null)
return PageNotFound();
if (document.ContentType != "application/pdf")
return RedirectToError("Only PDF files can be viewed inline.");
// Fourth parameter: true = open inline in browser
return StorageFile(document.StorageId, document.ContentType, document.FileName, true);
}
// POST: /acls/FileManager/Upload - Upload file (Json response)
[HttpPost]
[HandleExceptionsForJson]
public ActionResponse Upload(HttpPostedFileBase file, string description)
{
try
{
if (file == null || file.ContentLength == 0)
return Json(new { success = false, error = "No file uploaded" }, 400);
// Read file content
var fileBytes = new byte[file.ContentLength];
file.InputStream.Read(fileBytes, 0, file.ContentLength);
// Save to storage
var storageId = Storage.WriteFile(fileBytes, file.FileName, file.ContentType);
// Create document record
var document = new Document
{
FileName = file.FileName,
ContentType = file.ContentType,
FileSize = file.ContentLength,
StorageId = storageId,
Description = description,
UploadedBy = UserInfo.Name,
UploadedDate = DateTime.Now
};
Database.Insert(document);
return Json(new {
success = true,
documentId = document.Id,
message = "File uploaded successfully"
});
}
catch (Exception ex)
{
SystemInfo.Debug($"Upload error: {ex.Message}");
return Json(new { success = false, error = "Upload failed" }, 500);
}
}
}
Example 5: Dashboard with AJAX Integration
public class DashboardController : AspxController
{
// GET: /aspx/Dashboard - Main dashboard page (View response)
public override ActionResponse Index()
{
return View();
}
// GET: /acls/Dashboard/GetStats - Get dashboard statistics (Json response)
[HandleExceptionsForJson]
public ActionResponse GetStats()
{
var stats = new
{
totalAccounts = Database.Query<Account>().Count(),
totalContacts = Database.Query<Contact>().Count(),
openOpportunities = Database.Query<Opportunity>()
.Where(o => !o.IsClosed)
.Count(),
totalRevenue = Database.Query<Opportunity>()
.Where(o => o.Stage == "Closed Won")
.ToList()
.Sum(o => o.Amount),
thisMonthRevenue = Database.Query<Opportunity>()
.Where(o => o.Stage == "Closed Won" &&
o.CloseDate >= DateTime.Now.AddMonths(-1))
.ToList()
.Sum(o => o.Amount)
};
return Json(stats, JsonRequestBehavior.AllowGet);
}
// GET: /acls/Dashboard/GetRecentActivity - Get recent activities (Json response)
[HandleExceptionsForJson]
public ActionResponse GetRecentActivity(int count = 10)
{
var recentOpportunities = Database.Query<Opportunity>()
.OrderByDescending(o => o.LastModifiedDate)
.Take(count)
.Select(o => new
{
id = o.Id,
name = o.Name,
stage = o.Stage,
amount = o.Amount,
accountName = o.Account.Name,
lastModified = o.LastModifiedDate
})
.ToList();
return Json(new {
success = true,
data = recentOpportunities
}, JsonRequestBehavior.AllowGet);
}
// GET: /acls/Dashboard/ExportSnapshot - Export dashboard snapshot (PDF response)
public ActionResponse ExportSnapshot()
{
var stats = new
{
TotalAccounts = Database.Query<Account>().Count(),
TotalContacts = Database.Query<Contact>().Count(),
OpenOpportunities = Database.Query<Opportunity>().Where(o => !o.IsClosed).Count(),
TotalRevenue = Database.Query<Opportunity>()
.Where(o => o.Stage == "Closed Won")
.ToList()
.Sum(o => o.Amount)
};
var html = $@"
<html>
<head>
<style>
body {{ font-family: Arial; padding: 20px; }}
h1 {{ color: navy; }}
.metric {{ background: #f0f0f0; padding: 15px; margin: 10px 0; }}
.metric-value {{ font-size: 24px; font-weight: bold; color: navy; }}
</style>
</head>
<body>
<h1>Dashboard Snapshot</h1>
<p>Generated: {DateTime.Now:MM/dd/yyyy hh:mm tt}</p>
<div class='metric'>
<div>Total Accounts</div>
<div class='metric-value'>{stats.TotalAccounts}</div>
</div>
<div class='metric'>
<div>Total Contacts</div>
<div class='metric-value'>{stats.TotalContacts}</div>
</div>
<div class='metric'>
<div>Open Opportunities</div>
<div class='metric-value'>{stats.OpenOpportunities}</div>
</div>
<div class='metric'>
<div>Total Revenue (Closed Won)</div>
<div class='metric-value'>${stats.TotalRevenue:N2}</div>
</div>
</body>
</html>
";
return Pdf(html, $"Dashboard_Snapshot_{DateTime.Now:yyyyMMdd}.pdf");
}
}
JavaScript in Active Page calls the AJAX endpoints:
// Fetch dashboard statistics
fetch('/acls/Dashboard/GetStats')
.then(response => response.json())
.then(data => {
document.getElementById('totalAccounts').textContent = data.totalAccounts;
document.getElementById('totalContacts').textContent = data.totalContacts;
document.getElementById('openOpportunities').textContent = data.openOpportunities;
document.getElementById('totalRevenue').textContent = '$' + data.totalRevenue.toLocaleString();
});
// Fetch chart data
fetch('/acls/Dashboard/GetChartData?chartType=revenue-by-month')
.then(response => response.json())
.then(result => {
if (result.success) {
renderChart(result.data);
}
});
Best Practices
1. Choose the Right Response Type
✅ Use View() for user-facing pages
public override ActionResponse Index()
{
return View(model);
}
✅ Use Json() for APIs
[HandleExceptionsForJson]
public ActionResponse GetData()
{
return Json(data, JsonRequestBehavior.AllowGet);
}
✅ Use Redirect() after POST to prevent resubmission
[HttpPost]
public ActionResponse Create(Contact model)
{
Database.Insert(model);
return RedirectToAction("Index"); // ✅ Correct
// return View(model); // ❌ Avoid - causes form resubmission
}
2. Always Use [HandleExceptionsForJson] for JSON Responses
❌ Without attribute:
public ActionResponse GetContact(string id)
{
var contact = Database.Retrieve<Contact>(id);
return Json(contact, JsonRequestBehavior.AllowGet);
}
// If an exception occurs, response is HTML error page, not JSON
✅ With attribute:
[HandleExceptionsForJson]
public ActionResponse GetContact(string id)
{
var contact = Database.Retrieve<Contact>(id);
return Json(contact, JsonRequestBehavior.AllowGet);
}
// Exceptions are automatically returned as JSON
3. Use Absolute URLs for PDF Resources
❌ Incorrect:
<img src="/images/logo.png" />
<link rel="stylesheet" href="/css/styles.css" />
✅ Correct:
<img src="https://yourportal.magentrix.com/images/logo.png" />
<link rel="stylesheet" href="https://yourportal.magentrix.com/css/styles.css" />
4. Set Appropriate Content Types
// CSV files
return File(bytes, "text/csv", "data.csv");
// Excel files
return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "data.xlsx");
// JSON text
return Content(json, "application/json");
// XML text
return Content(xml, "application/xml");
5. Use StorageFile for Cloud-Stored Files
❌ Inefficient:
public ActionResponse Download(string id)
{
// Loads entire file into memory
var fileBytes = Storage.ReadFileBytes(id);
return File(fileBytes, contentType, fileName);
}
✅ Efficient:
public ActionResponse Download(string id)
{
// Streams directly
return StorageFile(id, contentType, fileName);
}
6. Provide Meaningful Filenames
❌ Generic:
return File(bytes, "text/csv", "export.csv");
✅ Descriptive:
return File(bytes, "text/csv", $"Contacts_Export_{DateTime.Now:yyyyMMdd}.csv");
7. Handle Errors Gracefully
public ActionResponse Download(string id)
{
try
{
var document = Database.Retrieve<Document>(id);
if (document == null)
return PageNotFound();
var fileBytes = Storage.ReadFileBytes(document.StorageId);
return File(fileBytes, document.ContentType, document.FileName);
}
catch (Exception ex)
{
SystemInfo.Error(ex);
return RedirectToError("An error occurred while downloading the file.");
}
}
Common Pitfalls
❌ Pitfall 1: Using View() in Standalone Controllers
// URL: /acls/MyApi/GetData
public class MyApiController : AspxController
{
public ActionResponse GetData()
{
// ❌ ERROR: No Active Page exists!
return View();
}
}
Solution:
[HandleExceptionsForJson]
public ActionResponse GetData()
{
var data = Database.Query<Contact>().ToList();
// ✅ Correct
return Json(data, JsonRequestBehavior.AllowGet);
}
❌ Pitfall 2: Returning View After POST
[HttpPost]
public ActionResponse Create(Contact model)
{
Database.Insert(model);
// ❌ Causes form resubmission on refresh
return View(model);
}
Solution:
[HttpPost]
public ActionResponse Create(Contact model)
{
Database.Insert(model);
// ✅ Correct - PRG pattern
return RedirectToAction("Index");
}
❌ Pitfall 3: Forgetting JsonRequestBehavior.AllowGet
public ActionResponse GetContacts()
{
var contacts = Database.Query<Contact>().Limit(10).ToList();
// ❌ Only works for POST
return Json(contacts);
}
Solution:
[HandleExceptionsForJson]
public ActionResponse GetContacts()
{
var contacts = Database.Query<Contact>().Limit(10).ToList();
// ✅ Works for GET
return Json(contacts, JsonRequestBehavior.AllowGet);
}
❌ Pitfall 4: Not Checking for Null Before Returning View
public ActionResponse Details(string id)
{
var contact = Database.Retrieve<Contact>(id);
// ❌ Crashes if contact is null
return View(contact);
}
Solution:
public ActionResponse Details(string id)
{
var contact = Database.Retrieve<Contact>(id);
// ✅ Graceful error handling
if (contact == null)
return PageNotFound();
return View(contact);
}
Summary
This section covered all response types available in Magentrix controllers:
✅ View Responses - Render Active Pages for user-facing interfaces
✅ PartialView Responses - Render page fragments and widgets
✅ Redirect Responses - Navigate to URLs or controller actions
✅ File Responses - Stream binary files for download
✅ StorageFile Responses - Stream files from Magentrix Cloud Storage
✅ PDF Responses - Generate dynamic PDF documents
✅ Json Responses - Return JSON data for APIs
✅ Content Responses - Return text, HTML, XML, or other content
✅ Special Responses - Handle errors and unauthorized access
Next Steps
Continue your learning journey:
Quick Reference
Response Type Cheat Sheet
// View responses
return View();
return View(model);
return PartialView(model);
return PageNotFound();
return RecordDeleted();
// Redirect responses
return Redirect("~/home/index");
return RedirectToAction("Index");
return RedirectToAction("Edit", "Contact", new { id = "123" });
return Redirect(ActivePages.MyPage, new { id = "123" });
// File responses
return File(bytes, "text/csv", "data.csv");
return StorageFile(blobId, contentType, fileName);
return Pdf(model, "report.pdf");
// Data responses
return Json(data, JsonRequestBehavior.AllowGet);
return Content("Hello World");
return Content(html, "text/html");
// Error responses
return UnauthorizedAccessResponse();
return RedirectToError("Error message");
💡 Mastering response types is essential for building robust controllers! Choose the right response type for each scenario to create efficient, user-friendly applications.