Execute a controller from view in ASP.NET MVC - Razor - c#

I am trying to print the name of the user who executes my web app. It is written in MVC-Razor.
From the initial View, I would to execute the controller below:
[Authorize]
public ActionResult Check()
{
var check = new CheckAD();
var user = new User {Name = check.CheckSecurityWithAD()};
if (!string.IsNullOrEmpty(user.Name))
{
return View("Checked", user);
}
var errors = new ErrorsModel()
{
Messages = new List<string>(){"You don't have permission"}
};
return View("Error", errors);
}
This controller returns another view if the user is correctly authenticated:
#model UsersActivationWeb.Models.User
#{
ViewBag.Title = "Checked";
}
#{ <p> Logged come #Model.Name </p>};
How can I print the second view (I think it's a partial view) in the first one using the controller?
Thanks

Sounds to me like you need an Html.Action. This will run the controller code and display the view contents that are produced where you place the call.
Most likely you will need this overload, Html.Action(string actionName, string controllerName).
Assuming the controller is called CheckController. In your initial view call it like this
#Html.Action("Check","Check")
Since you don't want people navigating to the Check view you should give it a ChildActionOnly attribute so it looks like this
[Authorize]
[ChildActionOnly]
public ActionResult Check()
{
//rest of code
}
Finally you almost certainly don't want the layout contents to appear with the Checked view so change your Checked view to this
#model UsersActivationWeb.Models.User
#{
Layout = null;
}
#{ <p> Logged come #Model.Name </p>};
Since you are doing authorization logic in the Check action you might not need the Authorize attribute. I say that because with it a user not logged in will not get the error or their name. Maybe you want this though, I'd need to know more about your code to say for sure.
This way you will either get the name of the user or the errors as required.

Related

MVC Redirect based on Controller action in Layout

Hi i am trying to add a controller to my layout that can redirect from the page based on some condition. Wondering what is the best approach. my Layout is something like this
<body class="body">
#Html.Action("IsPageNeedsRedirection", "Authentication", new { area = string.Empty })
#Html.Partial("Header", Model)
#Html.Partial("Body", Model)
</body>
Then my controller for Authentication looks like this :
public class AuthenticationController : Controller
{
public ActionResult IsPageNeedsRedirection()
{
var urlToRedirect= '/login';
var isAuthenticationRequired = // Get t/f based on current page to know if it requries authentication;
if (isAuthenticationRequired && !isLoggedIn){
return Redirect(urlToRedirect);
}
else {return null;}
}
}
This is not working because
if the condition is true redirection fails and get an exception "Child actions are not allowed to perform redirect actions" (which makes sense because the view is ready to be rendered already)
if the condition is false, still throws an exception.
What would be the ideal way to handle such authentication redirects? this happens for only some pages and i unless i know what page, i wont know if the page needs authentication or not.

Send Selected DropDownList value to HomeController ActionResult

