Hello fellow programmers!
I am currently stuck in a school assignment. I am working with ASP.NET, MVC2 (I know, it's old :( ) Here is the situation: I have a controller who will decide with an if statement what view to return. For example:
Here is a method within my controller:
public ActionResult Verification()
{
String lastName = Request["lastName"];
String name = Request["name"];
String dob = Request["dob"];
String phone = Request["phone"];
String selection = Request["selection"];
Verification verifying = new Verification(lastName, name, dob, phone, selection);
String other = verifying.returnIfGood();
if (!(other == ""))
{
return RedirectToAction("probleme1");
}
else
{
return RedirectToAction("probleme2");
}
}
RedirectToAction seem to point directly at a view, but I also need to pass the view a string to work with. I also noticed if I call a method within my controller, no view gets returned, which I am a little confused about. I am trying to stay away from ajax and jquery since I haven't seen those yet. Is there any way to do this using only c#?
Here you need to ajax call with query string like
Controller/action?viewname='abc'
Then
Get on actionresult method
Then redirect to action on the basis of viewName.
Or
Redirect(controller/actionName)
Your question is quite confusing as to what you're trying to accomplish, and some of the comments seem to indicate a misunderstanding of what is actually going on. Given that, I'm going to try to address your question but may be missing the mark.
RedirectToAction does not "point directly at a view". It causes an HTTP 302 response to be sent to the browser with a new URL which the browser then turns around and requests with a new GET operation.
Given that you haven't provided the code for the probleme1 and probleme2 action methods I have no idea what other logic might live there. If all they do is return a view you could rewrite your Verification method to return the view instead and include the "string to work with" as a model.
public ActionResult Verification()
{
// code omitted for brevity...
if (!(other == ""))
{
return View("probleme1", "some string");
}
else
{
return View("probleme2", "some other string");
}
}
If the logic in probleme1 and probleme2 is more complex and you really need to preserve the RedirectToAction approach you could set a value in TempData in your Verification method and then pass that value along in the other methods.
public ActionResult Verification()
{
// code omitted for brevity...
if (!(other == ""))
{
TempData.ModelString = "some string";
return RedirectToAction("probleme1");
}
else
{
TempData.ModelString = "some other string";
return RedirectToAction("probleme2");
}
}
public ActionResult probleme1()
{
return View(<view name>, TempData.ModelString);
}
In this case you need to use TempData and not ViewData as the former will survive the redirect while the latter will not. Put another way, TempData is for holding information between controller actions during a redirect, ViewData is for sending information from the controller to the view.
Related
I have this simple API controller in a NetCore 2.2 web game that is supposed to return a list of monsters based on the dungeonID(Guid).
So I use the URL, passing in the guid of the dungeonID, to that controller like this:
https://localhost:44361/MonsterList/GetMonsters/2f14c8gf-2e7e-466a-bcbg-f4440e92b3dg
But when I step through the code, I just see all zeroes for the dungeonID:
public async Task<JsonResult> GetMonsters(Guid dungeonID)
{
var monsters = await _context.MonsterList.Where(c => c.DungeonID == (dungeonID)).ToListAsync();
return Json(monsters);
}
This returns nothing because, for reasons I don't know, dungeonID is always all zeroes.
But this does work if I hard-code in the dungeonID:
https://localhost:44361/MonsterList/GetMonsters
public async Task<JsonResult> GetMonsters()
{
var monsters = await _context.MonsterList.Where(c => c.DungeonID == Guid.Parse("2f14c8gf-2e7e-466a-bcbg-f4440e92b3dg")).ToListAsync();
return Json(monsters);
}
I've seen lots of posts similiar to mine, like these:
asp.net webapi 2 post parameter is always null
Post parameter is always null
But nothing seems to work.
How do I add the ability to pass in a Guid parameter?
Thanks!
One point is 2f14c8gf-2e7e-466a-bcbg-f4440e92b3dg is not a GUID/UUID . Just try with a correct GUID :
https://localhost:44384/api/values/GetMonsters/2e6ae748-10c2-4e23-84c3-9d3db7c09631
It seems that in ASP.NET Core, the value in asp-* attributes (e.g. asp-for) is taken from the request payload before the model. Example:
Post this value:
MyProperty="User entered value."
To this action:
[HttpPost]
public IActionResult Foo(MyModel m)
{
m.MyProperty = "Change it to this!";
return View();
}
OR this action
[HttpPost]
public IActionResult Foo(MyModel m)
{
m.MyProperty = "Change it to this!";
return View(m);
}
View renders this:
<input asp-for="MyProperty" />
The value in the form input is User entered value. and not Change it to this!.
First of all, I'm surprised that we don't need to pass the model to the view and it works. Secondly, I'm shocked that the request payload takes precedence over the model that's passed into the view. Anyone know what the rationale is for this design decision? Is there a way to override the user entered value when using asp-for attributes?
I believe this is the expected behavior/by design. Because when you submit the form, the form data will be stored to ModelState dictionary and when razor renders your form elements, it will use the values from the Model state dictionary. That is why you are seeing your form element values even when you are not passing an object of your view model to the View() method.
If you want to update the input values, you need to explcitly clear the Model state dictionary. You can use ModelState.Clear() method to do so.
[HttpPost]
public IActionResult Create(YourviewModel model)
{
ModelState.Clear();
model.YourProperty = "New Value";
return View(model);
}
The reason it uses Model state dictionary to render the form element values is to support use cases like, showing the previously submitted values in the form when there is a validation error occurs.
EDIT : I found a link to the official github repo of aspnet mvc where this is confirmed by Eilon (asp.net team member)
https://github.com/aspnet/Mvc/issues/4486#issuecomment-210603605
I can confirm your observation. What's really going to blow your mind is that this:
[HttpPost]
public IActionResult Foo (MyModel m)
{
m.MyProperty = "changed";
var result = new MyModel { MyProperty = "changed" };
return View(result);
}
...gives you the same result.
I think you should log a bug: https://github.com/aspnet/mvc/issues
Edit: I now remember this issue from previous encounters myself and concede that it isn't necessarily a bug, but rather an unintended consequence. The reasons for the result of executing this code is not obvious. There likely isn't a non-trivial way to surface a warning about this, but PRG is a good pattern to follow.
I'm new to Spring MVC and trying to get a Post/Redirect/Get pattern working. We're trying to implement a survey where each page can display a variable number of questions. The way I'd like to implement this is a GET handler that prepares the next survey page and then hands that off to the view. In the same Controller, have a Post handler that processes the form's answers to the survey questions, submits that to the survey service, which returns the next page of questions, and then redirects that next surveyPage to the getNextPage GET handler.
Most of it is working, except the problem is that I don't know how to hand that 'next survey page' object from the POST handler to the getNextPage GET handler in the redirect. The redirect is working; it goes from the POST method to the GET method, but the surveyPage ModelAttribute is a new object in the GET method, and not the one that was set at the end of the POST method. As you can see, I've tried using ModelAttribute, but it doesn't work. I also tried using #SessionAttributes above the class, but then got a HttpSessionRequiredException.
We didn't know how to handle the dynamic form containing a variable # of questions with Spring MVC Forms, so we just did straight JSTL. It's funky but it works. That funkiness is what resulted in using the #RequestBody and SurveyPageBean coming back with the Post. Honestly, I don't know how the SurveyPageBean is populated. It looks like some Spring MVC magic, but it's working so I'm leaving it alone for now (another developer did this and then I picked it up, and we're both new to Spring MVC). Please don't get distracted by the unusual form handling, unless that is part of the problem with the empty surveyPage ModelAttribute not being redirected.
Here's the Controller snippet:
#Controller
#RequestMapping("/surveyPage")
public class SurveyPageController{
#RequestMapping(method=RequestMethod.GET)
public String getNextPage(#ModelAttribute("surveyPage") SurveyPage surveyPage, Model model) {
if(surveyPage.getPageId() == null) {
// call to surveyService (defined elsewhere) to start Survey and get first page
surveyPage = surveyService.startSurvey("New Survey");
}
model.addAttribute("surveyPage", surveyPage);
return "surveyPage";
}
#RequestMapping(method=RequestMethod.POST)
public String processSubmit(#RequestBody String body, SurveyPageBean submitQuestionBean, Model model, #ModelAttribute("surveyPage") SurveyPage surveyPage) {
// process form results, package them up and send to service, which
// returns the next page, if any
surveyPage = surveyService.submitPage(SurveyPageWithAnswers);
if (results.getPageId() == null) {
// The survey is done
surveyPage = surveyService.quitSurvey(surveyId);
return "redirect:done";
}
model.addAttribute("surveyPage ", surveyPage );
return "redirect:surveyPage";
}
Use Flash Attributes as shown in Warlock's Thoughts.
#RequestMapping(method = RequestMethod.POST)
public String handleFormSubmission(..., final RedirectAttributes redirectAttrs) {
...
redirectAttrs.addFlashAttribute("AttributeName", value);
return "redirect:to_some_url_handled_by_BController";
}
Your GET takes the surveyPage as a model attribute, which means it is reading it from the URL. In the POST, rather than adding the surveyPage to the model (which is lost because you are telling the client to redirect, which creates a new request and therefore a new model) you should add the surveyPage as a query parameter in your "redirect:surveyPage" You'll have to look at how the surveyPage is constructed from the query params in order to know what to put on the query string.
If, for instance, the surveyPage is constructed from a user, page number, and question count or something, I believe you could do something like "redirect:surveyPage?userId=1234&pageNumber=5678&questionCount=12 in order to pass that model attribute along.
I am new to MVC, so hopefully my question will be straight forward. I am thinking of a scenario where the user submits a form (that is a partial view) and it undergoes server validation. I am wondering how I will know the result of the validation on the client side (javascript) after the form is submitted. For example, if validation fails I will obviously want to return the partial view again with validation messages set, but if it passes validation I may not necessarily want to return the partial view. I may want to return a json object with a message or hide a div or something. I want to be able to determine the validation result on the client. Is something like that possible? Or can I approach this a different way?
The tricky part with AJAX is that the client and server both have to agree on what's supposed to come back from the server in any circumstance. You have a few options:
Your server will always return HTML, and jQuery will always replace the editor content with the HTML that comes back. If the model is invalid, you return a PartialView result. If the model is valid, you return a <script> tag that tells the page what it needs to do (e.g. close a dialog, redirect to a different page, whatever). jQuery will automatically run any script it finds in the results when it tries to insert them into the DOM.
Your server will always return a JSON object representing what happened. In this scenario, your client-side javascript code has to be complex enough to take the results and modify your page to match. Under normal circumstances, this will mean that you don't get to take advantage of MVC's validation features.
Same as 2, except that you use a custom utility method to render the partial view you want into a string, and you make that entire string part of the JSON that comes back. The javascript code then just has to be smart enough to check whether the JSON shows a valid or invalid result, and if the result is valid, replace the contents of your editor area with the partialview HTML that is returned as part of the JSON object you got back.
Same as 3, except you develop an event-based architecture where all your AJAX requests will always expect to get back a JSON object with one or more "events" in it. The AJAX code can then be consolidated into one method that hands the events off to an Event Bus. The event bus then passes the event information into callbacks that have "subscribed" to these events. That way, depending on what kind of events you return from the server, you can have different actions occur on the client side. This strategy requires a lot more up-front work to put in place, but once it's done you can have a lot more flexibility, and the client-side code becomes vastly more maintainable.
Partial views would not have a Layout page. You may use this code to check if the view is rendered as partial view.
#if (String.IsNullOrWhiteSpace(Layout))
{
// Do somthing if it is partial view
}
else
{
// Do somthing if it is full page view
}
If you are using the MVC Data Annotations for validating your Model, then the controller will have a property called ModelState (typeof(ModelStateDictionary) which as a property of IsValid to determine if your Model passed into the Controller/Action is valid.
With IsValid you can return a Json object that can tell your Javascript what to do next.
Update
Here is a really basic example (USES jQuery):
[SomeController.cs]
public class SomeController : Controller
{
public ActionResult ShowForm()
{
return View();
}
public ActionResult ValidateForm(MyFormModel FormModel)
{
FormValidationResults result = new FormValidationResults();
result.IsValid = ModelState.IsValid;
if (result.IsValid)
{
result.RedirectToUrl = "/Controller/Action";
}
this.Json(result);
}
}
[FormValidationResult.cs]
public class FormValidationResults
{
public bool IsValid { get; set; }
public string RedirectToUrl { get; set; }
}
[View.js]
$(document).ready(function()
{
$("#button").click(function()
{
var form = $("#myForm");
$.ajax(
{
url: '/Some/ValidateForm',
type: 'POST',
data: form.serialize(),
success: function(jsonResult)
{
if (jsonResult.IsValid)
{
window.location = jsonResult.RedirectToUrl;
}
}
});
});
});
The populated catList is always Count=0 when the code jumps to CreateProduct() so I take it it does not get delivered.
Considering RouteValueDictionary does not do this ? Any other way?
public ActionResult GetCats(int CatID)
{
List<Category> catList = new List<Category>();
if (CatID >= 0 )
{
catList = context.Categories.Where(x => x.PCATID == CatID).ToList();
}
return RedirectToAction("CreateProduct", "Admin", new { catList });
}
public ActionResult CreateProduct(List<Category> catList) { }
You are actually trying to use controllers to do data access.
Move the "GetCats" data retrieval into your business layer (Service object, Repository, whatever suits you).
Then, CreateProduct will need to be there twice (2 signatures). One with no parameters in which you are going to call "GetCats" from your business layer and send it to the view.
The other implementation is going to be the flagged with the HttpPostAttribute and will contain in parameters all the necessary information to create a cat.
That's all. Simple and easy.
You could place any items that you need in TempData then call RedirectToAction.
RedirectToAction simply returns a "302" code to the browser with the URL to redirect to. When this happens your browser performs a GET with that URL.
Your RouteValues need to be simple. You can't really pass complex object or collections using a route value.
if you don't care about the browser url changing you could just
return CreateProduct(catList)