Asp.net Core调用TempData.Keep()返回HTTP 500错误问题咨询
问题根源
你遇到的两个问题核心都是Asp.net Core中TempData的特性限制:
- 生命周期特性:
TempData存储的内容默认被读取一次后就会被标记为删除,你在GET方法存入的TempData["cities"]在视图渲染下拉框时已经被读取,所以POST请求拿到的就是null,触发空引用异常。 - 存储类型限制:默认的Cookie型TempData提供程序只支持字符串、数值等基础类型,无法直接存储自定义类、集合这类复杂类型,你直接存
List<City>的话,运行时序列化失败就会抛出500错误,调用Keep()时触发持久化逻辑自然也会报错。
解决方案1:兼容TempData的修改方案(按现有逻辑调整)
步骤1:序列化复杂类型存储
存取TempData时都通过JSON序列化/反序列化处理,首先确保你的City类是普通POCO类(有无参构造函数、属性都是公开可读写)。
修改GET方法CitiesOnMap:
public IActionResult CitiesOnMap() { List<City> cities = new List<City> { new City { CityName = "Hatay", Latitude = 123, Longtitude = 435 }, new City { CityName = "Konya", Latitude = 123, Longtitude = 435 }, // 其余城市初始化逻辑保持不变 }; // 序列化为字符串再存TempData TempData["cities"] = System.Text.Json.JsonSerializer.Serialize(cities); return View(); }
步骤2:视图读取后保留TempData
修改CitiesOnMap.cshtml的下拉框渲染逻辑,读完TempData后调用Keep()保留内容供POST请求使用:
@{ var cities = new List<MVC.Models.City>(); if (TempData["cities"] != null) { cities = System.Text.Json.JsonSerializer.Deserialize<List<MVC.Models.City>>(TempData["cities"].ToString()); // 标记保留该键值,不被删除 TempData.Keep("cities"); } } <!-- 下拉框部分 --> <select id="drpEmpList" name="SelectedCity" class="form-control"> <option disabled selected hidden>Choose a City </option> @foreach (var city in cities) { <option>@city.CityName</option> } </select>
步骤3:POST方法反序列化读取
修改POST版CitiesOnMap方法,先反序列化再使用:
[HttpPost] public IActionResult CitiesOnMap(string SelectedCity) { if (TempData["cities"] != null) { var cities = System.Text.Json.JsonSerializer.Deserialize<List<City>>(TempData["cities"].ToString()); foreach (var redirectedCity in cities) { if (string.Equals(redirectedCity.CityName, SelectedCity)) { // 选中的城市也序列化后存TempData TempData["selectedCity"] = System.Text.Json.JsonSerializer.Serialize(redirectedCity); return RedirectToAction(redirectedCity.CityName, "City"); } } } return RedirectToAction(SelectedCity, "City"); }
解决方案2:更合理的优化方案(推荐)
你的场景完全不需要在TempData里存整个城市列表:城市列表是固定的,你可以把城市列表抽成控制器的静态只读字段,GET方法直接传视图,POST方法直接用静态列表查询即可,完全规避TempData的特性问题,性能也更好:
public class CityController : Controller { private readonly ILogger<CityController> _logger; // 抽成静态只读的城市列表,所有请求共用 private static readonly List<City> _cities = new List<City> { new City { CityName = "Hatay", Latitude = 123, Longtitude = 435 }, new City { CityName = "Konya", Latitude = 123, Longtitude = 435 }, // 其余城市初始化逻辑保持不变 }; public CityController(ILogger<CityController> logger) { _logger = logger; } public IActionResult CitiesOnMap() { // 直接用View传列表给视图,不需要TempData return View(_cities); } [HttpPost] public IActionResult CitiesOnMap(string SelectedCity) { var targetCity = _cities.FirstOrDefault(c => string.Equals(c.CityName, SelectedCity)); if(targetCity != null) { TempData["selectedCity"] = System.Text.Json.JsonSerializer.Serialize(targetCity); return RedirectToAction(targetCity.CityName, "City"); } return RedirectToAction(SelectedCity, "City"); } // 其余方法保持不变 }
对应视图直接接收模型即可,不需要处理TempData:
@model List<MVC.Models.City> @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ ViewData["Title"] = "CitiesOnMap"; } <!-- 其余页面结构保持不变 --> <select id="drpEmpList" name="SelectedCity" class="form-control"> <option disabled selected hidden>Choose a City </option> @foreach (var city in Model) { <option>@city.CityName</option> } </select>
内容的提问来源于stack exchange,提问作者Mertcan
相关产品推荐
相关产品推荐

