我在ASP .Net Core MVC应用程序中有一个分页表,其中表的第一个“页面”正确显示指定的表行数。问题是,当按下“下一步”按钮将异步调用发送到控制器操作以获取下一组行时,服务器无法找到控制器操作。
我一直在关注this guide,并且概述了以下代码应该用于视图中的页面链接(在本例中修改后使用):
@{
var prevDisabled = !Model.PageList.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.PageList.HasNextPage ? "disabled" : "";
}
<a asp-action="GetResultList"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-page="@(Model.PageList.PageIndex - 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @prevDisabled">
Previous
</a>
<a asp-action="GetResultList"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-page="@(Model.PageList.PageIndex + 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @nextDisabled">
Next
</a>
最初加载表单时,网址中的链接为http://localhost:62501/Home/..%2FresortDeals%2FGetResultList
,但点击“下一步”按钮时,网址为:http://localhost:62501/resortDeals/GetResultList?page=2
这很奇怪,因为表单提交按钮的asp动作具有相同的值:
<div>
<input type="submit" value="View Results" asp-action="GetResultList" class="btn btn-sm" />
</div>
我假设问题可能与asp-route-page
中指定的页面参数的值有关,但我不确定为什么它可能导致有一个非常不同的网址被用作我仍然很擅长使用标记助手。 可能导致“下一步”按钮生成错误网址的原因是什么?
控制器操作:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> GetResultList(ResortDataJoinObj resDeals, int page =1)
{
if (ModelState.IsValid)
{
var resultsObj = (from rd in _db.ResortData
join ra in _db.ResortAvailability on rd.RecNo equals ra.RecNoDate
where ra.TotalPrice < Int32.Parse(resDeals.priceHighEnd) && ra.TotalPrice > Int32.Parse(resDeals.priceLowEnd)
select new ResortDealResultsObject
{
Name = rd.Name,
ImageUrl = rd.ImageUrl,
ResortDetails = rd.ResortDetails,
CheckIn = ra.CheckIn,
Address = rd.Address,
TotalPrice = ra.TotalPrice
});
int i = 0;
List<ResortDealResultsObject> resultList = new List<ResortDealResultsObject>();
foreach (var row in resultsObj)
{
var tempVm = new ResortDealResultsObject
{
Name = row.Name,
ImageUrl = row.ImageUrl,
ResortDetails = row.ResortDetails,
CheckIn = row.CheckIn,
Address = row.Address,
TotalPrice = row.TotalPrice
};
resultList.Add(tempVm);
}
int pageSize = 3;
var model = await PaginatedList<ResortDealResultsObject>.CreateAsync(resultsObj, page, pageSize);
ResortDataJoinObj joinObj = new ResortDataJoinObj();
joinObj.PageList = model;
ViewBag.rowsReturned = true;
return View(joinObj);
}
return View(resDeals);
}
HTML表格:
<form asp-action="GetResultList" method="post">
<div>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<div class="row">
<div class="col-sm-1">
<label asp-for="ResData.Name"></label>
<input asp-for="ResData.Name" />
<span asp-validation-for="ResData.Name"></span>
</div>
@*<div class="col-sm-2">
<label asp-for="ResAvail.CheckIn"></label>
<input asp-for="ResAvail.CheckIn" />
<span asp-validation-for="ResAvail.CheckIn"></span>
</div>
<div class="col-sm-3">
<label asp-for="ResAvail.CheckOut"></label>
<input asp-for="ResAvail.CheckOut" />
<span asp-validation-for="ResAvail.CheckOut"></span>
</div>*@
<div class="col-sm-4">
<label asp-for="priceLowEnd"></label>
<input asp-for="priceLowEnd" />
<span asp-validation-for="priceLowEnd"></span>
</div>
<div class="col-sm-5">
<label asp-for="priceHighEnd"></label>
<input asp-for="priceHighEnd" />
<span asp-validation-for="priceHighEnd"></span>
</div>
</div>
</div>
<span asp-validation-for="ResData.Name" class="text-danger"></span>
</div>
<div>
<input type="submit" value="View Results" asp-action="GetResultList" class="btn btn-sm" />
</div>
</form>
答案 0 :(得分:1)
点击&#34;下一步&#34>在控制器上找不到您的操作。因为锚点正在提交get
请求,并且控制器配置为仅接受[HttpPost]
个请求。
修改
基本上,我更新您的表单以使用get
1.更新表单以使用method="get"
2.从控制器操作中删除[HttpPost]
和[ValidateAntiForgeryToken]
3.更新Next
和Previous
链接以获取具有已修改页码的当前查询字符串(如下所示)
@{
var existing = Url.ActionContext.HttpContext.Request.Query.ToDictionary(f => f.Key, f => f.Value.ToString());
existing["sortOrder"] = ViewData["CurrentSort"]?.ToString();
existing["currentFilter"] = ViewData["CurrentFilter"]?.ToString();
var previousData = new Dictionary<string, string>(existing);
previousData["page"] = $"{Model.Results.PageIndex - 1}";
var nextData = new Dictionary<string, string>(existing);
nextData["page"] = $"{Model.Results.PageIndex + 1}";
}
<a asp-all-route-data="previousData" asp-action="GetResultList">Previous</a>
<a asp-all-route-data="nextData" asp-action="GetResultList">Next</a>
答案 1 :(得分:1)
让我重新说明我在评论中说的话:
通常对于分页,您应该只有一个控制器操作来拥有参数
page=1
,就像您拥有的那样,以指示当前页面选择。在分页项链接上,您可以获取当前网址,只需将page
参数更改为currentPage +/- 1
,currentPage +/- 2
等。
所以我会像下面这样做分页:
Pager.cs
来表示逻辑using System;
namespace DL.SO.Framework.Mvc.Pagination
{
public class Pager
{
// Here I hard code the page size but you can set it as one of
// the parameters of its constructor
private const int PAGESIZE = 12;
public int TotalItems { get; private set; }
public int CurrentPage { get; private set; }
public int TotalPages { get; private set; }
public int StartPage { get; private set; }
public int EndPage { get; private set; }
public int PageSize
{
get { return PAGESIZE; }
}
public int ShowingRangeFromItem
{
get
{
int fromItem = (CurrentPage - 1) * PageSize + 1;
if (fromItem > TotalItems)
{
fromItem = TotalItems;
}
return fromItem;
}
}
public int ShowingRangeToItem
{
get
{
int toItem = CurrentPage * PageSize;
if (toItem > TotalItems)
{
toItem = TotalItems;
}
return toItem;
}
}
// Constructor
public Pager(int totalItems, int currentPage = 1)
{
// Calculate total, start and end pages
var totalPages = (int)Math.Ceiling((decimal)totalItems / (decimal)PageSize);
currentPage = currentPage < 1
? 1
: currentPage;
// I only want to display +/- 2 pagination links
var startPage = currentPage - 2;
var endPage = currentPage + 2;
if (startPage <= 0)
{
endPage = endPage - startPage + 1;
startPage = 1;
}
if (endPage > totalPages)
{
endPage = totalPages;
if (endPage > 5)
{
startPage = endPage - 4;
}
}
TotalItems = totalItems;
CurrentPage = currentPage;
TotalPages = totalPages;
StartPage = startPage;
EndPage = endPage;
}
}
}
_PagerPartial.cshtml
,以构建分页@model DL.SO.Framework.Mvc.Pagination.Pager
@if (Model != null && Model.TotalItems > 0)
{
<div class="list-pager">
<div class="list-pager-info">
<span>
Showing
<strong>@Model.ShowingRangeFromItem-@Model.ShowingRangeToItem</strong>
of
<strong>@Model.TotalItems</strong>
entries
</span>
</div>
<div class="list-pagination">
<ul class="pagination">
<li class="page-item @(Model.CurrentPage == 1? "disabled" : "")">
<!-- See how I change the page parameter? -->
<a href="@Url.Current(new { page = Model.CurrentPage - 1 })"
class="page-link" tabindex="-1">
< Prev
</a>
</li>
@for (int i = Model.StartPage; i <= Model.EndPage; i++)
{
<li class="page-item @(i == Model.CurrentPage? "active" : "")">
<!-- See how I change the page parameter? -->
<a href="@Url.Current(new { page = i })" class="page-link">@i</a>
</li>
}
<li class="page-item @(Model.CurrentPage >= Model.EndPage? "disabled" : "")">
<!-- See how I change the page parameter? -->
<a href="@Url.Current(new { page = Model.CurrentPage + 1 })"
class="page-link" tabindex="-1">
Next >
</a>
</li>
</ul>
</div>
</div>
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace DL.SO.Framework.Mvc.Extensions
{
public static class UrlHelperExtensions
{
public static string Current(this IUrlHelper url, object routeValues)
{
// Convert new route values to a dictionary
var newRouteData = new RouteValueDictionary(routeValues);
// Get current route data
var currentRouteData = url.ActionContext.RouteData.Values;
// Get current route query string and add them back to the new route
var currentQuery = url.ActionContext.HttpContext.Request.Query;
foreach (var param in currentQuery)
{
currentRouteData[param.Key] = param.Value;
}
// Merge new route data
foreach (var item in newRouteData)
{
currentRouteData[item.Key] = item.Value;
}
return url.RouteUrl(currentRouteData);
}
}
}
我们假设您有一个显示产品列表的ProductController
。通常,您有一个该页面的视图模型,在该视图模型中,您可以拥有一个产品列表,以及代表分页的pager
类。
public class ProductListViewModel
{
public IList<ProductSummaryViewModel> Products { get; set; }
public Pager Pager { get; set; }
}
public class ProductSummaryViewModel
{
public int ProductId { get; set; }
public string Name { get; set; }
}
// I am just making this up. Not production ready
public class ProductController : Controller
{
private readonly AppDbContext _dbContext;
public ProductController(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public IActionResult Index(int page = 1)
{
var vm = new ProductListViewModel
{
Products = new List<ProductSummaryViewModel>()
};
// Get products from database
var products = _dbContext.Products
.AsNoTracking();
// Setup pagination
vm.Pager = new Pager(products.Count(), page);
var pagedProducts = products
.Skip((vm.Pager.CurrentPage - 1) * vm.Pager.PageSize)
.Take(vm.Pager.PageSize)
.ToList();
foreach (var product in pagedProducts)
{
vm.Products.Add(new ProductSummaryViewModel
{
ProductId = product.Id,
Name = product.Name
});
}
return View(vm);
}
}
@model ProductListViewModel
@{
ViewData["Title"] = "Products";
}
@foreach (var product in Model.Products)
{
<!-- However you want to display your products -->
}
<!--
-- This is how you generate the pagination. Just render the partial
-- with the pager model
-->
@Html.Partial("_PagerPartial", Model.Pager)