I have control for user authorization which includes form, two textboxes and submit button.
This control included in the master page through RenderAction method.
I have registration page (its view included through RenderBody method) also with form. When I submit data from the registration form, the login control is triggered also and its handler (controller method for handling POST data) is called. Below you can see controller methods for authorization.
How can I prevent sending POST data to the login control after submitting data from other forms?
[HttpPost]
public RedirectResult LogIn(AuthViewModel authResult)
{
if (ModelState.IsValid)
{
userService.LogInUser(authResult.Login, authResult.Password, Request.UserHostAddress);
}
else
{
TempData["AuthMessage"] = GetValidationMessage();
}
string redirectUrl = "/";
if (Request.UrlReferrer != null)
{
redirectUrl = Request.UrlReferrer.AbsoluteUri.ToString();
}
return Redirect(redirectUrl);
}
[HttpGet]
[ChildActionOnly]
public PartialViewResult LogIn()
{
if (userService.IsUserLoggedIn())
{
User currentUser = userService.GetLoggedInUser();
ViewBag.LoggedInMessage = currentUser.FullName + "(" + currentUser.Login + ")";
}
return PartialView("AuthControl");
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div>
<div id="header">
<div>
<div>
#{Html.RenderPartial("SearchControl");}
</div>
</div>
</div>
<div id="right_menu">
<div>
#{Html.RenderAction("LogIn", "Navigation");}
</div>
#{Html.RenderAction("Menu", "Navigation");}
<div>
#{Html.RenderAction("Index", "Messages");}
</div>
<div>
#{Html.RenderAction("TagCloud", "Navigation");}
</div>
</div>
<div id="main_content">
#RenderBody()
</div>
<div id="footer">
</div>
</div>
</body>
</html>
AuthControl:
#model AuthViewModel
<div class="rounded-corners auth-panel">
#if (ViewBag.LoggedInMessage == null)
{
<div class="auth-container">
#using (Html.BeginForm("LogIn", "Navigation"))
{
<div>
<label for="Login">
Login:
</label>
#Html.TextBoxFor(m => m.Login, new { #class="middle-field"})
</div>
<div>
<label for="Password">
Password:
</label>
#Html.PasswordFor(m => m.Password, new { #class="middle-field"})
</div>
<div class="in-center">
<input type="image" src="#Url.Content("~/Content/Images/submit.png")"/>
</div>
}
</div>
<div class="error-msg">
#if (TempData["AuthMessage"] != null)
{
#Html.Raw(TempData["AuthMessage"].ToString())
}
#Html.ValidationSummary()
</div>
<div class="small-nav-message">
Registration
</div>
}
</div>
Registration page:
RegistrationViewModel
#{
ViewBag.Title = "Registration";
}
#if (TempData["RegistrationFinished"] == null || !(bool)TempData["RegistrationFinished"])
{
<div class="post content-holder">
<div class="fields-holder">
<div >
<div class="error-msg">
#if (TempData["ValidationMessage"] != null)
{
#Html.Raw(TempData["ValidationMessage"].ToString())
}
</div>
#using (Html.BeginForm())
{
<span>
Email:
</span>
<span>
#Html.TextBoxFor(v => v.Email)
</span>
<span>
Password:
</span>
<span>
#Html.PasswordFor(v => v.Password)
</span>
<input type="submit" value="Register"/>
}
</div>
</div>
</div>
}
else
{
<div>
Activation link was sent to your email.
</div>
}
In the Registration view, change
#using (Html.BeginForm())
to
#using (Html.BeginForm("Index", "Registration"))
In a single controller, single Action scenario, the extra specific routing information is not required, but obviously the routing engine can't figure out on it's own which controller/action to route to with multiple controllers/actions.
Edit based on comments:
So this is a routing problem. Try adding a specific route for your Registration action. Something like
routes.MapRoute(
"Register", // Route name
"{controller}/Index/{registrationResult}", // URL with parameters
new {
controller = "{controller}",
action = "Selector",
registrationResult= UrlParameter.Optional
}
);
'registrationResult' would be the name of the parameter in the post Action. I'm thinking that the view models are so similar the routing engine can't differentiate between the two. Add the above route before the default route and it should match it when the registration form is submitted.
To solve my problem I check IsChildAction property from the controller context. Also I have to clear the model state.
[HttpPost]
public ActionResult LogIn(AuthViewModel authResult)
{
if (!this.ControllerContext.IsChildAction)
{
if (ModelState.IsValid)
{
userService.LogInUser(authResult.Login, authResult.Password, Request.UserHostAddress);
}
else
{
TempData["AuthMessage"] = GetValidationMessage();
}
string redirectUrl = "/";
if (Request.UrlReferrer != null)
{
redirectUrl = Request.UrlReferrer.AbsoluteUri.ToString();
}
return Redirect(redirectUrl);
}
ModelState.Clear();
return PartialView("AuthControl");
}
Related
I have a simple page where i try to load an image from the database.
The method in controller is not responding to the url.Action call.
i have a page in another area which is identical to this page and it is working fine there
(I literally copy the html from the working view to this view and the method is still not invoked).
On the browser is get :
GET https://localhost:7260/admin/AdImage/MyAction?path=img%2Fjpg net::ERR_BLOCKED_BY_CLIENT
Not getting this error in the other working page.
Here is the view calling the controller method:
#model Click2Lock.Models.ForCompListVM
#{ ViewData["Title"] = "Home Page"; }
<body>
<div class="col-4" >
<div id="imageDiv">
<img src= '#Url.Action("MyAction", "AdImage", new{path="img/jpg"})'/> #* NOT RESPONDING *#
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="forCompList.CompId">Company</label>
</div>
<div class="col-8">
#Html.DropDownListFor(m => m.forCompList.CompId, Model.CompList,
new { #class = "form-control" })
</div>
</div>
<div class="col-2 text-right">
<button class="btn btn-primary" style="background-color:forestgreen" onclick="LoadImage()">
<i class="fa fa-refresh"></i> Load Company Image</button>
</div>
</div>
</body>
controller method:
[HttpGet]
[Route("compmanager/AdImage/MyAction")]
public FileContentResult MyAction()
{
byte[] img = new byte[100000];
ApplicationUser appUser =
_unitOfWork.ApplicationUser.GetAll().Where(a => a.UserName == User.Identity.Name).FirstOrDefault();
if (appUser != null)
{
img = _unitOfWork.Company.GetAll().Where(a => a.Id == appUser.CompanyId).
Select(a => a.PhotoAd).FirstOrDefault();
}
if (img == null)
{
return null;
}
return new FileContentResult(img, "img/jpg");
}
To prevent cross side scripting i implement CSP to one of my applications. At moment i´m reconfigure all html classes, so that javascript always comes from my server.
Now i found a page with an Ajax.BeginForm and always get the error "Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'"." if i want to submit the form and update the view.
Can anybody help me, where the problem is?
Here is my html classes (shorted):
UserInformation.cshtml:
<div id="OpenAccountInformation">#Html.Action("OpenAccountInformation")</div>
</div>
AccountInformation.cshtml:
#Scripts.Render("~/Scripts/bundles/ManageUsers/AccountInformation")
#model Tumormodelle.Models.ViewModels.AzaraUserModel
<input type="hidden" value="#ViewBag.Editable" id="EditableUserInformation">
<div id="Editable">
#using (Ajax.BeginForm("EditUser", "ManageUsers", new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "OpenAccountInformation", HttpMethod = "post", }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.UserID)
<div>
<div>
#Html.LabelFor(m => m.Username, new { #class = "entryFieldLabel" })
</div>
</div>
<div>
<div>
<button name="button" value="save" class="formbutton" id="saveButton">save</button>
<button name="button" value="cancel" class="formbutton" id="cancelButton">cancel</button>
</div>
}
</div>
<div id="NonEditable">
<div>
<div>
#Html.LabelFor(m => m.Username, new { #class = "entryFieldLabel" })
</div>
</div>
<div>
<div>
<button name="button" value="edit" class="formbutton" id="editButton" type="button">edit</button>
</div>
</div>
</div>
and the c# methods:
public ActionResult EditUser(AzaraUserModel AzaraUserModel, string button)
{
if (button == Tumormodelle.Properties.Resources.Save)
{
if (ModelState.IsValid)
{
azaraUserManagement.Update(AzaraUserModel.Username, AzaraUserModel.Title, AzaraUserModel.FirstName, AzaraUserModel.LastName, AzaraUserModel.EMailAddress, null, AzaraUserModel.Phone, AzaraUserModel.UserID, (byte)AzaraUserModel.ShowMail.ID);
ViewBag.Message = Tumormodelle.Properties.Resources.Personal_Data_Changed;
ViewBag.Editable = true;
}
else ViewBag.Editable = false;
BindShowMailList();
return PartialView("AccountInformation", AzaraUserModel);
}
else
{
return RedirectToAction("OpenAccountInformation", "ManageUsers");
}
}
public ActionResult UserInformation()
{
return View("UserInformation");
}
public PartialViewResult OpenAccountInformation()
{
AzaraUserModel AzaraUserModel = new AzaraUserModel(azaraUserManagement.GetSingle(AzaraSession.Current.UserComparison.GetUser().Id));
BindShowMailList();
ViewBag.Editable = true;
return PartialView("AccountInformation", AzaraUserModel);
}
Edit: With help of Chrome debugger i find out, that the error is thrown in the moment form becomes submited.
Ajax.BeginForm will be generating inline script in the generated HTML of your page, which you have disallowed by use of script-src 'self' in your Content Security Policy.
If you want to use the CSP to prevent any inline injected scripts you must use Html.BeginForm instead and add the JavaScript to submit this via Ajax in an external .js file.
try to add this attribute to your controller post action
[ValidateInput(false)]
I want to set a bool to true in the controller when producing a certain view and then alter the header of the view accordingly. This should be dead simple but instead Im getting:
Cannot perform runtime binding on a null reference Exception Details:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot perform
runtime binding on a null reference
All I'm doing is in controller:
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.IsRegistration = true;
return View();
}
and then in view:
#if (ViewBag.IsRegistration)
{
<legend>Register using another service.</legend>
}
else
{
<legend>Use another service to log in.</legend>
}
but it fails on:
#if (ViewBag.IsRegistration)
UPDATE
Relevant Controller Code:
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.IsRegistration = "true";
return View();
}
The register view:
#model Mvc.Models.RegisterViewModel
#{
Layout = "~/Views/Shared/_AccountLayout.cshtml";
ViewBag.Title = "Register";
}
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
</hgroup>
<div class="row">
<div class="col-lg-6">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset class="form-horizontal">
<legend>Create a new account.</legend>
<div class="control-group">
#Html.LabelFor(m => m.UserName, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(m => m.UserName)
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.Password, new { #class = "control-label" })
<div class="controls">
#Html.PasswordFor(m => m.Password)
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "control-label" })
<div class="controls">
#Html.PasswordFor(m => m.ConfirmPassword)
</div>
</div>
<div class="form-actions no-color">
<input type="submit" value="Register" class="btn" />
</div>
</fieldset>
}
</div>
<div class="col-lg-6"></div>
<section id="socialLoginForm">
#Html.Action("ExternalLoginsList", new { ReturnUrl = ViewBag.ReturnUrl })
</section>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The ExternalLoginsList partial:
#using Glimpse.Core.Extensions
#using Microsoft.Owin.Security
#model ICollection<AuthenticationDescription>
#if (Model.Count == 0)
{
<div class="message-info">
<p>There are no external authentication services configured</p>
</div>
}
else
{
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
<fieldset id="socialLoginList">
#if (!string.IsNullOrEmpty(ViewBag.IsRegistration))
{
<legend>Register using another service.</legend>
}
else
{
<legend>Use another service to log in.</legend>
}
<p>
#foreach (AuthenticationDescription p in Model) {
<button type="submit" class="btn" id="#p.AuthenticationType" name="provider" value="#p.AuthenticationType" title="Log in using your #p.Caption account">#p.AuthenticationType</button>
}
</p>
</fieldset>
}
}
Try:
#if (ViewBag.IsRegistration == true)
I know this is an old question, but I think I have an elegant answer, so in case anyone reads this after searching, here is mine:
#if (ViewBag.IsRegistration ?? false)
Try this:
Replace the line in your controller:
ViewBag.IsRegistration = true;
with
ViewBag.IsRegistration = new bool?(true);
and replace the line in your view:
#if (ViewBag.IsRegistration)
with
#if ((ViewBag.IsRegistration as bool?).Value)
Effectively you are putting a nullable bool in the ViewBag and then unwrapping it.
Simply check for null before checking for true:
if (ViewBag.IsRegistration != null && ViewBag.IsRegistration)
Try TempData instead of ViewBag.
Change your code from
Controller
ViewBag.IsRegistration=true;
to
TempData["IsReg"]=true;
and in View
#if((bool)TempData["IsReg"])
It seems that you are using the value in child partial view and you are adding the data in parent action.The values in viewbag cannot pass out data from one action to anothers action's view. While TempData use session it can be used to pass data to one action to another actions view.
Ok so as per Floods suggestion in comments, I need to pass the arguments around. The ViewBag from the parent View does not flow through to partial views.
So in the code for the Register View I needed to change from
<section id="socialLoginForm">
#Html.Action("ExternalLoginsList", new {ReturnUrl = ViewBag.ReturnUrl})
</section>
to
<section id="socialLoginForm">
#Html.Action("ExternalLoginsList",
new {ReturnUrl = ViewBag.ReturnUrl,
IsRegistering = #ViewBag.IsRegistering })
</section>
Then go into my account controller and change from:
[AllowAnonymous]
[ChildActionOnly]
public ActionResult ExternalLoginsList(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return (ActionResult)PartialView("_ExternalLoginsListPartial", new List<AuthenticationDescription>(AuthenticationManager.GetExternalAuthenticationTypes()));
}
to
[AllowAnonymous]
[ChildActionOnly]
public ActionResult ExternalLoginsList(string returnUrl, string isRegistering) {
ViewBag.IsRegistering = (isRegistering.ToLower() == "true");
ViewBag.ReturnUrl = returnUrl;
return (ActionResult)PartialView("_ExternalLoginsListPartial", new List<AuthenticationDescription>(AuthenticationManager.GetExternalAuthenticationTypes()));
}
Then in the ExternalLogins I can just:
#if (ViewBag.IsRegistering)
as necessary.
So Im effectively passing the IsRegistering bool from controller to main view then back up to action method on controller then into ViewBag which allow me to access the bool in the child partial view.
Many thanks.
Booleans in Viewbag are always tricky. Try this instead
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.Registration = "x";//x or whatever
return View();
}
#if (!String.IsNullorEmpty(ViewBag.Registration))
{
<legend>Register using another service.</legend>
}
else
{
<legend>Use another service to log in.</legend>
}
Maybe so:
#if ((ViewBag.IsRegistration != null) && (ViewBag.IsRegistration is bool) && (bool)ViewBag.IsRegistration)
{
}
I'm trying to refresh a partial view inside of a view when a form is submitted. However, whenever I try it just renders the partial view as a normal view. Can someone tell me what I'm doing wrong?
Controller:
public ActionResult ChangeHeatName(string heatName, string updatedHeat)
{
string user = User.Identity.Name;
HomeModel H = new HomeModel();
H.ChangeHeatName(heatName, updatedHeat, user);
ChemViewModel mySlagViewModel = new ChemViewModel();
mySlagViewModel = H.QueryResults(heatName);
return PartialView("PartialChemAnalysis", mySlagViewModel);
}
Partial view form (contained in partial view, not main view):
#using (Ajax.BeginForm("ChangeHeatName", "Home", new AjaxOptions(){UpdateTargetId = "chemDiv" InsertionMode = InsertionMode.Replace}))
{
<section>
Heat Name:<input type="text" name="heatName" value="#Html.ValueFor(x => x.heatname)" style ="width:100px"/>
Change to:<input type="text" name="updatedHeat" value="" style="width: 100px" />
<input type="submit" name="ChangeHeatName" value="Change" />
</section>
}
Index view where partial view is being rendered:
#if(ViewBag.SearchKey == null)
{
<div class="content-wrapper">
<hgroup class="title">
<h1>#HttpContext.Current.User.Identity.Name</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
</div>
}
#using (Html.BeginForm("Index", "Home", "POST"))
{
<div class="searchField">
<input type="text" class="search-query" name="heatSearch" placeholder="Search">
<button class="btn btn-success" type="submit">Search</button>
<br />
#if (ViewBag.AverageSuccessful == true)
{
<input type="text" name="AvgConfirmation" class="search-query" value="Average Submitted Successfully" width:"400px" placeholder="Search" />
}
</div>
}
#if(ViewBag.SearchKey != null)
{
<div>
<div id ="chemDiv">
#Html.Action("PartialChemAnalysis", "Home", (string)ViewBag.SearchKey)
</div>
<div id ="slafDiv">
#Html.Action("PartialSlagView", "Home", (string)ViewBag.SearchKey)
</div>
</div>
}
Index controller that passes SearchKey:
[HttpPost]
public ActionResult Index(string heatSearch)
{
ViewBag.SearchKey = heatSearch;
return View();
}
Currently your ajax.beginform is in your partial view, that's all fine and dandy, but your partialview is not rendered inside your index, so really your never doing the ajax replace logic you're just calling a the action method and getting a full page refresh of the partial view.
here's what would work.
#if(ViewBag.SearchKey != null)
{
<div>
<div id ="chemDiv">
#Html.Partial("ChangeHeatName")
</div>
<div id ="slafDiv">
#Html.Action("PartialSlagView", "Home", (string)ViewBag.SearchKey)
</div>
</div>
}
Now your Ajax.Beginform is rendered in the index view, and when the button is clicked it will refresh.
Edit: you'll need to do something with #Html.Action("PartialChemAnalysis", "Home", (string)ViewBag.SearchKey) possibly stick it in your partial view, because everything in "chemDiv" will now be replaced upon update.
You're not specifying POST in Ajax.BeginForm(). Try this:
#using (Ajax.BeginForm("ChangeHeatName", "Home", FormMethod.Post,
new AjaxOptions(){UpdateTargetId = "chemDiv" InsertionMode = InsertionMode.Replace}))
{...}
Also, stick a breakpoint on your controller action and step through it, see if it's actually hitting return PartialView() or skipping over it.
Posting this because it's not an intuitive fix. Apparently there are issues with MVC 4 and jQuery 1.9.1 so to get this to work I had to change my reference to jQuery 1.7.1
I have a MVC beginform (with JQUERY UI MOBILE AND AJAX.) When the user clicks submit,
I would like **<div data-role="page" id="two">** to display on the page.
#using (Html.BeginForm())
{
<div data-role="page" id="one">
<div data-role="header">Header stuff</div><!-- /header -->
<div data-role="content" >
<div class="ui-body ui-body-b">
.....DATA......
<input type="submit" value="Submit" />
</div></div><div>
<div data-role="page" id="two">
<div data-role="header">Header stuff2</div><!-- /header -->
<div data-role="content" >
<div class="ui-body ui-body-b">
FINISH
</div></div><div>
}
I just ran into this and found out I cannot return the anchor tag from MVC's controllers. So, I redirect users from a parameter that the controller passes back:
if( getParameterByName("anchorID") != null) {
location.href = '#' + getParameterByName("anchorID");
}
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)')
.exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
Then you should be able to get your div to view after clicking the submit.