ASP MVC5 Partial view authentification redirection - c#

I'm quite new with ASP so be tolerant :)
I've got a view with a search form in it.
View
<div id="search-form" class="row search-form">
#using(Ajax.BeginForm("Search",
"Home",
new AjaxOptions
{
UpdateTargetId = "result",
InsertionMode = InsertionMode.Replace,
LoadingElementId = "ajax_loader"
},
new { #class = "form-horizontal col-sm-offset-3 col-sm-6" }
))
{
<div class="form-group">
<div class="col-sm-10">
#{string query = string.Empty;}
#Html.EditorFor(x => query, new { htmlAttributes = new { #class = "form-control" } })
</div>
<input id="submitbtn" type="submit" value="Search" class="btn btn-primary col-sm-2" />
</div>
}
</div>
<div id="ajax_loader" style="display:none">
<img src="~/Content/Images/ajax_loader.gif" alt="Ajax Loader" />
</div>
<div id="result"></div>
Controller
[AllowAnonymous]
public ActionResult Index()
{
ViewBag.Title = "serach form";
return View();
}
public async Task<ActionResult> Search(string query)
{
WinesApiController winesCtrl = new WinesApiController();
var listOfWines = await winesCtrl.Get(query);
return PartialView("_WineResult", listOfWines);
}
The Search method in my controller returns a PartialView. When I decorate all the methodsin the controller with the [AllowAnonymous] attribute, everything works very well. But what I would like it to display the form for anybody, but as long as you click on the search button, you need to be logged in. So I deleted all the AllowAnonymous attributes but on the Index method (the one which render my View). Now, the result of my call is not shown anymore (which is quite ok) but I am not redirected to the login view.
My question is then, why the call to the partial view does not redirect me to the login view ? I guess that authentification is performed because I cannot see the results of the submit action, but why am I not redirected ?

You are making an ajax call to the Search Action right? The default asp.net mvc AuthorizeAttribute does not return an appropriate response when authorization fails on ajax calls.
You could write your own Authorization Filter, that returns a better response, like this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class YourAuthorizeAttribute : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
var httpContext = filterContext.HttpContext;
var request = httpContext.Request;
var response = httpContext.Response;
if (request.IsAjaxRequest()) {
response.SuppressFormsAuthenticationRedirect = true;
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.End();
}
base.HandleUnauthorizedRequest(filterContext);
}
}
With this filter, you could have a default javascript code to handle all unauthorized scenarios:
$.ajaxSetup({
statusCode: {
401: function (response) {
var returnUrl = encodeURI(window.location.pathname + window.location.search);
var loginUrl = '/custumer/login?ReturnUrl=' + returnUrl;
window.location.href = loginUrl;
}
}
});

What happens if you put an Authorize attribute on the action?
[Authorize()]
public async Task<ActionResult> Search(string query)
{
WinesApiController winesCtrl = new WinesApiController();
var listOfWines = await winesCtrl.Get(query);
return PartialView("_WineResult", listOfWines);
}

Related

C# Inject Javascript into View with JsonResult

I have a Controller Action which returns a JsonResult instead of a View. Depending on whether or not the method completed successfully, I need to add and execute a Javascript. I've added an OnActionExecuted ActionFilter to the method to generate and add the script, but because it's returning a JsonResult, I don't have a ViewResult to which I can add my script.
I'm a bit out of ideas on this. Does anyone have a solution to this problem or know of another way to approach this issue?
Controller method:
[InsertJavascript]
public async Task<ActionResult> Create(CreateAccountPageV2 currentPage, CreateAccountViewModel model)
{
//some logic here
return return Json(new
{
success = true,
redirectUrl = false,
html = partialViewToString,
invalidFields = InvalidFields
});
}
Action Filter:
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class InsertJavascriptAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var jsonResult = filterContext.Result as JsonResult;
if (jsonResult == null)
{
return;
}
var script = GenerateMyJavascript();
// some way to send the script to View and execute it
}
}
Edit: The request is made from an Ajax Form contained in a Partial View.
Partial View file where the request is made to the Controller:
#model CreateAccountViewModel
#using (Ajax.BeginForm(null, null, new AjaxOptions
{
HttpMethod = "Post",
Url = Model.Url,
OnBegin = #OnBeginId,
OnSuccess = #OnSuccessId,
OnFailure = #OnFailureId
}, new
{
#class = "form--tight",
data_component = "Auth",
data_auth_type = "create",
data_id = #guid,
}))
{
<fieldset>
// input fields
<div class="Auth-createAccount-submitContainer">
<p class="required">*#Model.RequiredFieldLabel</p>
<button type="submit" id="createFormSubmitBtn" class="btn btn-primary Auth-createAccount-submitButton">
</button>
</div>
</fieldset>
}

MVC Ajax Partial View call returns partialview site without "parent"

