ASP.NET MVC Multiple Actions for one job - c#

So I am just starting out learning MVC and was wondering if I should use multiple actions in my controller or just one to achieve a simple registration page.
Should I do something like this (Multiple actions):
HTML (RegisterForm)
<form action="CheckRegistration" method="post">
Username:
<input type="text" name="username"/>
<input type="submit" value="Login" />
</form>
Controller
public ActionResult RegisterForm()
{
return View();
}
public ActionResult CheckRegistration()
{
bool success = true;
// Create User Object and populate it with form data
User currentUser = new User();
currentUser.Username = Request.Form["username"].Trim().ToString();
// Validate Registration
// code
// Add user to database
// code
if (success)
{
return View("Login");
}else
{
return View("RegistrationSuccess");
}
}
or this (Single action):
HTML (Register)
<form action="Register" method="post">
Username:
<input type="text" name="username"/>
<input type="submit" value="Login" />
</form>
Controller
public ActionResult Register()
{
bool success = true;
String otherData = ""
// Create User Object and populate it with form data
User currentUser = new User();
currentUser.Username = Request.Form["username"].Trim().ToString();
// Validate Registration
// code
// Add user to database
// code
if (success)
{
return View("Login");
}else
{
return View("Register", otherData);
}
}
With the first way I thought of, it has multiple actions and separates it into multiple steps.
The second way uses one actions so when Register view is called the first time, it won't add a user to the database since validation fails and will just return the View().
Which way is better from a professional standpoint (better) or are these both bad ways and there is a better way.

You should have a simple view and do login in the post.
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}

You can use same name method in MVC.
1) Declare First method As [HttpGet] so it will return View and
2) Declare Second Method As [HttpPost]
[HttpGet]
public ActionResult RegisterForm()
{
return View();
}
[HttpPost]
public ActionResult RegisterForm()
{
bool success = true;
String otherData = ""
// Create User Object and populate it with form data
User currentUser = new User();
currentUser.Username = Request.Form["username"].Trim().ToString();
// Validate Registration
// code
// Add user to database
// code
if (success)
{
return RedirectToAction("RegisterSuccess");
}else
{
return View("RegisterForm");
}
}

Related

Sending a message from one controller to another

When I register a user, it redirects to the Manage controller (where the user profile is).
return RedirectToAction("Index", "Manage", new { id = user.Id });
and in the Manage Controller
[Route("")]
[Route("{id}")]
public ActionResult Index(string id)
{
if (id == null)
{
id = User.Identity.GetUserId();
}
var user = UserManager.FindById(id);
return View(user);
}
I need to send a string message of "newUser", so I can display a modal message the first time the user makes the account and is redirected to their profile.
My question is how exactly? I tried to send a string parameter like this:
return RedirectToAction("Index", "Manage", new { id = user.Id, msg = "newUser" });
and change the Index constructor to Index(string id, string msg) but for some reason both the id and msg get the value of the user id.
Anyone know a solution to this?
Manage / Index
public ActionResult Index(string id)
{
if (id == null)
{
id = User.Identity.GetUserId();
}
var user = UserManager.FindById(id);
return View(user);
}
You can make use of TempData for passing data from one controller to anhother.
Example :
public ActionResult Index()
{
var model = new Review()
{
Body = "Start",
Rating=5
};
TempData["ModelName"] = model;
return RedirectToAction("About");
}
public ActionResult About()
{
var model= TempData["ModelName"];
return View(model);
}
Refer this link for more info : https://www.codeproject.com/articles/476967/what-is-viewdata-viewbag-and-tempdata-mvc-option
You code syntax is almost correct except you are missing " in msg = "newUser
You could have use TempData for sending data from controller to controller.
For more reference :
http://www.tutorialsteacher.com/mvc/tempdata-in-asp.net-mvc

Add Facebook login button in your login page in c# mvc4

