MVC Redirect from void-POST Action - c#

My current action looks like this:
[HttpPost]
public void AddMessage([FromBody] ShoutboxMessage input)
{
if (!string.IsNullOrWhiteSpace(input.Message) && Request.Cookies["usrid"] != null)
{
input.SbUserId = int.Parse(Request.Cookies["usrid"]);
input.Timestamp = DateTime.UtcNow;
context.ShoutboxMessages.Add(input);
context.SaveChanges();
}
}
I would like to just do this:
[HttpPost]
public void AddMessage([FromBody] ShoutboxMessage input)
{
if (Request.Cookies["usrid"] == null)
RedirectToAction("Login");
if (!string.IsNullOrWhiteSpace(input.Message))
{
//...
}
}
but that doesn't work, obviously. Is there a way to redirect from an action that's supposed to return void? Or, the other way around, can an Action that's supposed to return an ActionResult not result in any redirection or reload of the current page?
Edit: the "duplicate" has nothing to do with this. it may be that a void action returns basically the same as an EmptyResult action, but that's not the topic here, basically I want to know how to chose between an EmptyResult and an ActionResult at runtime.

Something like this? You can always return EmptyResult() from Action.
[HttpPost]
public ActionResult AddMessage([FromBody] ShoutboxMessage input)
{
if (Request.Cookies["usrid"] == null)
return this.RedirectToAction("Login");
if (!string.IsNullOrWhiteSpace(input.Message))
{
//...
return new EmptyResult();
}
}

Related

MVC Redirect won't work if I call it outside the main function

So, when I click a button - it hits submit. I manage to get the URL value fine. My issue is that hitting "RedirectingTime" function won't redirect the page. It just stops and displays a blank page. If I put it inside the Submit function then it works fine but I don't want to change my submit from a void to a redirect as I would like to also return a view if it errors.
Thanks!
[HttpPost]
public void Submit(URLModel model)
{
string url = EmailEnd(model);
if (url != "0")
{
RedirectingTime(url);
}
else
{
Error();
}
}
public RedirectResult RedirectingTime(string url)
{
return Redirect(url);
}
public ActionResult Error()
{
return View();
}
Based on the answer below - the working code is:
[HttpPost]
public ActionResult Submit(URLModel model)
{
string url = EmailEnd(model);
if (url != "0")
{
return Redirect(url);
}
else
{
return View("Error");
}
}
}
The problem is your Submit action returning void. That will always result in a blank page, because void is essentially the same as EmptyResult.
The action you hit is what returns the result, not some action you call from it. Even though RedirectingTime returns a redirect, your Submit action never returns that, so the result is still EmptyResult and not RedirectResult.
Also, for what it's worth, it's atypical and unnecessary to explicitly set the type of the return value of the action. Virtually every MVC action signature should simply have ActionResult as the return. You can actually return anything with that, RedirectResult, ViewResult, JsonResult, etc.
The crux of your problem is that your Submit action is not returning anything. It has to return an ActionResult (or a derived form of ActionResult). If you want to "Redirect" to another action on your controller, you use RedirectToAction. You can use this with or without parameters as illustrated below:
[HttpPost]
public ActionResult Submit(URLModel model)
{
string url = EmailEnd(model);
if (url != "0")
{
return RedirectToAction("RedirectingTime", new { url = url });
}
else
{
return RedirectToAction("Error");
}
}
public RedirectResult RedirectingTime(string url)
{
return Redirect(url);
}
public ActionResult Error()
{
return View();
}

Web Api routing to action with string paramater

It seems simple like in ASP.NET MVC but I can't figure out how to map an URI with a string parameter to an action on my controller. So I have three actions in my controller as below:
//GET: api/Societe
public IEnumerable<Societe> GetSociete()
{
List<Societe> listeSociete = Librairie.Societes.getAllSociete();
return listeSociete.ToList();
}
//GET: api/Societe/id
[ResponseType(typeof(Societe))]
public IHttpActionResult GetSociete(int id)
{
Societe societeRecherchee = Librairie.Societes.getSociete(id);
if (societeRecherchee == null)
{
return NotFound();
}
else
{
return Ok(societeRecherchee);
}
}
public IHttpActionResult GetSocieteByLibelle(string name)
{
Societe societeRecherchee = new Societe();
if (!string.IsNullOrWhiteSpace(name))
{
societeRecherchee = Librairie.Societes.getSocieteByLibelle(name);
if (societeRecherchee == null)
{
return NotFound();
}
}
return Ok(societeRecherchee);
}
So I would like to map an URI with the action:
GetSocieteByLibelle(string name)
My route configuration is the default one for Wep API project. May someone explain me how to map an URI to that action ? Thanks in advance !
Looks like the two routes are resulting in the same method call being invoked. Try putting a [Route] attribute on one of them as follows:
[Route("api/Societe/libelle/{name}")]
public IHttpActionResult GetSocieteByLibelle(string name)
{
}
Note that the default /api/Societe/{id} should still hit your first Action.

Wrong view being returned for controller method