Hi I have a drop down list that is filled in from comma delimited values in the config. This works fine.
What I am trying to do is to send the selected value on button click to a ActionResult in the HomeController.
I created a Model, which is taking a string. When I hit the button I get error:
The view 'TestAction' or its master was not found or no view engine supports the searched locations.
This is what my Controller looks like:
[HttpPost]
[ActionName("TestAction")]
public ActionResult TestAction(SQL_Blocks_App.Models.DropdownList SelectedValue)
{
//System.Diagnostics.Debug.WriteLine(SelectedValue);
return View();
}
This is what my model looks like:
public class DropdownList
{
//
// GET: /DropdownList/
[Display(Name = "Servers")]
public string SelectedValue{ get; set; }
}
and this is what my Index View looks like:
<form id="SelectedValue" action="/Home/TestAction" method="post" style="margin: 0">
<div class="col-lg-5">
#{
ViewBag.Title = "Index";
}
#Html.DropDownList("YourElementName", (IEnumerable<SelectListItem>)ViewBag.DropdownVals, "--Choose Your Value--", new
{
//size = "5",
style = "width: 600px"
})
</div>
<div class="col-lg-5">
<input type="submit" value="Run Query" />
<input id="Button2" type="button" value="Clear" onclick="window.location.reload()" />
</div>
</form>
I want to clarify. My end goal is to use the selected value in a SQL query in the ActionResult and return the results back to the index so I can fill them in a table. ( You don't have to show me how to do the SQL part for now I just would like to see the selected value at least printed in the output.)
Redirect to index action, and pass the parameters along
[HttpPost]
[ActionName("TestAction")]
public ActionResult TestAction(SQL_Blocks_App.Models.DropdownList _selectedValue)
{
//System.Diagnostics.Debug.WriteLine(SelectedValue);
return RedirectToAction("Index", "[Controller]", new {#_selectedValue = _selectedValue });
}
and then your Index method should accept the parameter.
[HttpGet]
public ActionResult Index(SQL_Blocks_App.Models.DropdownList _selectedValue)
{
//use _selectedValue
}
I would recommend using another method other than your index, or make Dropdownlist nullable/set a default for it.
The default framework behavior of return View() is to return a view with the same name as the currently-executing action. Which is TestAction. The error is telling you that no such view was found.
You have a couple of options. You can either create the view, or you can return something else. For example, if you want to redirect back to the Index then you can return a redirect result:
return RedirectToAction("Index");
You could also specify the Index view in the response:
return View("Index");
However, keep in mind that the URL will still be for TestAction and not for Index, which could result in unexpected changes to behavior if you're not aware of this.
Edit: Based on comments on this answer, it sounds like what you actually want is to build a pair of actions which generally operate on the same view. This isn't particularly common for an index view, but is very common for edit views. The only difference is semantics, structurally the concept works anywhere.
Consider two actions:
public ActionResult Index()
{
// just show the page
return View();
}
[HttpPost]
public ActionResult Index(SQL_Blocks_App.Models.DropdownList SelectedValue)
{
// receive data from the page
// perform some operation
// and show the page again
return View();
}
Requests between these two actions would differ only by the HTTP verb (GET or POST), not by the action name on the URL. That name would always be "Index". But when the form on the index view is submitted via POST and has a "SelectedValue", the second action is invoked instead of the first.
In that second action you would perform your database interaction, gather whatever data you needed, and if necessary include a model or some additional data in the response.
You TestAction method is returning to a View. Make sure View TestAction.cshtml exists and is in the Home folder.

ViewDataDictionary vs anonymous object in ASP.net MVC 5

I am a beginner and I am going through some tutorials in my MVC. So, I came across two scenarios.
Scenario 1.
I had to pass some data to my view on post and then send that data as hidden field. Here is the code.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(ForgotPasswordMV viewModel)
{
if (ModelState.IsValid)
{
return RedirectToAction("VerifyToken", new { emailId = viewModel.EmailId });
}
^^ USING ANONYMOUS OBJECTS
return View();
}
public ActionResult VerifyToken(string emailId = null)
{
VerifyTokenMV viewModel = new VerifyTokenMV
{
EmailId = emailId
};
return View(viewModel);
}
VerifyToken View
#using (#Html.BeginForm("VerifyToken", "Security"))
{
#Html.HiddenFor(m => m.EmailId)
<button class="btn btn-primary">Continue</button>
}
Works Perfectly fine. I am able to receive values of EmailId. So far so good.
Scenario 2.
Needed to open a partial view from Main view, here is the snippet.
Main cshtml file
<div class="abc">
#Html.Partial("../Widget/Customize", Model.Unit, new ViewDataDictionary() { { "ElementName", "UnitWidget" } })
</div>
partial cshtml file
#{
string custWidgetElementName = ViewBag.ElementName;
}
// some html code below
Observation:
In scenario 2 why have I used ViewDataDictionary. Although both example works perfectly fine. But is there any reason that I had to use ViewDataDictionary. In scenraio 1 can we use ViewDataDictionary? If Yes, then which one is optimum solution.
Question: When I need to pass values shall I use new {key : value} or use ViewDataDictionary or there is no corelation? Instead of ViewDataDictionary can I use anonymous object in Senario 2
Your two scenarios are totally different. They are not doing the same thing.
In scenario 1 when using this line:
return RedirectToAction("VerifyToken", new { emailId = viewModel.EmailId });
A new URL is genrated and sent back to the client (the browser) with HTTP Code 301 or 302. When received the browser will re-contact your application wiht the received URL. With that URL, your application will execute the associated action. In your case, the client's browser will call VerifyToken action with the emailId parameter setted when you call RedirectionToAction into ForgotPassword action. So using RedirectionToAction method is just telling that method to generate a new URL with parameter defined in the anonymous type.
In scenario 2 is completely different to scenario 1. With this line:
#Html.Partial("../Widget/Customize", Model.Unit, new ViewDataDictionary() { { "ElementName", "UnitWidget" } })
You're telling your view to inject the partial view which path is ../Widget/Customize. Because that partial view the strongly typed, you passed Model.Unit as an second parameter. You use also a third parameter new ViewDataDictionary() { { "ElementName", "UnitWidget" } } because your partial seems to internally need to access to the dynamic property ViewBag or dictionary property ViewData of your view.
Conclusion:
In scenario 1 you are just telling the client's browser to go to the new URL you have generated after requesting ForgetPassword URL. We just call that a rediretion.
In scenario 2, you're just rendering a partial view into a view. The client's broswer doesn't know anything what's going on with partial views they don't know if they exist.

How to create a action button which changes its color whenever underlying View has changed until ApplicationUser visits this View

In my application used only by ApplicationUser, there are no [AllowAnonymous] actions besides login.
I would like achieve behaviour known from forums but because there is going to by only one View for which I need this behaviour probably there are several solutions.
There is a view Index.cshtml for Index() action of AnnouncmentController
#model IEnumerable<WebApplication2.Models.Announcment>
#{
ViewBag.Title = "Index";
}
RENDERING ANNOUNCEMENTS
In _Layout.cshtml I would like to add:
<li>#Html.ActionLink("Home", "Announcments", "Announcment")</li>
which will change color to RED after user logs in and there is any new announcment.
Is there any quick way to do this? There will be no more buttons like this in my application.
The best way I can think of is to use child actions. First, you add a new action to your controller:
AnnouncementController
[ChildActionOnly]
[AllowAnonymous]
public ActionResult AnnouncementsLink()
{
bool hasNewAnnouncments = false;
if (User.Identity.IsAuthenticated)
{
hasNewAnnouncements = // check for new announcements for user
}
return PartialView("_AnnouncmentsLink", hasNewAnnouncements);
}
Note: [AllowAnonymous] is necessary for this child action because it's going to be used in the layout and at the very least, on the sign in page, the user will be anonymous. If you don't allow anonymous, the user will be redirected to the sign in page in order to render the child action, which would then require the user to sign in before viewing, so they would be redirected again, etc. Essentially, you end up with a nasty endless redirect that will eventually result in a 500 error.
Views\Announcement\_AnnouncementLink.cshtml
#model bool
#if (Model)
{
#Html.ActionLink("Home", "Announcements", "Announcement", new { #class = "highlight" })
}
else
{
#Html.ActionLink("Home", "Announcements", "Announcement")
}
_Layout.cshtml
<li>#Html.Action("AnnouncementsLink", "Announcement")</li>
Then, of course, you'd want to add a style declaration for .highlight:
#nav .highlight {
color: red;
}