I have a requirement in my application to add the Facebook login button through which user can Authenticate through Facebook credentials.
I found this: https://learn.microsoft.com/en-us/aspnet/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
My requirement is to add a facebook button in my login page, not on default login page which MVC provides.
I found one helpful thing, but it's throwing an exception
PFB the code which I am referring
public class AccountController : Controller
{
private Uri RedirectUri
{
get
{
var uriBuilder = new UriBuilder(Request.Url);
uriBuilder.Query = null;
uriBuilder.Fragment = null;
uriBuilder.Path = Url.Action("FacebookCallback");
return uriBuilder.Uri;
}
}
[AllowAnonymous]
public ActionResult login()
{
return View();
}
public ActionResult logout()
{
FormsAuthentication.SignOut();
return View("Login");
}
[AllowAnonymous]
public ActionResult Facebook()
{
var fb = new FacebookClient();
var loginUrl = fb.GetLoginUrl(new
{
client_id = "444195149059600",
client_secret = "89223ca2d87cc4a741000d5c1ea57694",
redirect_uri = RedirectUri.AbsoluteUri,
response_type = "code",
scope = "email" // Add other permissions as needed
});
return Redirect(loginUrl.AbsoluteUri);
}
public ActionResult FacebookCallback(string code)
{
var fb = new FacebookClient();
dynamic result = fb.Post("oauth/access_token", new
{
client_id = "444195149059600",
client_secret = "89223ca2d87cc4a741000d5c1ea57694",
redirect_uri = RedirectUri.AbsoluteUri,
code = code
});
var accessToken = result.access_token;
// Store the access token in the session for farther use
Session["AccessToken"] = accessToken;
// update the facebook client with the access token so
// we can make requests on behalf of the user
fb.AccessToken = accessToken;
// Get the user's information
dynamic me = fb.Get("me?fields=first_name,middle_name,last_name,id,email");
string email = me.email;
string firstname = me.first_name;
string middlename = me.middle_name;
string lastname = me.last_name;
// Set the auth cookie
FormsAuthentication.SetAuthCookie(email, false);
return RedirectToAction("Index", "Home");
}
}
}
I am getting some error in Actionresult Facebook(). How can I solve it?
On your view you must be have like this;
using (Html.BeginForm("ExternalLogin", "Account", new {Model.ReturnUrl}))
{
#Html.AntiForgeryToken()
<div id="socialLoginList">
<p>
#foreach (var p in loginProviders)
{
<button type="submit" class="btn btn-default" id="#p.AuthenticationType" name="provider" value="#p.AuthenticationType" title="Log in using your #p.Caption account">#p.AuthenticationType</button>
}
</p>
</div>
}
I assumed you install OWIN to your application. So on your startup class
public partial class Startup
{
public static IDataProtectionProvider DataProtectionProvider { get; set; }
public void ConfigureAuth(IAppBuilder app)
{
app.UseFacebookAuthentication(
appId: "",
appSecret: "");
}
}
On your external login, you have method like this
[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
{
// Require that the user has already logged in via username/password or external login
if (!await SignInManager.HasBeenVerifiedAsync())
return View("Error");
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
return RedirectToAction("Login");
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation",
new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model,
string returnUrl)
{
if (User.Identity.IsAuthenticated)
return RedirectToAction("Index", "Manage");
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
return View("ExternalLoginFailure");
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, false, false);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
Your link refers to MVC 5, if you use MVC 4 you should look here: https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/using-oauth-providers-with-mvc
There are a few important differences between these versions.. And if you provide more information about the recieved error, it is easier to give a concrete hint about the underlying problem
Bst rgrds

Get return URL on Custom Authorize Attribute

I have a custom Authorize attribute to handle LogIn. And I need to redirect user to last page after login. For example :
Product Controller
[CustomAuthorize]
public ActionResult Detail(int productID)
{
//code here
return View(model);
}
Let's say user isn't logged in when he tried to access Product/Detail/msi-gtx-970, my web application will redirect the user to LogIn page. I want to redirect user back to Product/Detail/msi-gtx-970 after successful LogIn. How to do that?
My LogIn Controller
[AllowAnonymous]
public ActionResult LogIn()
{
//code here
return View();
}
[HttpPost]
[AllowAnonymous]
public ActionResult LogIn(string returnUrl)
{
//code here
if (string.IsNullOrEmpty(returnUrl))
{
return View("Index", "Home");
}
return Redirect(returnUrl);
}
Thanks
You need to receive the returnUrl on your get Action;
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
Change your form on the "Login "view passing the url as parameter for posting the url value:
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
....
}
the rest of your code is fine
in your customeAuthorizer attribute you should have the filterContext object and then you can use the following code sample.
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
filterContext.HttpContext.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Please Provide authToken";
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Authentication",
action = "Login",
errorMessage = "Invalid Resourse Access Attempt",
ReturnUrl = filterContext.HttpContext.Request.Path.Value
}));
or you can use the following function for this purpose as well.
public void AuthFailed(AuthorizationFilterContext filterContext)
{
Console.WriteLine(filterContext.HttpContext.Request.Path.Value);
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
filterContext.HttpContext.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Please Provide authToken";
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Authentication",
action = "Login",
errorMessage = "Invalid Resourse Access Attempt",
ReturnUrl = filterContext.HttpContext.Request.Path.Value
}));
}
and in your login (GET) action you can handle it like this.
TempData["ReturnUrl"] = Request.Query["returnUrl"].ToString();
and after the successfull login (when the user successfully logged in) you gotta redirect it to the same requested page. Login (POST)
if (TempData["ReturnUrl"] != null)
{
string[] temp = TempData["ReturnUrl"].ToString().Split('/');
if (temp.Length == 3)
{
return RedirectToAction(temp[1], temp[0], new { id = temp[2] });
}
else if (temp.Length == 1)
{
return RedirectToAction("Index", "Home");
}
else
{
return RedirectToAction(temp[1], temp[0]);
}
}
else
{
return RedirectToAction("Index", "Home");
}

MVC4 .NET 4.5 async action method does not redirect

