ValidateAntiForgeryToken throwing error - validation

My controller is throwing the error
The required anti-forgery form field "__RequestVerificationToken" is not present.
But This is exactly what I am doing
Logging in with test user
VIEW
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Log in Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</li>
<li>
#Html.LabelFor(m => m.RememberMe)
#Html.CheckBoxFor(m => m.RememberMe)
</li>
CONTROLLER
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToCreateUserProfile(model, returnUrl);
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
once authenticated i am redirected to the home page
then i click on a menu option to show me user profile and i get the above error
LAYOUT VIEW (Showing more code the needed but want to make the JS is causing an issue)
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/kendo/2013.2.918/kendo.common.min.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/kendo/2013.2.918/kendo.dataviz.min.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/kendo/2013.2.918/kendo.metro.min.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/kendo/2013.2.918/kendo.dataviz.metro.min.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/kendo/2013.2.918/jquery.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo/2013.2.918/kendo.all.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo/2013.2.918/kendo.aspnetmvc.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo.modernizr.custom.js")"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
var pluginUrl =
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
_gaq.push(['_setAccount', 'UA-44529127-1']);
_gaq.push(['_trackPageview']);
(function () {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<header>
<div class="content-wrapper">
<div class="float-left">
<p class="site-title">#Html.ActionLink("your logo here", "Index", "Home")</p>
</div>
<div class="float-right">
<section id="login">
#Html.Partial("_LoginPartial")
</section>
<nav>
<ul id="menu">
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "About", "Home")</li>
<li>#Html.ActionLink("Contact", "Contact", "Home")</li>
#if (User.IsInRole("Admin"))
{
<li>#Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>
}
</ul>
</nav>
</div>
</div>
</header>
<div id="body">
#if (Request.IsAuthenticated)
{
<ul id="IndexHomeMenu">
#if (User.IsInRole("Admin"))
{
<li>
Administration#*#Html.ActionLink("Administration", "Contact", "Home")*#
<ul>
<li>#Html.ActionLink("Manage Roles", "Index", "AdminView")</li>
<li>#Html.ActionLink("Manage Users", "Contact", "Home")</li>
<li>#Html.ActionLink("Inactive Reasons", "Index", "InactiveReasonView")</li>
</ul>
</li>
}
<li>
My Information
<ul>
<li>#Html.ActionLink("Profile", "EditByName", "UserView", new { UserName = User.Identity.Name }, new { #class = "selected" })</li>
<li>#Html.ActionLink("Phone Numbers", "Active", "PhoneNumberView",new {userName= User.Identity.Name },null)</li>
<li>#Html.ActionLink("Address's", "Active", "AddressView",new {userName= User.Identity.Name },null)</li>
#if(!User.IsInRole("Clients")){
<li>#Html.ActionLink("Subscription", "Index", "AdminView")</li>}
</ul>
I am clicking on #Html.ActionLink("Profile", "EditByName", "UserView", new { UserName = User.Identity.Name }, new { #class = "selected" })
CONTROLLER
[ValidateAntiForgeryToken]
public ActionResult EditByName(string userName)//EditByName
{
if (User.Identity.IsAuthenticated)
{
UserModel usermodel = repository.Get(User.Identity.Name);// db.UserModels.Find(id);
if (usermodel == null)
{
return RedirectToAction("Create","UserView", User.Identity.Name);
}
return View(usermodel);
}
else { return RedirectToAction("Login", controllerName: "AccountView"); }
}
This is when the error occurs. and im not sure whats missing, I am creating the token and it is on all forms.

You are using the [ValidateAntiForgeryToken] on a GET action (The EditByName action), while it is meant to work on POST actions.
See this question on the [ValidateAntiForgeryToken] purpose and this article explaining how to prevent CSRF attacks using it.

Remove the [ValidateAntiForgeryToken] from the EditByName GET-action method.
Also, use the [Authorize] atrribute instead of if (User.Identity.IsAuthenticated).
And may any user edit any profile, as long as they know the username?

Related

How to utilize re-usable Razor component/view/page (Razor Class Library and ASP.NET Core 6 MVC) through plain html and Javascript?

I are trying to develop a shareable widget.
I am developing by using razor.view, razor.page and razor.component (project templates are Razor Class Library and ASP.NET Core 6 MVC).
_Imports.razor under RCL Project is as follows:
#using System.Net.Http
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.Authorization
#using Microsoft.AspNetCore.Components.Forms
#using Microsoft.AspNetCore.Components.Routing
#using Microsoft.AspNetCore.Components.Web
#using Microsoft.JSInterop
#using System.IO
#using CarRentalWidget.Models.DBEntities.Franchises
#using CarRentalWidget.Models.ViewModels.GooogleGeoCodes
AutoCompleteLocationsComponent.razor is as follows:
#using System.Net.Http
#using System.Net.Http.Json
#using System.Threading.Tasks
#inject HttpClient Http
#namespace CarRentalWidget.RCL.CarWidgetUI.Pages
<div>
<input
type="text"
id="TxtBxPickUpLoc"
class="postcode tf-form-control"
placeholder="Please Enter Postcode or Town"
value="#inputValue"
#onchange="DoChangeLocation"
autoComplete="off"
/>
#if(!string.IsNullOrWhiteSpace(inputValue))
{
<a className="location" href="#" id="branch-selector" onClick={onClearLocations}></a>
}
else if (string.IsNullOrWhiteSpace(inputValue))
{
<a className="location empty" href="#" id="branch-selector" onClick={onClearLocations}></a>
}
</div>
<div class="sautocomplete-content-container tk-search-drop-wrapper ac-drop-active">
<div class="sautocomplete-content tk-search-drop">
<div class="branch-info">
<ul id="LocationSuggestions" class="suggestions">
#if (locationData != null && locationData.Count() > 0)
{
#foreach (FranchiseWebInquiry suggestion in locationData!)
{
// Flag the active suggestion with a class
if (index == activeSuggestionIndex)
{
className = "suggestion-active";
}
<li class="#className" key="#index + #suggestion.FranchiseName" onClick="onClick(suggestion)">
<a pickupid="#suggestion.PickupId + #suggestion.FranchisesId" franchisesid="#suggestion.FranchisesId" subofficeid="#suggestion.SubOfficeId" href="#">
<span class="locName">#suggestion.FranchiseName</span>
<span class="locDetails">#getAddress(suggestion)
</span>
</a>
</li>
++index;
}
}
</ul>
</div>
</div>
</div>
#code {
private string inputValue = "";
private string className = "";
private int activeSuggestionIndex;
private int index = 0;
private FranchiseWebInquiry[]? locationData;
private bool getError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
private GoogleGeocode? currentMapItems;
private async Task<GoogleGeocode?> loadGoogleGeoCodeData(string postCodeOrTown)
{
currentMapItems = await Http.GetFromJsonAsync<GoogleGeocode?>
("https://maps.googleapis.com/maps/api/geocode/json?types=geocode&language=en&key=mykey&address=" + postCodeOrTown);
return currentMapItems;
}
private async Task getLocationsApi(string postCodeOrTown)
{
await loadGoogleGeoCodeData(postCodeOrTown);
Result? serviceResult = currentMapItems!.Results!.FirstOrDefault();
Location? locDetail = serviceResult != null && serviceResult.Geometry != null ? serviceResult.Geometry.Location:null;
double providedLattitude = locDetail != null ? locDetail.Lat : 0;
double providedLongitude = locDetail != null ? locDetail.Lng : 0;
locationData = await Http.GetFromJsonAsync<FranchiseWebInquiry[]?>
("https://localhost:7177/api/CarRental/allfranchises/?postCodeOrAddress=" + postCodeOrTown + "&lattitude=" + providedLattitude + "&longitude=" + providedLongitude);
}
private async void DoChangeLocation(ChangeEventArgs e)
{
inputValue = e.Value!.ToString();
if (inputValue!.Length >= 5)
{
await getLocationsApi(inputValue);
}
Console.WriteLine("It is definitely: " + inputValue);
}
private string getAddress(FranchiseWebInquiry suggestion)
{
//debugger;
string localAddress = "";
if (suggestion != null)
{
if (!string.IsNullOrWhiteSpace(suggestion.Address1))
{
localAddress = suggestion.Address1 + " , ";
}
if (!string.IsNullOrWhiteSpace(suggestion.Address2))
{
localAddress += suggestion.Address2 + " , ";
}
if (!string.IsNullOrWhiteSpace(suggestion.Address3))
{
localAddress += suggestion.Address3 + " , ";
}
if (!string.IsNullOrWhiteSpace(suggestion.Town))
{
localAddress += suggestion.Town + "- " + (!string.IsNullOrWhiteSpace(suggestion.PostCode) ? "(" + suggestion.PostCode + "), ":"");
}
if (!string.IsNullOrWhiteSpace(suggestion.PrimaryContact))
{
localAddress += suggestion.PrimaryContact;
}
}
return localAddress;
}
}
and Index.cshtml is as follows:
#page "/car-index"
#model CarRentalWidget.RCL.CarWidgetUI.Pages.IndexPageModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Car Rental Widget</title>
</head>
<body>
<div class="widget-wrapper">
<div class="main-heading">
<h1>
Test 1
</h1>
</div>
<div class="widget-slider">
<div class="row step-row">
<div class="col-12">
<div class="pickup-location tf-search-group">
<div class="label-location tf-search-label">pick up location</div>
<div id="PickUpLocationControlId" class="input-group-location tf-search-field">
<div class="card">
#(await Html.RenderComponentAsync<CarRentalWidget.RCL.CarWidgetUI.Pages.AutoCompleteLocationsComponent>
(RenderMode.ServerPrerendered/*,new { Data="I came from Index",style= "badge-danger" }*/))
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Now I just added the reference in my ASP.NET Core 6 MVC project and open the browser and tried this url in the browser.
https://localhost:7177/car-index
Everything is working as expected. No issue till so far.
Now I am trying to consume this through a plain html (and Javascript/jQuery).
My client end application request is as follows:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
$.ajax({url: "https://localhost:7177/car-index", success: function(result){
$("#CarWidget").html(result);
}});
});
});
</script>
</head>
<body>
<div id="CarWidget">
</div>
</body>
</html>
See I tried with above script, there is no error code even in Browser Network tab.
Why?
For detailed understanding, you can follow this link as well
https://learn.microsoft.com/answers/comments/872715/view.html
and these links
https://learn.microsoft.com/en-us/aspnet/core/razor-pages/ui-class?view=aspnetcore-6.0&tabs=visual-studio
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/class-libraries?view=aspnetcore-6.0&tabs=visual-studio