I have a controller method that is returning the wrong view. The view I have is the same name as the controller method "AssignTask.cshtml". The method is "public virtual ActionResult AssignTask(ManageTaskModel model) "
Can anyone see what I'm doing wrong?
[HttpGet]
public virtual ActionResult ManageTasks()
{
try
{
var model = new ManageTaskModel ();
model.assignedPSUsers = Orchestrator.GetAssignedPSUsers();
return View(model);
}
catch (Exception e)
{
ModelState.AddModelError("ErrorMsg", e.Message);
};
return this.RedirectToAction("Index");
}
[HttpPost]
public virtual ActionResult ManageTasks(ManageTaskModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
try
{ //User has seleced the user that they want to see Tasks for
if (model.selectedUser != null && model.newUser==null)
{
model.assignedPSUsers = Orchestrator.GetAssignedPSUsers();
model.FcvsTaskList = Orchestrator.GetTasksForAssignedPSUser(model.selectedUser);
return AssignTask(model);
}
}
catch (Exception e)
{
ModelState.AddModelError("ErrorMsg", e.Message);
return View(model);
}
return this.RedirectToAction("Index");
}
[HttpGet]
public virtual ActionResult AssignTask(ManageTaskModel model)
{
if (model.selectedUser != null && model.newUser == null)
{
**return View(model);** //returning the ManageTask instead of AssignTask View
}
return this.RedirectToAction("Index");
}
In your ManageTasks action you return AssignTask(model). This doesn't work, because the request context still remembers that the user actually called ManageTasks. That's why it returns the view for ManageTasks.
The right way to do it is like that:
return RedirectToAction("AssignTask", model); // remember to pass the model here
You can see that if you put this line in AssignTask:
HttpContext.Request.Path
If you access it from ManageTasks using return AssignTask(model), the value will be "/YourController/ManageTasks".
If you either call this action directly from browser or with RedirectToAction the value will be "/YourController/AssignTask".
you can't redirect that way. instead of return AssignTask you need
return RedirectToAction("AssignTask");
and pass an id or something there. you will need to recreate the model in your AssignTask method

MVC how to return a view after an IF statement

My page has a search box which for has a few radio buttons. Depending on which radio button is selected will depend on which view is shown.
However, I don't know how to return the View.
My code is
public ActionResult Index(string jobType)
{
if (jobType.ToLower() == "this")
CandidateResults();
else
JobResults();
}
private ActionResult CandidateResults()
{
var model = //logic
return View(model);
}
private ActionResult JobResults()
{
var model = //logic
return View(model);
}
But this displays nothing on screen (a white page). This makes sense but I don't want to return Index, I want to return a new page (called either JobResults or Candidates) and create a View for both of these new pages but when I right click in my methods (JobResults() or Candidates()) I don't get the option to Add View.
At this stage I'm lost, can any one please give advice.
Either return the view from Index or redirect to CandidateResults or JobResults actions.
public ActionResult Index(string jobType)
{
if (jobType.ToLower() == "this")
return CandidateResults();
else
return JobResults();
}
private ActionResult CandidateResults()
{
var model = //logic
return View(model);
}
private ActionResult JobResults()
{
var model = //logic
return View(model);
}
Try this
public ActionResult Index(string jobType)
{
return (jobType.ToLower() == "this") ?
RedirectToAction("CandidateResults") :
RedirectToAction("JobResults");
}
private ActionResult CandidateResults()
{
var model = //logic
return View(model);
}
private ActionResult JobResults()
{
var model = //logic
return View(model);
}
In your private methods you have to specify the actual view you want to display.
public ActionResult Index(string jobType)
{
if (jobType.ToLower() == "this")
CandidateResults();
else
JobResults();
}
private ActionResult CandidateResults()
{
var model = //logic
return View("CandidateResults", model);
}
private ActionResult JobResults()
{
var model = //logic
return View("JobResults", model);
}
This happens because of the way the view engine works. The action name for the current request is always Index when the index function is called. Even if you call another method, the view engine will use the name of the current action and not the name of the currently executing function.
Just you need to redirect the user to proper controller method and that method will return its View as below:
public ActionResult Index(string jobType)
{
if (jobType.ToLower() == "this")
return RedirectToAction("CandidateResults","ControllerName");
else
return RedirectToAction("JobResults","ControllerName");
}
public ActionResult Index(string jobType)
{
if (jobType.ToLower() == "this")
return RedirectToAction("CandidateResults");
return RedirectToAction("JobResults");
}
private ActionResult CandidateResults()
{
var model = //logic
return View(model);
}
private ActionResult JobResults()
{
var model = //logic
return View(model);
}

Re-factoring Controller's Action in asp.net mvc3

I am writing this Action code (within same controller) more than 10 times for different Models. Is there any way i can reduce this code or how can i create a generic action.
[HttpPost]
public ActionResult SavePerson(Person p)
{
if (ModelState.IsValid)
{
//do something
return Redirect("/Main");
}
else
{
return View();
}
}
[HttpPost]
public ActionResult SaveCategory(Category c)
{
if (ModelState.IsValid)
{
//do something
return Redirect("/Main");
}
else
{
return View();
}
}
The main point is that //do something part always differs from action to action. So let's try to reduce all code other than that. You could use base controller for it
public class BaseController : Controller
{
[NonAction]
protected virtual ActionResult HandlePost<T>(T model, Action<T> processValidModel)
{
if (ModelState.IsValid)
{
processValidModel(model);
return RedirectToAction("Main");
}
else
{
return View(model);
}
}
}
And in derived controller
public class DerivedController : BaseController
{
[HttpPost]
public ActionResult Create(Person person)
{
return HandlePost(person, p => _repository.Save(p));
}
}
return ModelState.IsValid ? Redirect("/Main"):View();
as a start point would be the only line you need.
For functions which are going to be called too often, create a static class and define all such functions in that.
for example like following
public static class MyAppStaticClass
{
public static SavePerson(Person p)
{
... // your body
}
}
Then, you can refer it like MyAppStaticClass.SavePerson whenever you need it.

Categories

Resources