I am trying to implement a task action method in my MVC 4 application. Everything works on the back in, but it is not redirecting.
public class AccountController : AsyncController
{
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Login(LoginModel model, string returnUrl)
{
var client = new ClientHelper("login");
account = await client.CallActionType<LoginModel, Account>(EnumHelpers.HttpType.Post, model);
if (account != null)
{
validLogin = true;
}
return Redirect(returnUrl); // This is called but the page does not redirect, just sits a load
}
}
I was able to get it working after making the Action I was directing it to an async action as well. I am guessing if you have any async action method redirecting to another then that redirect must be async as well.
Here is just a quick example
public async Task<ActionResult> Login(LoginModel model) {
//You would do some async work here like I was doing.
return RedirectToAction("Action","Controller");//The action must be async as well
}
public async Task<ActionResult> Action() {//This must be an async task
return View();
}
[Authorize]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// find user by username first
var user = await UserManager.FindByNameAsync(model.Email);
if (user != null)
{
var validCredentials = await UserManager.FindAsync(model.Email, model.Password);
// When a user is lockedout, this check is done to ensure that even if the credentials are valid
// the user can not login until the lockout duration has passed
if (await UserManager.IsLockedOutAsync(user.Id))
{
ModelState.AddModelError("", string.Format("Invalid credentials. Please try again, or contact support", 60));
}
// if user is subject to lockouts and the credentials are invalid
// record the failure and check if user is lockedout and display message, otherwise,
// display the number of attempts remaining before lockout
else if (await UserManager.GetLockoutEnabledAsync(user.Id) && validCredentials == null)
{
// Record the failure which also may cause the user to be locked out
await UserManager.AccessFailedAsync(user.Id);
string message;
if (await UserManager.IsLockedOutAsync(user.Id))
{
message = string.Format("Invalid credentials. Please try again, or contact support", 60);
}
else
{
int accessFailedCount = await UserManager.GetAccessFailedCountAsync(user.Id);
int attemptsLeft = (5 - accessFailedCount);
message = string.Format("Invalid credentials. Please try again, or contact support.", attemptsLeft);
}
ModelState.AddModelError("", message);
}
else if (validCredentials == null)
{
ModelState.AddModelError("", "Invalid credentials. Please try again, or contact support.");
}
else
{
await SignInAsync(user, model.RememberMe);
// When token is verified correctly, clear the access failed count used for lockout
await UserManager.ResetAccessFailedCountAsync(user.Id);
return RedirectToLocal(returnUrl);
}
}
else
{
ModelState.AddModelError("", string.Format("Invalid credentials. Please try again, or contact support", 60));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
//ModelState.AddModelError("", "The user either does not exist or is not confirmed.");
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
else
{
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account",
new { UserId = user.Id, code = code }, protocol: Request.Url.Scheme);
string Data = System.IO.File.ReadAllText(Server.MapPath(#"~/documents/email_password_reset.txt"));
AspNetUser oUser = dbPortal.AspNetUsers.Find(user.Id);
// can't use string.format becuase of CSS
Data = Data.Replace("{0}", oUser.Name); // user name
Data = Data.Replace("{1}", callbackUrl); // URL to click
Data = Data.Replace("{2}", DateTime.Now.Year.ToString()); // copyright year
await UserManager.SendEmailAsync(user.Id, "Reset Password", Data);
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public async Task<ActionResult> ForgotPasswordConfirmation()
{
return View();
}
}
the above solution doesn't work for me

strange premature validation of control

I am learning a newly created default simple MVC4 web project.
In the index page, I have a link for the user to log on the site with his account. After that he will be redirected to a form to enter new name, new password.
I have this form ready for validation using [Required]. But as sooon as the redirected page is completely loaded, these controls (username and password) were done validated (Field needs be filled in) too.
Here is the code of POST after the user log in with his account
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToCreateUser(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
and here is the method RedirectToCreateUser
private ActionResult RedirectToCreateUser(string url)
{
if (Url.IsLocalUrl(url))
{
return Redirect(url);
}
else
{
return RedirectToAction("CreateNewUser", "Account");
}
}
finally the CreateNewUser method which is for http GET
public ActionResult CreateNewUser(CreateNewUserModel model)
{
return View(model);
}
and another one for http POST which I think hasn't been accessed yet though.
[HttpPost]
public ActionResult CreateNewUser(CreateNewUserModel model, string url)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, null, true);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("CreateUserSuccess", "Account");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
else
{
}
return View(model);
}
your problem is here
finally the CreateNewUser method which is for http GET
public ActionResult CreateNewUser(CreateNewUserModel model)
{
return View(model);
}
You cannot pass an object as a parameter on a get request. Probably that signature should be
public ActionResult CreateNewUser()
{
var model = new CreateNewUserModel();
return View(model);
}
or something similar
The CreateNewUser action is firing on the [HttpPost] and attempting to post with invalid (empty) credentials.
You need to add something to this effect using [HttpGet]:
[HttpGet]
public ActionResult CreateNewUser(CreateNewUserModel model)
{
return View(model);
}

Categories

Resources