does not contain public definition for get enumerator

my view statement shows error that " for each statement cannot operate on variables of type public definition for 'get enumerator' "
I defined all objects in model and view models. still i get this error. any solution for this?
MY VIEW:
#using Edi.ViewModel
#model viewmodel
#{
<link rel="stylesheet" type="text/css" href="~/Content/style.css" />
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<form id="form1" name="zoo">
<div class="med">
<pre>
<fieldset id="field1">
<legend>Bill Date Range</legend>
<input type="checkbox">After <input type="date" /> <input type="checkbox">Before <input type="date" />
</fieldset>
<fieldset id="field2">
<legend>Options</legend>
<label>Facility</label> #Html.DropDownListFor(Model => Model.Facility, Model.Facility, "Select Facility", new { style = "width:200px; height :30px" })
<label>Ins Queue</label> #Html.DropDownListFor(Model => Model.InsQueue, Model.InsQueue, "Select InsQueue", new { style = "width:200px; height:30px" })
Claim type <select style="width: 100px; height:30px"><option value="ALL">All</option><option value="NEW">New</option><option value="RESUBMIT">Resubmit</option></select>
</fieldset>
<pre>
<label>No of Bills Selected </label><input type="text" size="1" /> <label> EDI send To </label> #Html.DropDownListFor(Model => Model.EdiSendTo, Model.EdiSendTo, "Select Edi", new { style = "width:200px; height :30px" }) <button type="button" onclick="">Select All</button> <button type="reset" onclick="">Refresh</button> <button type="button" onclick="">Clear All</button> <button type="button" onclick="">Edit Bill</button>
</pre>
<table id="myTable">
<tr class="hd">
<th>visit</th>
<th>billno</th>
<th>patient</th>
<th>providername</th>
<th>insname</th>
<th>payby</th>
<th>inscode</th>
<th>user</th>
<th>claimtype</th>
</tr>
-------------------#foreach (var items in Model)------------------------------
{
<tr onclick="javascript:showRow1(this);">
<td>#items.visit</td>
<td>#items.billno</td>
<td>#items.Patient</td>
<td>#items.ProviderName</td>
<td>#items.InsName</td>
<td>#items.PayBy</td>
<td>#items.InsCode</td>
<td>#items.User</td>
<td>#items.ClaimType</td>
</tr>
}
</table>
</div>
</form>
</body>
</html>
MY CONTROLLER:
namespace Edi.Controllers
{
public class HomeController :Controller
{
// GET: Home
public ActionResult Index()
{
mbill mt = new mbill();
billing db = new billing();
viewmodel vm = new viewmodel();
vm.Facility = db.Add1();
vm.EdiSendTo = db.Add2();
vm.InsQueue = db.Add3();
List<mbill> itemlist = new List<mbill>();
itemlist = db.Index();
return View("Index",vm);
}
}
}
MY MODEL:
public List<mbill> Index()
{
List<mbill> itemList = new List<mbill>();
connection();
SqlCommand cmd = new SqlCommand("procGetEDIBills", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataAdapter sd = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
sd.Fill(dt);
mbill item = new mbill();
using (SqlDataReader sdr = cmd.ExecuteReader())
{
mbill io = new mbill();
while (sdr.Read())
{
io = new mbill();
io.visit = sdr["VisitDate"].ToString();
io.billno = sdr["Billno"].ToString();
io.Patient = sdr["PatientName"].ToString();
io.ProviderName = sdr["ProviderName"].ToString();
io.InsName = sdr["InsuranceName"].ToString();
io.PayBy = sdr["BillPayBy"].ToString();
io.InsCode = sdr["InsuranceId"].ToString();
io.User = sdr["QueueByUser"].ToString();
io.ClaimType = sdr["ClaimType"].ToString();
itemList.Add(io);
}
}
con.Close();
return itemList;
}
}
if i delete #using Edi.ViewModel, #model viewmodel, on view then foreach error disappears and dropdownlistfor shows does not contain definition for dropdownlistfor error.