MVC3 Model In Layout As Partial

I'm trying to utilize the MVC3 model validation in my project as of current, however I want to have a simple login section to show in the layout at all times if the user is not logged in. I have the majority of code in place, however the only thing I'm stuck on is how I can post the model back to the form for any validation messages that I produce and need to return.
Normally something like this will work:
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel)
{
if(ModelState.IsValid())
{
//Run Further checks & functions
//Upon successful login, retuns to somewhere (Just site index in this example)
return RedirectToAction("Index", "Site");
}
return View(model);
}
Now obviously this won't work as I can't return View(model); on the partial unless I just want the login form to be displayed, however I want it to post back to the page that I have been editing from. For example: I navigate to a certain page, contact us, and want to login now. I enter my details on the form that is always available and I enter my details. An error occurs (Incorrect password, incorrect login, account doesn't exist etc...) and I should be brought back to the contact page with the form still filled in with the details that I entered (except obviously password) with validation summary working etc...
Also, any forms on the page that the layout has rendered still need to work correctly (with models etc)
I'm open to suggestions on how to get this working by other means of submission/return however it would be ideal to have the MVC model validation working.
If anyone needs me to elaborate on anything said, feel free to comment. I'll be actively responding for a while.
you should create a partial view for login and instead of using "#Html.BeginForm" use #Html.AjaxBegin which submit your page by Ajax call and it RenderHtmlString of login view.
for e.g
public ActionResult Login(LoginModel)
{
if(ModelState.IsValid())
{
//Run Further checks & functions
//Upon successful login, retuns to somewhere (Just site index in this example)
return RedirectToAction("Index", "Site");
}
return RenderPartialViewToString("Login",model);
}
protected string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
after adding "RenderPartialViewToString" method which will return you "RenderHtmlString" of your partial view. you must be pass viewName and Model as parameter to this Method.
in your partail View.
<div id="targetId">
</div>
#using(Ajax.BeginForm("Login",new AjaxOptions{ HttpMethod="POST", UpdateTargetId="targetId"}))
{
<input type="submit" value="save" />
}
Note: you must be pass UpdateTargetId there your result will Append.
See this question: How do I pass value to MVC3 master page ( _layout)?
There are complete guide what to do to pass your model to layout

Categories