Redirect to action with parameters always null in mvc - asp.net-mvc-4

When I tried redirect to action, the parameter is always null when I received ? I don't know why this happening like these.
ActionResult action1() {
if(ModelState.IsValid) {
// Here user object with updated data
redirectToAction("action2", new{ user = user });
}
return view(Model);
}
ActionResult action2(User user) {
// user object here always null when control comes to action 2
return view(user);
}
And with this I've another doubt. when I accessed action with route, i can get values only by RouteData.Values["Id"]. the values routed doesn't send to parameter.
<a href="#Url.RouteUrl("RouteToAction", new { Id = "454" }> </a>
Here Am I miss any configure ? or anything I miss.
ActionResult tempAction(Id) {
// Here Id always null or empty..
// I can get data only by RouteData.Values["Id"]
}

You cannot pass complex objects in an url like that. You will have to send its constituent parts:
public ActionResult Action1()
{
if (ModelState.IsValid)
{
// Here user object with updated data
return RedirectToAction("action2", new {
id = user.Id,
firstName = user.FirstName,
lastName = user.LastName,
...
});
}
return view(Model);
}
Also notice that I have added the return RedirectToAction instead of only calling RedirectToAction as shown in your code.
But a much better approach is to send only the id of the user:
public ActionResult Action1()
{
if (ModelState.IsValid)
{
// Here user object with updated data
return RedirectToAction("action2", new {
id = user.Id,
});
}
return view(Model);
}
and in your target action use this id to retrieve the user from wherever this user is stored (could be database or something):
public ActionResult Action2(int id)
{
User user = GetUserFromSomeWhere(id);
return view(user);
}
Some alternative approaches (but one I don't recommend or use) is to persist the object in TempData:
public ActionResult Action1()
{
if(ModelState.IsValid)
{
TempData["user"] = user;
// Here user object with updated data
return RedirectToAction("action2");
}
return view(Model);
}
and in your target action:
public ActionResult Action2()
{
User user = (User)TempData["user"];
return View(user);
}

Related

What return in web api asp net core

I'm not sure what I should return in LoginAsync method. Sometimes I have an error to return, but sometimes authentication succes. I'm currently using dynamic, but heard it's bad practice. What should i use?
It's LoginAsync in AccountService service:
public async Task<dynamic> LoginAsync(User user)
{
var existingUser_byEmail = await FindUserByEmailAsync(user.Email);
if (existingUser_byEmail == default)
return new Error
{
StatusCode = 400,
ErrorMessages = { { "email", "Nie odnaleziono użytkownika z podanym adresem e-mail" } }
};
if (BCrypt.Net.BCrypt.EnhancedVerify(user.Password, existingUser_byEmail.Password))
return new AuthSuccessful { StatusCode = 200, Token = _jwtService.GenerateToken(existingUser_byEmail) };
else
return new Error { StatusCode = 401, ErrorMessages = { { "password", "Błędne hasło" } } };
}
And it's Login method in AccountController:
[HttpPost("login")]
public async Task<IActionResult> LogIn([FromBody] User user)
{
var response = await _accountService.LoginAsync(user);
return StatusCode(response.StatusCode, response);
}
Thanks for all answers, have a nice day! :D
You have multiple options.
1. Interface/base class
It seems everything you return has a similar structure – a StatusCode property, and some other additional properties that make sense in the context of the given status code.
So the most obvious might be to create a base class or an interface for these, like this:
public interface IOperationResult
{
int StatusCode { get; init; }
object Response { get; }
}
public class Error : IOperationResult
{
public int StatusCode { get; init; }
public string[,] ErrorMessages { get; init; }
public object Response => ErrorMessages;
}
public class AuthSuccessful : IOperationResult
{
public int StatusCode { get; init; }
public string Token { get; init; }
public object Response => Token;
}
This is a well-defined structure that will arguably support more complex business logic, when you might have to check the exact type of the return value and access properties on them in a type-safe manner.
2. Value tuples
Another option that I use a lot these days is returning a value tuple, with one member containing the success/failure, and another the result; like the following. It looks pretty bad in this case, because the format of the error messages aren't defined. But if you used a class or struct for that, it would be quite okay.
public async Task<(int statusCode, object response)> LoginAsync(User user)
{
var existingUser_byEmail = await FindUserByEmailAsync(user.Email);
if (existingUser_byEmail == default)
return (statusCode: 400, response: new[] { new[] { "email", "Nie odnaleziono użytkownika z podanym adresem e-mail" } });
if (BCrypt.Net.BCrypt.EnhancedVerify(user.Password, existingUser_byEmail.Password))
return (statusCode: 200, response: _jwtService.GenerateToken(existingUser_byEmail));
else
return (statusCode: 401, response: new[] { new[] { "password", "Błędne hasło" } });
}
// Then you can do a tuple deconstruction assignment:
[HttpPost("login")]
public async Task<IActionResult> LogIn([FromBody] User user)
{
var (statusCode, response) = await _accountService.LoginAsync(user);
return StatusCode(statusCode, response);
}
3. Do the HTTP code and error message selection outside the service
It's more traditional to return a different flag from authentication services, and then map that to an HTTP code somewhere closer to the controller (or inside the controller). This way you avoid coupling the service to HTTP concerns, which arguably shouldn't be their responsibility.
For example a lot of built-in Identity services use the Microsoft.AspNetCore.Identity.SignInResult class.
In the following implementation I changed the LoginAsync method to return a failed result both in the case of invalid password and invalid email. This is actually a better practice, because if you tell the person trying to log in that an email address does or doesn't have an account, you're leaking out user information.
public async Task<(SignInResult result, string token)> LoginAsync(User user)
{
var existingUser_byEmail = await FindUserByEmailAsync(user.Email);
if (existingUser_byEmail == default)
return (SignInResult.Failed, null);
if (BCrypt.Net.BCrypt.EnhancedVerify(user.Password, existingUser_byEmail.Password))
return (SignInResult.Success, _jwtService.GenerateToken(existingUser_byEmail));
else
return (SignInResult.Failed, null);
}
[HttpPost("login")]
public async Task<IActionResult> LogIn([FromBody] User user)
{
var (result, token) = await _accountService.LoginAsync(user);
if (result.Succeeded)
return Ok(token);
// Handle lock-out and 'login not allowed' situation too, if necessary.
return Unauthorized("Invalid password or email.");
}

Redirecting to a response view with a model does not keep model properties

I have a form view that submits form data to the post action on a controler and then redirects to another view that uses logic to display either a success or failure, but the new view just shows blank values for model properties. Here is the post action:
[HttpPost]
public ActionResult ContactUs(TTT.Models.ContactUsModel model)
{
logger.Info(model.URL + "Contact Us Form submitted");
var userkey = model.ValidationKey;
var sessionkey = Session["ContactUsKey"];
var lastsubmission = Session["ContactUsTime"];
model.Response = "success";
//first check if honeypot was populated via a bot and if so send it to the success page without doing anything
if (model.WorkAddress != "")
{
logger.Info("honeypot triggered");
return View("ContactUsResponse", model);
}
I'll leave out the remainder of the controler, but
And here is the view it's redirecting to:
#using TTT.Models
#using Sitecore.Mvc
#model ContactUsModel
<h1>#Model.Title</h1>
<div>#Model.Body</div>
<div>
#if (#Model.Response == "fail")
{
#Model.Failure;
} else
{
#Model.Success;
}
</div>
Instead of returning a new view, call RedirectToAction and return new view from that controller.
[HttpPost]
public ActionResult ContactUs(TTT.Models.ContactUsModel model)
{
//--- Code omitted for brevity
if (model.WorkAddress != "")
{
logger.Info("honeypot triggered");
return RedirectToAction("ContactUsResponse", new { response = model });
}
}
public ActionResult ContactUsResponse(TTT.Models.ContactUsModel response)
{
return View(model)
}

WebApi2 Routing with Get Request does not work

Hi I'm making a simple program that you can search for the user, register the user, and get all the user that are registered.
I want to be able to search for my users using
/api/user/search?name=...&email= B
But this is not working. I get an error that is:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:63881/api/user/search'.",
"MessageDetail": "No action was found on the controller 'User' that matches the request."
}
I have code that looks like this
[RoutePrefix("api/user")]
public class UserController : ApiController
{
public HttpResponseMessage Get() {
// Check if the user is in the database
return Request.CreateResponse(HttpStatusCode.OK, "Return All");
}
public HttpResponseMessage Post(UserForm form) {
if (ModelState.IsValid == false) {
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
if (form == null) {
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Please enter data");
}
// Register the person
return Request.CreateResponse(HttpStatusCode.OK, "Register");
}
[Route("search/{name}/{email}")]
[HttpGet]
public HttpResponseMessage SearchGet(string name, string email) {
return Request.CreateResponse(HttpStatusCode.OK, "Found");
}
Thanks!
Adding another route attribute to the SearchGet method should work:
[Route("search") ]
[Route("search/{name}/{email}")]
[HttpGet]
public HttpResponseMessage SearchGet(string name, string email) {
return Request.CreateResponse(HttpStatusCode.OK, "Found");
}
It will map the request based on the querystring parameter count and name or each querystring parameter.

Redirect view to another if a certain condition is met

I'd like my Index controller to do 2 things based on the logged-in user's role:
If the user's role is "Master", then it should continue to load the Index view which contains a list of Companies registered on the website.
If the user's role is "Admin", then the View should redirect to the Edit view with the user's Company details preloaded into the view.
It's this second action that I'm having trouble with. Consider the following:
public ActionResult Index()
{
if (Session["userid"] != null)
{
if (String.Compare(Convert.ToString(Session["userrole"]), "Master", StringComparison.InvariantCultureIgnoreCase) == 0)
return View(db.Companies.ToList());
return View("Edit", new Id = Convert.ToInt32(Session["companyid"])) // this doesn't work
}
return RedirectToAction("Index", "Home");
}
How can I redirect the user to the other view and preload the required data?
Thanks in advance!
public ActionResult Index()
{
if (Session["userid"] != null)
{
if (String.Compare(Convert.ToString(Session["userrole"]), "Master", StringComparison.InvariantCultureIgnoreCase) == 0)
return View(db.Companies.ToList());
return RedirectToAction("Edit", new { id = Convert.ToInt32(Session["companyid"]});
}
return RedirectToAction("Index", "Home");
}
public ActionResult Edit(int id)
{
///Get your company by id here
}

How to pass a parameter through CRUD in asp.net mvc4?

I want to add posts to threads in my forum project, but to do so I need to pass a parameter with thread ID, so after creating post it will redirect me back to that specific thread, but the problem is that I have no idea how to pass that parameter...
Here is my Create() code:
// GET: /Posts/Create
public ActionResult Create(int id)
{
ViewBag.ThreadId = new SelectList(db.Albums, "ThreadId", "Title");
ViewBag.IdOfThread = id;
return View();
}
//
// POST: /Posts/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Posts posts)
{
if (ModelState.IsValid)
{
db.Posts.Add(posts);
db.SaveChanges();
return RedirectToAction("Index", new { **id = 5** });
}
ViewBag.ThreadId = new SelectList(db.Albums, "ThreadId", "Title", posts.ThreadId);
//ViewBag.IdOfThread = id;
return View(posts);
}
When I strongly type number id = 5 it works as intended, so how can I make ActionResult Create(Posts posts) see my ViewBoxes from Create View? Or maybe there is some better way to do that without using ViewBoxes?
Through the glory of EF when you add a model to the Entity and call SaveChanges() it will automatically put the ID back into the model.
if (ModelState.IsValid)
{
db.Posts.Add(posts);
db.SaveChanges();
// Replace Id with whatever your auto increment PK is.
return RedirectToAction("Index", new { id = posts.Id });
}