I have the following action methods:
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
dto.Areas = _ph.GetProfiles();
return View(dto);
}
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
return RedirectToAction("DBLookupIndex", dto);
}
The user simple enters valid information into one or more of the textboxes on the webpage, and submits it to the controller by pressing submit. This then calls the Search-action.
By debugging, I have determined, that the function works. It does find what it should, but it is not passed on, when it redirects back to the DBLookupIndex-action.
My question is; What am I doing wrong? I have seen code examples similar to the one above provided as solutions for similar issues, but for some reason it does not work for me.
EDIT:
I realised after the first answer came, that I was missing some information. Whenever the page is loaded, it has to update a dropdown that is rendered in the view, in case new profiles/areas have been added. I do that with razor:
<select name="AreaId" asp-for="AreaId" class="form-control">
<option disabled selected value=""> -- Vælg et område -- </option>
#foreach (var a in Model.Areas)
{
<option value="#a.ProfileId">#a.Name</option>
}
That is why I have to used RedirectToAction, instead of having a new action render the same view. Unless there is a better way to do it? :)
Thank in advance for any help!
In addition to Peter B's answer, another option is to store it in the TempData object that exists on your base controller.
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
var orders = new List<Order>();
TempData["orders"] = orders;
return RedirectToAction("DBLookupIndex", dto);
}
You can then retrieve the data on the following request like so:
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
var yourData = (List<Order>)TempData["orders"];
...
return View(dto);
}
The TempData object exists for a single request and is then cleared up. You can read more about it here.
The object parameter in RedirectToAction is meant to set querystring values for the URL that is generated and then sent to the browser as a "Redirect to other page" result. It is supposed to be an object similar to new { id = 7, otherParam = 5 }, or a Dictionary, but certainly not a recordset or any other kind of business data.
It seems like you want to show the View that belongs to the DBLookupIndex action. That can be done in a very simple way, like this:
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
return View("DBLookupIndex", dto); // Render the "DBLookupIndex" view and pass it the dto object
}
Update: if you need dto.Areas to be always set, you can create a method that just does that.
Like this (1):
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
SetAreas(dto);
return View(dto);
}
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
SetAreas(dto);
return View("DBLookupIndex", dto);
}
private void SetAreas(DBLookupDTO dto)
{
dto.Areas = _ph.GetProfiles();
}
Or like this (2):
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
return SetAreasAndView(dto);
}
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
return SetAreasAndView(dto);
}
private ActionResult SetAreasAndView(DBLookupDTO dto)
{
dto.Areas = _ph.GetProfiles();
return View("DBLookupIndex", dto);
}
Related
I have the following Get action:
[HttpGet]
[AllowAnonymous, OnlyAnonymous]
public ActionResult VerifyVoucherCode()
{
var model = new VerifyVoucherCodeModel();
model.VoucherCode = Request.GetFirstQueryValue("token", "voucherCode");
if (!string.IsNullOrEmpty(model.VoucherCode))
{
// prepopulates the code for the user already in a form
}
return View(model);
}
And a POST action for when the user submits the form:
[HttpPost, AjaxOnly]
[AllowAnonymous, OnlyAnonymous]
[ValidateAntiForgeryToken]
public ActionResult VerifyVoucherCode(VerifyVoucherCode model)
{
return ExecuteAction(() =>
{
// do something
});
}
I want to change the logic so that if the voucher code is in the request, the form is submitted automatically for the user without them having to submit the form themselves (i.e. it takes them straight to that Post action). I've looked at lots of Stack Overflow questions and answers and it seems I cannot redirect to the Post action. I have also tried creating a second Get action which calls the Post action but that does not work either. Does anyone know the correct way to approach this problem?
Thanks
Assuming the model contains a single string for the voucher, you can do something like this:
[HttpGet]
[AllowAnonymous, OnlyAnonymous]
public ActionResult VerifyVoucherCode(string id)
{
if(string.IsNullOrEmpty(id))
{
MyModel model1 = new MyModel();
...
return View(model1);
}
//otherwise process the voucher here
...
return RedirectToAction("SuccessMsg");
}
I have an ASP.net application with a [HttpGet] for a view an ActionResult named 'Create' on a Controller called Students
My 'Student' is then posted to the following controller.
[HttpPost]
public async Task<ActionResult> Create(Student student)
{
Student.Add(student);
if (ModelState.IsValid)
{
var result = db.Students.Add(student);
await db.SaveChangesAsync();
return Details(result);
}
return View(new CreateStudent());
}
return Details(result); is the line I'm interested in.
Previously I had RedirectToAction where I passed in the result. Id property and used a GET request to query a database.
But, I'm not wanting to have a URL like 'students/details/id=123' Or 'students/details/123' I want to Post my model to the controller 'students/details'
var result is a single 'Student'and my 'Details' ActionResult looks like this:
[HttpPost]
public async Task<ActionResult> Details(Student student)
{
//
}
but return Details(result); doesn't work, I receive an error message stating:
'The model item passed into the dictionary is of type 'LinkApp.Models.Student', but this dictionary requires a model item of type 'LinkApp.Models.DTOs.CreateStudent'.'
But I believe this is because my URL never goes to '/Students/Details', my URL still shows '/Students/Create'
Any help is appreciated. And again, if it's not clear, please ask for any clarity
Thanks
EDIT DUE TO ME NOT BEING VERY CLEAR ;)
So I'm a little bit confused with what you're trying to achieve.
Here is what I've gathered: You want to POST the data. Right now you have a GET method. You can think of a GET method like something that retrieves, and a POST as something that gives.
So your GET method should not be trying to POST anything.
Your GET method should looks like it should be a POST since you're trying to "give" the data to something, not get the data.
[HttpPost]
public async Task<ActionResult> Create(Student student)
{
Student.Add(student);
if (ModelState.IsValid)
{
var result = db.Students.Add(student);
await db.SaveChangesAsync();
return View("Details", result);
}
return View(); //You should add a property to the model called ErrorMessage or something like that, then you could do student.ErrorMessage = "Model state was not valid";, then you could do return View(student); and in the view you could do something like #if (Model.ErrorMessage != null) { #Html.DisplayFor(m=>m.ErrorMessage); }
}
This is what your post should look like, you're saving the Student model to the database and returning to a view.
Now your GET should just be something like this:
[HttpGet]
public ActionResult Create()
{
return View();
}
Edit:
If you wanted your post to do the error message I left in the comment above you would just do this right above the second return View();
student.ErrorMessage = "Model state was not valid;" //Or whatever you want to say
return View(student);
Then in your view you would have something like this:
#if (student.ErrorMessage != null)
{
#Html.DisplayFor(m => m.ErrorMessage);
}
I am using MVC 4, and I have the following:
[HttpGet]
public ActionResult SomeForm(modelType model = null)
{
if(model != null)
return View(model);
return View(getModelFromSomewhere());
}
[HttpPost]
public ActionResult SomeForm(modelType model)
{
if(isValid())
doSomething();
else
return SomeForm(model) // Line in Question
}
However, obviously, I am getting an ambiguous method error on "Line in Question". I'm wondering if anyone has an elegant solution to be able to specify to return specifically the [Get] method of the same name?
Thank you!
You can't have methods with the same signature as you've pointed out already. In C# it also means you can't distinguish functions by just return type - so you must use different names if parameters are same (again default values are ignored when matching of signatures).
If you want separate GET and POST handler - use different names of methods and ActionNameAttribute to name the action:
[HttpGet]
[AciontName("SomeForm")]
public ActionResult SomeFormGet(modelType model = null) ...
[HttpPost]
[AciontName("SomeForm")]
public ActionResult SomeFormPost(modelType model) ...
make it compile...
[HttpPost]
public ActionResult SomeForm(modelType model, FormCollection fc)
{
if(isValid())
doSomething();
else
return SomeForm(model) // Line in Question
}
If you are using http get method you are waiting that browser will send you serialized model as a string query. For example, you are waiting url like
http://example.com?name=Andrew&type=Worker&field1=param1&field2=param2&....
It is common practice to use only id in your get method, so you can do it like this:
[HttpGet]
public ActionResult SomeForm(int id)
{
var model = FindModelById(id);
if(model != null)
return View(model);
return View(getModelFromSomewhere());
}
If you are looking for an elegant solution, it will be more elegant in architecture
I have two problems;
First, I can not go to the main page URL. It is not working as it is on the login page:
public ActionResult Index()
{
return View();
}
public ActionResult Login(string email, string password)
{
model = BLcontext.GetUserLogin(email, password);
if (model.UserID > 0)
{
return Redirect("/Home/Index");
or return PartialView("Index");
or return RedirectToAction("Index");
}
return PartialView("Login");
}
Second, how can I get userId value?
clientAuth "c495600a-71b3-44cb-a577-634426597c82,{\"UserID\":2,\"CurrentSessionID\":\"00000000-0000-0000-0000-000000000000\"},16.01.2015 15:50:53"
I did it
clientAuth.Split(',')[1]
but I see it.. I want value 2
return :"{\"UserID\":2"
I'm not entirely sure what or where your problem is based on what you've submitted. However, it should be as easy as this:
Home Controller: (Default Defined in your Routes)
public ActionResult Index()
{
return View();
}
By default that is a Get, which will automatically be returned when you call the raw controller.
On your other page, when you have an Event that occurs from a user interaction, you simply declare the method and direct to proper action like:
[HttpPost]
public ActionResult ValidateCustomer(int? id)
{
if(id != null)
return View("/SomeOtherController/Example");
return View("/Home/Index");
}
That would take the <form action="/Home/ValidateCustomer" method="POST"> and automatically force the submit input to pass your parameter in your fields, then go to the ActionResult ValidateCustomer hopefully this clarifies navigation a bit for you.
I can't get my Index() action to Pass a valid model to my Review() Action
... ActionResult Index()...
else
{
return RedirectToAction("Review", wizard); <--wizard is a valid object here....
}
ActionResult Review()
public ActionResult Review()
{
return View(_wizard); <-- THis is always null.
}
Update:
Here is my whole controller. I want to take the user from the wizard index, to a review page, then finally to a transmit page that actually saves the data. I'm having real problems wrapping my head around the final piece. When you are used to asp classic where you have to explicitly write everything from scratch, it's kind of hard to get used to the Magic inherit in MVC3. So, I bet I'm writing a lot of uneeded code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using mvc3test.Models;
using Microsoft.Web.Mvc;
using System.Web.Mvc;
using mvc3test.Services;
namespace mvc3test.Controllers
{
public class WizardController : Controller
{
private WizardViewModel wizard = new WizardViewModel();
private DR405DBContext db;
public WizardController(IDBContext dbContext)
{
db = (DR405DBContext)dbContext;
}
public WizardController()
{
db = new DR405DBContext();
}
public ActionResult Index()
{
wizard.Initialize();
return View(wizard);
}
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard, IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Review", wizard);
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
[AllowAnonymous]
public ActionResult Review(WizardViewModel model)
{
return View(model);
}
[AllowAnonymous]
[HttpGet]
public ActionResult Review(Int32 ID)
{
var service = new DR405Service(db);
var myWizard = service.WireUpDataModelToViewModel(service.DBContext.dr405s.Single(p => p.ID == ID));
return View(myWizard);
}
public ActionResult Transmit()
{
var service = new DR405Service(db);
service.Wizard = wizard;
service.Save();
return View();
}
}
}
Per msdn RedirectToAction will cause another get request to the Review action.
Returns an HTTP 302 response to the
browser, which causes the browser to
make a GET request to the specified
action.
This causes the wizard object to loose its value and needs to be repopulated.
View() simply returns the view associated with that action within the current context.
You could either place wizard in TempData, return View("Review", wizard), or have wizard passed as route values if possible.
RedirectToAction returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. So you can't pass a complex object as you do
It's not the best solution but try to put wizard object before redirecting in ViewData :
ViewData["wizard"] = wizard
and then get it in Review()
var wizard = (Wizard)ViewData["wizard"];
return RedirectToAction("Review", wizard); passed the wizard object to the view named Review. Review will need to be a strongly typed view based on the same class as wizard.
Posting your view code would be helpful if this does not answer your question.