if i start off on a Detail page:
如果我从详细信息页面开始:
http:\\www.mysite.com\App\Detail
i have a controller action called Update which normally will call redirectToAction back to the detail page. but i have an error that is caught in validation and i need to return before redirect (to avoid losing all of my ModelState). Here is my controller code:
我有一个名为Update的控制器操作,通常会将redirectToAction调用回详细信息页面。但我有一个错误,在验证中被捕获,我需要在重定向之前返回(以避免丢失我的所有ModelState)。这是我的控制器代码:
public override ActionResult Update(Application entity)
{
base.Update(entity);
if (!ModelState.IsValid)
{
return View("Detail", GetAppViewModel(entity.Id));
}
return RedirectToAction("Detail", new { id = entity.Id })
but now I see the view with the validation error messages (as i am using HTML.ValidationSummary() ) but the url looks like this:
但现在我看到带有验证错误消息的视图(因为我正在使用HTML.ValidationSummary())但是url看起来像这样:
http:\\www.mysite.com\App\Update
is there anyway i can avoid the URL from changing without some hack of putting modelstate into some temp variables? Is there a best practice here as the only examples i have seen have been putting ModelState in some tempdata between calling redirectToAction.
无论如何,我可以避免更改URL而没有将模型状态放入某些临时变量的黑客攻击?这里有一个最佳实践,因为我见过的唯一例子是在调用redirectToAction之间将ModelState放入一些tempdata中。
4 个解决方案
#1
5
As of ASP.NET MVC 2, there isn't any such API call that maintains the URL of the original action method when return View()
is called from another action method.
从ASP.NET MVC 2开始,没有任何此类API调用在从另一个操作方法调用返回View()时维护原始操作方法的URL。
Therefore as such, the recommended solution and a generally accepted convention in ASP.NET MVC is to have a corresponding, similarly named action method that only accepts a HTTP POST
verb. So in your case, having another action method named Detail
like so should solve your problem of having a different URL when validation fails.
因此,推荐的解决方案和ASP.NET MVC中普遍接受的约定是具有相应的,同样命名的操作方法,该方法仅接受HTTP POST动词。因此,在您的情况下,使用另一个名为Detail的操作方法就可以解决您在验证失败时使用不同URL的问题。
[HttpPost]
public ActionResult Detail(Application entity)
{
base.Update(entity);
if (ModelState.IsValid)
{
//Save the entity here
}
return View("Detail", new { id = entity.Id });
}
This solution is in line with ASP.NET MVC best practices and also avoids having to fiddle around with modestate and tempdate.
此解决方案符合ASP.NET MVC最佳实践,并且还避免了使用modestate和tempdate。
In addition, if you haven't explored this option already then client side validation in asp.net mvc might also provide for some solution with regards to your URL problem. I emphasize some since this approach won't work when javascript is disabled on the browser.
此外,如果您尚未探索此选项,那么asp.net mvc中的客户端验证可能还会针对您的URL问题提供一些解决方案。我强调一些,因为这种方法在浏览器上禁用javascript时不起作用。
So, the best solution would be have an action method named Detail
but accepting only HTTP POST
verb.
因此,最好的解决方案是使用名为Detail的操作方法,但只接受HTTP POST动词。
#2
3
The problem here is actually caused by your implementation. This doesn't answer your question, but it describes where you've gone wrong in the first place.
这里的问题实际上是由您的实现引起的。这不能回答你的问题,但它首先描述了你出错的地方。
If you want a page that is used to update or edit an item, the URL should reflect this. For example.
如果您需要用于更新或编辑项目的页面,则URL应反映此情况。例如。
You visit http:\www.mysite.com\App\Detail and it displays some information about something. That is what the URL describes it is going to do. In your controller, the Detail() method would return the Detail.aspx view.
您访问http:\ www.mysite.com \ App \ Detail,它会显示有关某些内容的信息。这就是URL描述它将要做的事情。在您的控制器中,Detail()方法将返回Detail.aspx视图。
To edit an item, you would visit http:\www.mysite.com\App\Edit and change the information you wish to update, the form would post back to the same URL - you can handle this in the controller with these methods:
要编辑项目,您将访问http:\ www.mysite.com \ App \ Edit并更改您要更新的信息,表单将回发到相同的URL - 您可以使用以下方法在控制器中处理:
[HttpGet]
public ActionResult Edit() {
MyModel model = new MyModel();
...
return View(model);
}
[HttpPost]
public ActionResult Edit(MyModel model) {
...
if (ModelState.IsValid) {
// Save and redirect
...
return RedirectToAction("Detail");
}
return View(model);
}
If you ever find yourself doing this...
如果你发现自己这样做......
return View("SomeView", model);
You are making your own life harder (as well as breaking the principles behind URLs).
你正在努力改变自己的生活(以及打破网址背后的原则)。
If you want to re-use part of a view, make it a partial view and render it inside of the view that is named after the method on the controller.
如果要重新使用视图的一部分,请将其设置为局部视图,并将其呈现在以控制器上的方法命名的视图内。
I apologise that this potentially isn't very helpful, but you are falling into an MVC anti-pattern trap by displaying the same view from a differently named method.
我很抱歉这可能不是很有帮助,但是你通过从不同命名的方法显示相同的视图而陷入MVC反模式陷阱。
#3
2
As @Malcolm sais, best practice is to put ModelState
in TempData
, but don't do it manually! If you'd do this manually in every controller action where it's relevant, you would introduce immense amounts of repeated code, and increase the maintenance cost vastly.
作为@Malcolm sais,最佳做法是将ModelState放在TempData中,但不要手动执行!如果您在每个相关的控制器操作中手动执行此操作,则会引入大量重复代码,并大大增加维护成本。
Instead, implement a couple of attributes that do the job for you. Kazi Manzur has an approach (scroll down to the end of the post) that has been widely spread, and Evan Nagle shows an implementation with tests that is essentially the same as Kazi's, but with different names. Since he also provides unit tests that make sure the attributes work, implementing them in your code will mean little or no maintenance cost. The only thing you'll have to keep track of is that the controller actions are decorated with the appropriate attributes, which can also be tested.
相反,实现一些为您完成工作的属性。 Kazi Manzur有一种广泛传播的方法(向下滚动到帖子的末尾),Evan Nagle展示了一种与Kazi's基本相同但具有不同名称的测试实现。由于他还提供确保属性有效的单元测试,因此在代码中实现它们意味着很少或根本没有维护成本。您必须要跟踪的唯一事情是控制器操作使用适当的属性进行修饰,这些属性也可以进行测试。
When you have the attributes in place, your controller might look something like this (I deliberately simplified, because I don't know the class you inherit from):
如果你有适当的属性,你的控制器可能看起来像这样(我故意简化,因为我不知道你继承的类):
[HttpPost, PassState]
public ActionResult Update(EntityType entity)
{
// Only update if the model is valid
if (ModelState.IsValid) {
base.Update(entity);
}
// Always redirect to Detail view.
// Any errors will be passed along automagically, thanks to the attribute.
return RedirectToAction("Detail", new { id = entity.Id });
}
[HttpGet, GetState]
public ActionResult Detail(int id)
{
// Get stuff from the database and show the view
// The model state, if there is any, will be imported by the attribute.
}
You mention that you feel putting ModelState
in TempData
feels like a "hack" - why? I agree with you that doing it with repeated code in every single controller action seems hacky, but that's not what we're doing here. In fact, this is exactly what TempData
is for. And I don't think the above code looks hacky... do you?
你提到你觉得在TempData中放入ModelState感觉就像是“黑客” - 为什么?我同意你的观点,在每一个控制器动作中重复执行代码看起来很糟糕,但这不是我们在这里做的。实际上,这正是TempData的用途。而且我不认为上面的代码看起来很丑陋...你呢?
Although there are solutions to this problem that might appear simpler, such as just renaming the action method to preserve the URL, I would strongly advise against that approach. It does solve this problem, but introduces a couple of others - for example, you'll still have no protection against double form submission, and you'll have pretty confusing action names (where a call to Detail
actually changes stuff on the server).
虽然这个问题的解决方案可能看起来更简单,例如只是重命名动作方法以保留URL,但我强烈建议不要采用这种方法。它确实解决了这个问题,但介绍了其他几个问题 - 例如,你仍然没有防止双重表单提交的保护,你将会有相当混乱的动作名称(其中对Detail的调用实际上改变了服务器上的内容) 。
#4
1
The best practice you ask for is actually what you explained not to do: putting modelstate into tempdata. Tempdata is meant for it, that's why I would not call it a hack.
你要求的最佳实践实际上是你解释不做的事情:将模型状态放入tempdata。 Tempdata意味着它,这就是我不称之为黑客的原因。
If this is to much repetitive code you could use the attribute modeldatatotempdata of MVCContrib. But the store is still TempData.
如果这是重复的代码,您可以使用MVCContrib的属性modeldatatotempdata。但商店仍然是TempData。
#1
5
As of ASP.NET MVC 2, there isn't any such API call that maintains the URL of the original action method when return View()
is called from another action method.
从ASP.NET MVC 2开始,没有任何此类API调用在从另一个操作方法调用返回View()时维护原始操作方法的URL。
Therefore as such, the recommended solution and a generally accepted convention in ASP.NET MVC is to have a corresponding, similarly named action method that only accepts a HTTP POST
verb. So in your case, having another action method named Detail
like so should solve your problem of having a different URL when validation fails.
因此,推荐的解决方案和ASP.NET MVC中普遍接受的约定是具有相应的,同样命名的操作方法,该方法仅接受HTTP POST动词。因此,在您的情况下,使用另一个名为Detail的操作方法就可以解决您在验证失败时使用不同URL的问题。
[HttpPost]
public ActionResult Detail(Application entity)
{
base.Update(entity);
if (ModelState.IsValid)
{
//Save the entity here
}
return View("Detail", new { id = entity.Id });
}
This solution is in line with ASP.NET MVC best practices and also avoids having to fiddle around with modestate and tempdate.
此解决方案符合ASP.NET MVC最佳实践,并且还避免了使用modestate和tempdate。
In addition, if you haven't explored this option already then client side validation in asp.net mvc might also provide for some solution with regards to your URL problem. I emphasize some since this approach won't work when javascript is disabled on the browser.
此外,如果您尚未探索此选项,那么asp.net mvc中的客户端验证可能还会针对您的URL问题提供一些解决方案。我强调一些,因为这种方法在浏览器上禁用javascript时不起作用。
So, the best solution would be have an action method named Detail
but accepting only HTTP POST
verb.
因此,最好的解决方案是使用名为Detail的操作方法,但只接受HTTP POST动词。
#2
3
The problem here is actually caused by your implementation. This doesn't answer your question, but it describes where you've gone wrong in the first place.
这里的问题实际上是由您的实现引起的。这不能回答你的问题,但它首先描述了你出错的地方。
If you want a page that is used to update or edit an item, the URL should reflect this. For example.
如果您需要用于更新或编辑项目的页面,则URL应反映此情况。例如。
You visit http:\www.mysite.com\App\Detail and it displays some information about something. That is what the URL describes it is going to do. In your controller, the Detail() method would return the Detail.aspx view.
您访问http:\ www.mysite.com \ App \ Detail,它会显示有关某些内容的信息。这就是URL描述它将要做的事情。在您的控制器中,Detail()方法将返回Detail.aspx视图。
To edit an item, you would visit http:\www.mysite.com\App\Edit and change the information you wish to update, the form would post back to the same URL - you can handle this in the controller with these methods:
要编辑项目,您将访问http:\ www.mysite.com \ App \ Edit并更改您要更新的信息,表单将回发到相同的URL - 您可以使用以下方法在控制器中处理:
[HttpGet]
public ActionResult Edit() {
MyModel model = new MyModel();
...
return View(model);
}
[HttpPost]
public ActionResult Edit(MyModel model) {
...
if (ModelState.IsValid) {
// Save and redirect
...
return RedirectToAction("Detail");
}
return View(model);
}
If you ever find yourself doing this...
如果你发现自己这样做......
return View("SomeView", model);
You are making your own life harder (as well as breaking the principles behind URLs).
你正在努力改变自己的生活(以及打破网址背后的原则)。
If you want to re-use part of a view, make it a partial view and render it inside of the view that is named after the method on the controller.
如果要重新使用视图的一部分,请将其设置为局部视图,并将其呈现在以控制器上的方法命名的视图内。
I apologise that this potentially isn't very helpful, but you are falling into an MVC anti-pattern trap by displaying the same view from a differently named method.
我很抱歉这可能不是很有帮助,但是你通过从不同命名的方法显示相同的视图而陷入MVC反模式陷阱。
#3
2
As @Malcolm sais, best practice is to put ModelState
in TempData
, but don't do it manually! If you'd do this manually in every controller action where it's relevant, you would introduce immense amounts of repeated code, and increase the maintenance cost vastly.
作为@Malcolm sais,最佳做法是将ModelState放在TempData中,但不要手动执行!如果您在每个相关的控制器操作中手动执行此操作,则会引入大量重复代码,并大大增加维护成本。
Instead, implement a couple of attributes that do the job for you. Kazi Manzur has an approach (scroll down to the end of the post) that has been widely spread, and Evan Nagle shows an implementation with tests that is essentially the same as Kazi's, but with different names. Since he also provides unit tests that make sure the attributes work, implementing them in your code will mean little or no maintenance cost. The only thing you'll have to keep track of is that the controller actions are decorated with the appropriate attributes, which can also be tested.
相反,实现一些为您完成工作的属性。 Kazi Manzur有一种广泛传播的方法(向下滚动到帖子的末尾),Evan Nagle展示了一种与Kazi's基本相同但具有不同名称的测试实现。由于他还提供确保属性有效的单元测试,因此在代码中实现它们意味着很少或根本没有维护成本。您必须要跟踪的唯一事情是控制器操作使用适当的属性进行修饰,这些属性也可以进行测试。
When you have the attributes in place, your controller might look something like this (I deliberately simplified, because I don't know the class you inherit from):
如果你有适当的属性,你的控制器可能看起来像这样(我故意简化,因为我不知道你继承的类):
[HttpPost, PassState]
public ActionResult Update(EntityType entity)
{
// Only update if the model is valid
if (ModelState.IsValid) {
base.Update(entity);
}
// Always redirect to Detail view.
// Any errors will be passed along automagically, thanks to the attribute.
return RedirectToAction("Detail", new { id = entity.Id });
}
[HttpGet, GetState]
public ActionResult Detail(int id)
{
// Get stuff from the database and show the view
// The model state, if there is any, will be imported by the attribute.
}
You mention that you feel putting ModelState
in TempData
feels like a "hack" - why? I agree with you that doing it with repeated code in every single controller action seems hacky, but that's not what we're doing here. In fact, this is exactly what TempData
is for. And I don't think the above code looks hacky... do you?
你提到你觉得在TempData中放入ModelState感觉就像是“黑客” - 为什么?我同意你的观点,在每一个控制器动作中重复执行代码看起来很糟糕,但这不是我们在这里做的。实际上,这正是TempData的用途。而且我不认为上面的代码看起来很丑陋...你呢?
Although there are solutions to this problem that might appear simpler, such as just renaming the action method to preserve the URL, I would strongly advise against that approach. It does solve this problem, but introduces a couple of others - for example, you'll still have no protection against double form submission, and you'll have pretty confusing action names (where a call to Detail
actually changes stuff on the server).
虽然这个问题的解决方案可能看起来更简单,例如只是重命名动作方法以保留URL,但我强烈建议不要采用这种方法。它确实解决了这个问题,但介绍了其他几个问题 - 例如,你仍然没有防止双重表单提交的保护,你将会有相当混乱的动作名称(其中对Detail的调用实际上改变了服务器上的内容) 。
#4
1
The best practice you ask for is actually what you explained not to do: putting modelstate into tempdata. Tempdata is meant for it, that's why I would not call it a hack.
你要求的最佳实践实际上是你解释不做的事情:将模型状态放入tempdata。 Tempdata意味着它,这就是我不称之为黑客的原因。
If this is to much repetitive code you could use the attribute modeldatatotempdata of MVCContrib. But the store is still TempData.
如果这是重复的代码,您可以使用MVCContrib的属性modeldatatotempdata。但商店仍然是TempData。