asp.net mvc + Ajax.BeginForm OnBegin not firing

After reading several posts regarding this issue, it is mostly related to including
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
and the proper order of including jquery first.
<script src="~/Scripts/jquery-1.12.4.js"></script>
I think I did properly, but OnBegin is not firing still.
_Layout.cshtml:
<head>
<meta charset="utf-8" />
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
<title>Test</title>
#Scripts.Render("~/bundles/modernizr")
<script src="~/Scripts/jquery-1.12.4.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<script src="~/Scripts/respond.min.js"></script>
<script src="~/Scripts/jquery.dataTables.js"></script>
<script src="https://cdn.datatables.net/responsive/2.1.0/js/dataTables.responsive.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.2.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.2.2/js/buttons.colVis.min.js"></script>
<script src="~/Scripts/light-bootstrap-dashboard.js"></script>
<script src="~/Scripts/bootstrap-notify.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
#RenderSection("scripts", required: false)
</head>
So the order of process is:
School View:
#model Test.Models.School
<div class="editorRow">
#using (Html.BeginCollectionItem("schools"))
{
#Html.ActionLink("New Employee", "NewEmployee", "Test", new
{
#class = "btn btn-info btn-md",
data_toggle = "modal",
data_target = "#modal-container-employee"
})
</div>
<div id="modal-container-employee" class="modal fade" tableindex="-1" role="dialog">
<div class="modal-content">
<div class="modal-body">
</div>
</div>
</div>
Controller:
public ActionResult NewEmployee()
{
var newEmployee = new Employee();
return View(newEmployee);
}
Employee view:
#model Test.Models.Employee
#{
Layout = null;
}
#using (Ajax.BeginForm("PostEmployee", "Employee", new AjaxOptions
{
HttpMethod = "post",
InsertionMode = InsertionMode.Replace,
OnBegin = "closeModal"
}))
{
#Html.AntiForgeryToken()
<div id="test" class="">
<div class="modal-title title">
<input type="submit" id="submit" value="Save" /><span id="closes">x</span>
</div>
#Html.EditorFor(model => model.FullName)
</div>
}
<script>
function closeModal() {
$('#modal-container').modal('hide');
}
</script>
controller:
[HttpPost]
public ActionResult PostEmployee(Employee model)
{
try
{
var newEmployee = new Employee
{
FullName = model.FullName
};
db.Employees.Add(newEmployee);
db.SaveChanges();
return Json(new { status = "success" }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(new { status = "error", errorMessage = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
As a result, "success" Json is returned, but ajax is not catching it, it directly goes to the other page where it shows JSON array {"status":"success"}.
Can anyone find the problem?

ASP.NET MVC 3 RAZOR : How to get value of querystring in another controller action?

This is Index view:
<script type="text/jscript" language="javascript">
$(document).ready(function () {
$("#tabs").tabs();
});
</script>
<div class="demo">
<div id="tabs">
<ul>
<li>Overview</li>
<li><a href="/CarDetails/Specification?Id=" + '#ViewBag.versionId'>Specifications</a></li>
</ul>
<div>
</div>
<div>
</div>
</div>
</div>
public class CarDetailsController : Controller
{
public ActionResult Index(int? versionID)
{
ViewBag.versionId = versionID;
return View();
}
public ActionResult Specification(int Id)
{
return PartialView(CarDetails.GetCarModelVersionDetailsByConfigGrpId(Id));
}
}
Above controller is being called by
#Html.ActionLink(item.VersionName, "Index", "CarDetails", new { versionID = item.ModelVersionId },null)
I need querystring value of versionID within Specification action of CarDetailsControl.
But it becomes empty.
Please guide me.
Thanks,
#Paul
changing to
<li><a href="/CarDetails/Specification?versionID=#ViewBag.versionId" >Specifications</a></li>
has resolved my problem.

MVC3 Ajax.BeginForm with javascript disabled

I'm having a problem getting a form to work without javascript being enabled.
This should be enough to go on, ask if you need to know anything else - I don't want to just put the whole solution up here!
~/Views/_ViewStart.cshtml:
#{ Layout = "~/Views/Shared/Layout.cshtml"; }
~/Views/Shared/Layout.cshtml:
#using System.Globalization; #{ CultureInfo culture = CultureInfo.GetCultureInfo(UICulture); }<!DOCTYPE html>
<html lang="#culture.Name" dir="#(culture.TextInfo.IsRightToLeft ? "rtl" : "ltr")">
<head>
<title>AppName :: #ViewBag.Title</title>
<link href="#Url.Content("~/favicon.ico")" rel="shortcut icon" type="image/x-icon" />
<link href="#Url.Content("~/apple-touch-icon.png")" rel="apple-touch-icon" />
<link href="#Url.Content("~/Content/css/site.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Content/js/jquery-1.6.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Content/js/app.js")" type="text/javascript"></script>
#RenderSection("SectionHead", false)
</head>
<body>
<div id="page-container">
<div id="nav">
<div id="nav-user">
#{ Html.RenderAction("LoginStatus", "Account"); }
#{ Html.RenderPartial("CultureSelector"); }
</div>
</div>
<div id="page-content">
<h2>#ViewBag.Title</h2>
#RenderBody()
</div>
</div>
</body>
</html>
~/Views/Account/Index.cshtml:
#model AccountFilterModel
#{
ViewBag.Title = "Account Home";
var loadingId = "loading" + new Random().Next();
Model.FilterFormId = "filter-account-form";
}
#using (Ajax.BeginForm("List", "Account", Model, new AjaxOptions { UpdateTargetId = "result-list", LoadingElementId = loadingId }, new { id = "filter-account-form" })) {
<!-- form controls and validation summary stuff -->
<input id="filter" type="submit" value="Filter" />
<span id="#loadingId" style="display: none">
<img src="#Url.Content("~/Content/images/ajax-loader.gif")" alt="Loading..." />
</span>
}
<div id="result-list">
#{ Html.RenderAction("List", Model); }
</div>
~/Views/Account/List.cshtml:
#model FilterResultModel
#helper SortLink(AccountSort sort, SortDirection dir) {
string display = (dir == SortDirection.Ascending ? "a" : "d"); // TODO: css here
if (Model.Filter.SortBy != null && ((AccountSortModel)Model.Filter.SortBy).Sort == sort && dir == Model.Filter.SortOrder) {
#:#display
} else {
FilterModel fm = new FilterModel(Model.Filter);
fm.SortBy = AccountSortModel.SortOption[sort];
fm.SortOrder = dir;
#display
}
}
#if (Model.Results.Count > 0) {
var first = Model.Results.First();
<table>
<caption>
#string.Format(LocalText.FilterStats, Model.FirstResultIndex + 1, Model.LastResultIndex + 1, Model.CurrentPageIndex + 1, Model.LastPageIndex + 1, Model.FilteredCount, Model.TotalCount)
</caption>
<thead>
<tr>
<th>
#Html.LabelFor(m => first.Username)
<span class="sort-ascending">
#SortLink(AccountSort.UsernameLower, SortDirection.Ascending)
</span>
<span class="sort-descending">
#SortLink(AccountSort.UsernameLower, SortDirection.Descending)
</span>
</th>
<!-- other table headers -->
</tr>
</thead>
<tbody>
#foreach (AccountModel account in Model.Results) {
<tr>
<td>#Html.EncodedReplace(account.Username, Model.Filter.Search, "<span class=\"filter-match\">{0}</span>")</td>
<!-- other columns -->
</tr>
}
</tbody>
</table>
Html.RenderPartial("ListPager", Model);
} else {
<p>No Results</p>
}
Relevant part of AccountController.cs:
public ActionResult Index(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue) fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
return View(fm);
}
public ActionResult List(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue) fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
return Request.IsAjaxRequest() ? (ActionResult)PartialView("List", Service.Get(fm)) : View("Index", model);
}
With javascript enabled, this works fine - the content of div#result-list is updated as expected.
If I don't do the Request.AjaxRequest() and just return the PartialView, then with javascript disabled I get a page with just the content of the results on it. If I have the code as above, then I get a StackOverflowException.
How do I get this to work?
Solution
Thanks to #xixonia, I discovered the problem - here is my solution:
public ActionResult List(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue)
fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
if (Request.HttpMethod == "GET")
return PartialView("List", Service.Get(fm));
if (Request.HttpMethod == "POST")
return Request.IsAjaxRequest() ? (ActionResult) PartialView("List", Service.Get(fm)) : RedirectToAction("Index", model);
return new HttpStatusCodeResult((int) HttpStatusCode.MethodNotAllowed);
}
You can use the following extension method to determine if the request is an ajax request
Request.IsAjaxRequest()
If it is, you can return a partial view, otherwise you can return a full view or redirect.
if(Request.IsAjaxRequest())
{
return PartialView("view", model);
}
else
{
return View(model);
}
edit: here's the problem:
The "List" is returning the "Index" view when the request is not an AJAX request:
public ActionResult List(AccountSort? accountSort, FilterModel model = null) {
FilterModel fm = model ?? new FilterModel();
if (accountSort.HasValue) fm.SortBy = AccountSortModel.SortOption[accountSort.Value];
return Request.IsAjaxRequest() ? (ActionResult)PartialView("List", Service.Get(fm)) : View("Index", model);
}
The "Index" view is rendering the "List" action:
#{ Html.RenderAction("List", Model); }
AKA: Recursion.
You need to engineer a way to display your list without drawing the index page, or make your index page draw a partial view with your list modal as a parameter.

Resources