i worked on a partial reload page via Ajax.
This is what i have:
Controller:
public class EmailController : Controller
{
//
// GET: /Email/
[Authorize]
public ActionResult Index(string id)
{
//.. Some Code here to get my Email List
return View(emails);
}
[Authorize]
public PartialViewResult EmailPartial(string id = "1,2")
{
//.. Some Code here to get my Email List with the new filter
return PartialViewResult(emails);
}
}
Main View:
#model IEnumerable<namespace.Emailgesendetview>
#{
ViewBag.Title = "Index";
AjaxOptions ajaxOpts = new AjaxOptions
{
UpdateTargetId = "emailBody"
};
Layout = "~/Views/Shared/_Default.cshtml";
}
// .... uninteresting html code
<div id="emailBody">
#Html.Action("EmailPartial", new { selectedRole = Model })
</div>
// .... uninteresting html code
// Here comes my action link:
<li>
#Ajax.ActionLink("martin",
"EmailPartial",
"Email",
new { id = "3"},
new AjaxOptions()
{
HttpMethod = "GET",
AllowCache = false,
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "emailBody"
})
</li>
Partial View:
#model IEnumerable<namespace.Emailgesendetview>
<div class="mail-body">
<ul class="mail-list">
#{
foreach (var userMail in Model)
{
//... Code vor showing the models
}
}
</ul>
</div>
If i click on the link now, he starts the function: EmailPartial, but he didn't add (or replace i tryed both) the content. He Opens the EmailPartial view alone?!
In this post:
How to render partial view in MVC5 via ajax call to a controller and return HTML
the Partial function is not a PartialViewResult it's a ActionResult, but i already tryd that.
Would be great i you could show me my error or misstake.
Thanks a lot and have a nice day :)
Because you have not included jquery-unobtrusive-ajax.js in your view or layout so its making a normal redirect. – Stephen Muecke Mar 31 at 10:30

Why does Ajax.BeginForm load index again before partialview?

I am using Ajax.BeginForm to update a div with a partialview (loading logs based on the search input in the search fields).
The general idea is to load the Index the first time you log in with default values and then only update the log (partial view) when you search from there on.
The problem - When I debug my program it stops at Index in the controller before loading the partial view - resulting in long loading times.
The question - How can I make the Ajax request only load the partial view?
Code
_LogLayout.cshtml
<div id="log" class="tab">
<h1>Log</h1>
#using (Ajax.BeginForm("LogPartialView", "LogModelsController",
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "divLogs",
}, new
{
id = "NewTableId"
}))
{
<p>#Html.TextBox("SearchString", null, new { #placeholder = "Message" })</p>
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
<p>
#Html.DropDownList("SelectedCustomer", Model.LogModelVmObject.CustomerList, new { #id = "logdropdownlabel", #class = "dropdownlabels" })
</p>
}
<p>
<input type="submit" class="standardbutton logsearch" name="submit" value="Search" />
</p>
}
#using (Html.BeginForm("ExportData", "LogModels"))
{
<input type="submit" name="export" class="standardbutton export" value="Export to Excel" />
}
<div id="divLogs">
#Html.Raw(ViewBag.Data)
#Html.Partial("_LogPartialLayout")
</div>
</div>
</div>
LogModelsController.cs
/// <returns>
/// Returns the populated log with the current customers information if the user is of the Role Member,
/// otherwise if the user is in the Role Admin - then show all customers logs by default.
/// </returns>
public async Task<ActionResult> Index()
{
if (Session["myID"] == null)
return ExpireSession();
const int pageNumber = 1;
var lmvm = new LogModelVm { CurrentSort = null };
var myId = Convert.ToInt32(Session["myID"].ToString());
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
_customer = _cdvdb.GetAllCustomerIds();
_message = _db.GetLogs();
}
else if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Member"))
{
_message = _db.GetLogsById(myId);
}
var logs = _message.OrderByDescending(i => i.Timestamp).ToPagedList(pageNumber, PageSize);
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
if (_customer != null)
{
var selectListItems = _customer as SelectListItem[] ?? _customer.ToArray();
foreach (var log in logs)
log.Name = selectListItems.FirstOrDefault(a => a.Value == log.CustomerId.ToString())?.Text;
lmvm.CustomerList = selectListItems;
}
}
lmvm.Logs = logs;
var model = new LogStatisticsModel
{
LogModelObject = new LogModel(),
StatisticsModel = await StatisticsData.GetAllCurrentStatisticsValues(1, DateTime.Now),
LogModelVmObject = lmvm
};
return View(model);
}
/// <returns>
/// Returns a partial view of the log.
/// </returns>
[HttpPost]
public ActionResult LogPartialView(string searchString, int? selectedCustomer, string currentMessageFilter, string currentCustomerFilter, int? page, string sortOrder)
{
// Some code.
return PartialView("_LogPartialLayout", model);
}
RouteConfig.cs
using System.Web.Mvc;
using System.Web.Routing;
namespace MyProject
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Login", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("Log", "{controller}/{action}/{Id}");
routes.MapRoute("Admin", "");
}
}
}
Index.cshtml
#model MyProject.Models.LogStatisticsModel
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "MyPortal";
Layout = "~/Views/Shared/_LogLayout.cshtml";
}
After a long discussion with #StephenMuecke which (among a ton of useful things) told me to try and comment out every single javascript in the _LogLayout.cshtml file and one by one uncomment them back and see if any of them caused the problem.
I found that the following script caused the problem.
<script type="text/javascript">
$('#loadercheck').click(function ()
{
$('#loader').show();
$.ajax(
{
success: function ()
{
$('#loader').delay(1200).hide(400);
}
});
})
</script>
The following script was used to display a spinning loader while the log was loading data. Apparently this caused the Index() to be called again. So I will have to find a new way to display a loader now...

2 forms in 1 view with 1 controller

So here is the situation, I have a loginpartial.cshtml, which I put on the page with RenderAction
this loginpartial contains an IsAuthenticated, where if the user is not authenticated it shows the login form.
Secondly, I have a manage page which contains some fields like firstname etc.
When I push the save button, it saves it nicely to the database. However, the login form also gets called, and I have no idea why.
layout.cshtml:
#{Html.RenderAction("Login", "User");}
LoginPartial.cshtml:
<div class="nav navbar-nav navbar-right">
#if (User.Identity.IsAuthenticated)
{
<div>No login</div>
}
else
{
using (Html.BeginForm("Login", "User", FormMethod.Post))
{
#Html.AntiForgeryToken();
#Html.ValidationSummary(true, "Login failed.")
<div class="Login">
//fields here
</div>
}
}
</div>
UserController Login:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(User user)
{
if (ModelState.IsValid)
{
if (isValid(user.Email, user.Password))
{
using (MVCV2DbContext MVCV2DbContext = new MVCV2DbContext())
{
var users = MVCV2DbContext.Users.Single(u => u.Email == user.Email);
FormsAuthentication.SetAuthCookie(users.ID.ToString(), false);
//FormsAuthentication.SetAuthCookie(user.Email, false);
}
return RedirectToAction("Index", "User");
}
}
return View(user);
}
manage.cshtml:
using (Html.BeginForm("Manage", "User", FormMethod.Post))
{
#Html.AntiForgeryToken();
#Html.ValidationSummary(true, "Not all good")
<div>
//fields here
</div>
}
usercontroller manage:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(User_Details users)
{
if (ModelState.IsValid)
{
int U_ID = Convert.ToInt32(GetUserIDFromCookie());
using (MVCV2DbContext MVCV2DbContext = new MVCV2DbContext())
{
var user = MVCV2DbContext.User_Details.SingleOrDefault(u => u.User_ID == U_ID);
if (user != null)
{
user.User_FirstName = users.User_FirstName;
user.User_Insertions = users.User_Insertions;
user.User_LastName = users.User_LastName;
MVCV2DbContext.SaveChanges();
}
}
}
return View();
}
Instead of if(User.Identity.IsAuthenticated) try Request.IsAuthenticated
Also what is your debugging saying?
#{Html.RenderAction("Login", "User");}
in your layout file
will call the action method when ever the view is rendered
you can get around this by moving
if(User.Identity.IsAuthenticated) ... don't run auth code
out of the view and into the layout view, or controller (with appropriate changes to the IsAuthenticated Test)

Get Button Id on the controller using Ajax.BeginForm

The Objective:
I have a post with an Ajax.BeginForm and my objective is to get The Button Id on the controller. I've seen examples using Html.BeginForm, But I need an Ajax form,
The Code: C# MVC3
View:
#using (Ajax.BeginForm("Save", "Valoration", new AjaxOptions() { HttpMethod = "Post", UpdateTargetId = "HvmDetailTabStrip", OnSuccess = "OnSuccessSaveValoration" }))
{
<div id ="HvmDetailTabStrip">
#(Html.Partial("_ValorationDetail"))
</div>
<button type="submit" style="display:none" id="db1"></button>
<button type="submit" style="display:none" id="db2"></button>
}
Controller:
[HttpPost]
public ActionResult Save(ValorationModel model)
{
if ("db1")
{
var result = ValorationService.Save(ValorationModel);
}
else
{
// ....
}
return PartialView("_ValorationDetail", ValorationModel);
}
You can get your buttons' values like this:
#using (Ajax.BeginForm("Save", "Valoration", new AjaxOptions() { HttpMethod = "Post", UpdateTargetId = "HvmDetailTabStrip", OnSuccess = "OnSuccessSaveValoration" }))
{
<div id ="HvmDetailTabStrip">
#(Html.Partial("_ValorationDetail"))
</div>
<button type="submit" name="submitButton" value="db1"></button>
<button type="submit" name="submitButton" value="db2"></button>
}
And in your controller you can write:
[HttpPost]
public ActionResult Save(ValorationModel model)
{
string buttonValue = Request["submitButton"];
if(buttonValue == "db1"){
var result = ValorationService.Save(ValorationModel);
}else
{
....
}
return PartialView("_ValorationDetail", ValorationModel);
}
Or if count of parameters you pass in method doesn't matter, you can use this:
[HttpPost]
public ActionResult Save(ValorationModel model, string submitButton)
{
if(submitButton == "db1"){
var result = ValorationService.Save(ValorationModel);
}else
{
....
}
return PartialView("_ValorationDetail", ValorationModel);
}
Other way how you can solve your problem is here ASP.Net MVC - Submit buttons with same value

Categories