How to call a POST method with multiple parameters? - c#

I'm building a MVC project and I have some problems to send the data from my view to the controller or apicontroller.
I need to call a POST method in the apicontroller with the parameters or the object;
string fname = "Mark";
string lname = "Twain";
string address = "Some street";
or
An instance of the class Person.
How can I send multiple parameters or an object?
Should I send it direct to the apicontroller or send it the original controller? I have both a Studentcontroller and an StudentInfocontroller : apicontroller
Should I use Html.ActionLink or JavaScript $.post?

You could use a post or many other ways to get the data, but if it's a form and you are using asp.net, Razor handles that for you. Html.BeginForm assumes you have a model that will be updated by the user via texboxes or other controls. Then some button click will do the call. Notice <button type="submit"> tells the form that when the button is pressed the service is to be called and the model object is posted.
This answer is the bare bones of using Html.BeginForm. You will need to dig a little deeper to get a good understanding.
#model Student
#using (Html.BeginForm("InsertStudent", "StudentInfocontroller"))
{
// style it appropriately
#Html.TextBoxFor(m => m.fName)
#Html.TextBoxFor(m => m.lName)
<button type="submit" class="btn">Submit</button>
}
Controller call.
[Route("InsertStudent")]
public async Task<IActionResult> InsertStudent(Student student)
{
// do something with the student object received. Like insert or update the database.
Repository.InsertOrUpdate(student); // assuming you have a repository.
return View("Confirmation", student); <--- let the user know?
}

Related

Passing data from one form to the action method of another form within the same view

I have a typed view called AccountSetup.cshtml with two forms:
#model ViewModel
#using (Html.BeginForm())
{
// input form stuff
<input type="submit" id="Submit"" />
}
#using (Html.BeginForm())
{
// other input form stuff
<input type="button" onclick="location.href='#Url.Action("CreateAccount", "AccountSetup")'" />
}
The first form sends the data to the this post method and that works fine. However when I submit the second form which goes to the Get method, I lose the model data. Wouldn't the TempData.Keep retain all the data for all posts back to the controller?
[HttpPost]
public ActionResult AccountSetup(ViewModel AccountInfo)
{
TempData["AccountInfo"] = AccountInfo;
// writing to database
TempData.Keep("AccountInfo");
return View(AccountInfo);
}
[HttpGet]
public ActionResult CreateAccount(ViewModel AccountInfo)
{
if (TempData["AccountInfo"] != null)
{
// functionality
var data = TempData["AccountInfo"] as ViewModel
}
return View();
}
Every request (get/put/post) is completely independent of the next (imagine your code is running on a cluster of servers), so just because you keep some data 'locally', it's not going to exist on the next call to the server (or one of the other servers in the cluster). A specific TempData instance only exists for the duration of 'AccountSetup'.
If you want something to be in the view model for CreateAccount, you will nee to use things like #Html.HiddenFor in your view.
In reality though, you would want to only store maybe the 'account id' in the view and reload the actual data from a database as you don't want to 'believe' anything the client sends you as any hacker would be able to pass whatever data they require to your server, so you MUST verify everything.

MVC - Submit button not working with multiple partial views

I'm new to MVC, so please go easy on me. I was handed a project that was started in MVC and have been told to add to it. I was fine until I got to a section that needed data from two different models. Now I'm stuck and my boss wants this done yesterday. Please help if you can.
I have a view with two partial views and a submit button. Each of the partial views use a different model. The views display information on the screen which the user can change. The submit button is then clicked. In the controller, I would like to access the data from both models (the values that the user entered on the screen). Right now, I can't even get the submit button to call the controller. That is the part I need immediate help with, but I will eventually need to know how to access both model's data from the controller.
Here's the basic idea of what I want to do:
This is my view: CreateContract.cshtml
<div class="container-fluid">
#using (Html.BeginForm("CreateContract", "CreateContract", FormMethod.Post, new { #class = "form-horizontal " }))
{
<div id="PartialDiv">
#{
Html.RenderPartial("ApplicationPartialView", new CarmelFinancialWeb.Models.ModelApplication());
Html.RenderPartial("ContractPartialView");
}
</div>
<input id="btnCreateContract" type="submit" class="btn btn-primary" value="Save" name="CreateContract" />
}
</div>
This is part of the controller CreateContractController.cs. This method is hit when the view opens and is working correctly.
[AuthorizeAdmin]
public ActionResult CreateContract(string ID)
{
ModelContract obj_Contract = new ModelContract();
obj_Contract.BuyerName = "MOCS";
return View(#"~/Views/CreateContract/CreateContract.cshtml", obj_Contract);
}
This is part of the controller CreateContractController.cs. This method is not being hit when the submit button is clicked. I've tried including the string ID variable from the other method and both ModelContract and ModelApplication (and various combinations thereof), but I cannot get this method to be called.
[AuthorizeAdmin]
[HttpPost]
public ActionResult CreateContract()
{
ModelApplication obj_App = new ModelApplication();
return View(#"~/Views/CreateContract/CreateContract.cshtml", obj_App);
}
These are the methods in the controller for the partial views. These aren't getting called either.
public PartialViewResult ApplicationPartialView(string ID)
{
ModelApplication obj_App = new ModelApplication();
if (ID != null && ID != "0" && ID != null && ID != "")
{
obj_App = objBllApplication.GetApplicationByID(int.Parse(ID));
}
return PartialView("CreateContractApplicationPartialView");
}
public PartialViewResult ContractContractPartialView()
{
ModelContract obj_Contract = new ModelContract();
obj_Contract.DealerID = "MOCS";
return PartialView("CreateContractContractPartialView");
}
There's a lot going on under the hood here which is beneficial for you to know, especially since you're new to this. First, a view can only have one model. The high-level reason is that it's actually rendered via a generic class that the chosen model fills in as it's type. You can somewhat cheat, as you are here, by using partial views, but you still have to get a model instance into those. It's seems you're trying to do that by creating actions in your controller to represent those partials, but these are effectively useless. They are never being called. Html.Partial or Html.RenderPartial will just render the specified view, either with the same model as the calling view, by default, or the object passed in to those methods as the model (second parameter). It does not go back to the controller.
In MVC, there is something called "child actions" which work as you seem to want partials to here. By using Html.Action or Html.RenderAction instead, you can call this actions on your controller that return partial views. Two things there, though:
If you're going to return a partial view (instead of a full-fledged view), then you should decorate the action with [ChildActionOnly]. Otherwise, the actions are exposed to direct URL access from the browser, which would render the partial view alone, without any layout.
Child actions can only be used for GET-style requests. You can't POST to a child action.
It's actually best for things like this, to only use child actions to render separate areas of a form if those individual areas of the form will be posted to separate locations. Take for example a combo page where you have both a login or signup concept. You can use child actions to render each individual group of fields, but the login portion should POST to a login action and the signup portion should posted to a signup action. You wouldn't POST everything from both forms to the same action.
In your particular scenario, partial views are actually the way to go, but you just need to tweak the model of your main view to be a view model that contains both sub-models. For example:
public class MyAwesomeViewModel // name doesn't matter
{
public ModelApplication Application { get; set; }
public ModelContract Contract { get; set; }
}
Then, in your main view:
#model Namespace.To.MyAwesomeViewModel
...
#Html.Partial("ApplicationPartialView", Model.Application)
#Html.Partial("ContractPartialView", Model.Contract)
Finally, your POST action would take this view model as a parameter:
[HttpPost]
public ActionResult CreateContract(MyAwesomeViewModel model)
{
...
}

MVC Restful routing and returning Views

My question is very similar to this one MVC Rest and returning views but the answer isn't working for me. I have implemented Restful Routing in my MVC application using (http://restfulrouting.com/).
When I want to add a new record the url is:
localhost/operations/1/exhibits/new
This calls the New action which returns New.cshtml as the view which contains a form. When the user submits the form and the Create action is called successfully on the Exhibits controller.
If the model state has errors I would like to return back to the New view with the date entered by the use still in place and show an error message (not implemented yet).
At present
return View("New", model)
sends back the data and renders the "New" view but the url changes to:
/localhost/operations/1/exhibits
I have checked the route values and the action being returned still is "create". I have navigation links that are driven by the action and controller values and the incorrect url means these don't get rendered properly.
Controller
public class ExhibitController : Controller
{
public ActionResult Index()
{
CreateExhibitViewModel model = new CreateExhibitViewModel();
return View(model);
}
public ActionResult New()
{
return View();
}
[HttpPost]
public ActionResult Create(MyModel model)
{
if(!ModelState.IsValid)
{
return View("New", model")
}
// Process my model
return RedirectToAction("Index");
}
}
View
#model RocketBook.Web.ViewModels.Exhibit.CreateExhibitViewModel
#{
Html.HttpMethodOverride(HttpVerbs.Put);
ViewBag.Title = "Operation " + ViewBag.OperationName;
}
<div class="panel panel-default">
<div class="panel-heading">
<h4>New Exhibit</h4>
</div>
<div class="panel-body">
<div class="col-lg-6 form-horizontal">
#using (var form = Html.Bootstrap().Begin(new Form("create", "exhibit").Id("newexhibit").Type(FormType.Horizontal).FormMethod(FormMethod.Post).WidthLg(4)))
{
#Html.AntiForgeryToken()
<fieldset>
<legend>Details</legend>
#Html.HiddenFor(m => m.OperationID)
#Html.HiddenFor(m => m.JobID)
#form.FormGroup().TextBoxFor(m => m.Barcode)
#form.FormGroup().TextBoxFor(m => m.ExhibitRef)
#form.FormGroup().TextBoxFor(m => m.ExhibitDescription)
#form.FormGroup().DropDownListFor(m => m.ClassificationGroupID, Model.ClassificationGroups).OptionLabel("")
#form.FormGroup().DropDownListFor(m => m.ClassificationID, Model.Classifications).OptionLabel("")
#form.FormGroup().DropDownListFor(m => m.ExhibitPriority, Model.EntityPriorities).OptionLabel("")
</fieldset>
<hr />
#(form.FormGroup().CustomControls(
Html.Bootstrap().SubmitButton().Style(ButtonStyle.Primary).Text("Add Exhibit")))
}
</div>
</div>
</div>
I continued this discussion on the RestfulRouting Github page at
https://github.com/stevehodgkiss/restful-routing/issues/76
For anyone who also finds this behaviour and is confused, don't be, it is in fact the correct behaviour. Here is an explanaition from Steve Hodgkiss creator of the RestfulRouting project for ASP.NET MVC
No that's expected, that's the path you go to when creating a model, and if something goes wrong it's only natural that it be halted there. When they pass validation they can move on...
A couple of solutions exist for distinguishing the URL. The HTTP Method used when calling
http://localhost/operations/1/exhibits
is a GET request and should call the Index action. If we have returned to this URL having had an error in the create action the HTTP method should be show as a POST. This can be accessed using
System.Web.HttpContext.Current.Request.HttpMethod
Another solution as suggested by Khalid is:
If you are using a ViewModel you could just flip a value on the ViewModel from inside the action. Since you are returning the model back in your Create action you can just touch a property. Might save you from having
System.Web.HttpContext.Current.Request.HttpMethod
lingering in your code.
Oh and if you put it on the viewmodel you can create a convention with a ActionFilter. If model == FlippyModel, just auto flip that property. If it fails, then that property will be true, if it passes you are moving on to the next view.
At first glance it looks like the OperationsId is not being resolved into the url / form action.
What happens when you first come to the New page is that the operationId is being passed in by ASP.NET MVC. ASP.NET MVC is being helpful by trying to find and using any old route values and plugging them into your route for you. Sounds confusing but let me explain by urls.
// url
/localhost/operations/1/exhibits/new
// on the view
Url.Action("create", "exhibits", new { /* operationId is implicit */ })
Next we do a POST to the create action.
// url
/localhost/operations/1/exhibits
// on the view
Url.Action("create", "exhibits", new { /* operationId is missing! */ })
The issue arises when you get sent back to this page because the action from above is missing the operationId. This is a symptom of the route value dictionary in ASP.NET MVC (I have no idea why it does this, but it does).
Solution:
I make all my routes explicit, don't lean on ASP.NET MVC to give you the implicit values, because it is just too hard to remember when to use them. Instead just get in the habit of always being explicit.
// on new the view
Url.Action("create", "exhibits", new { operationId = Model.OperationId })
// on the edit view
Url.Action("update", "exhibits", new { operationId = Model.OperationId, id = Model.Id })
This will work every time and you don't have to worry about whether the value you need is sitting in the route value dictionary.

How to implement custom controllers in Umbraco 6

I am having trouble getting my custom controllers to behave properly in an Umbraco MVC site. I can't work out how to pass custom models and use custom ActionResults. When I try to send a form the method is not being called. So a method like this
public ActionResult Home(string username, string password)
{
Debug.WriteLine("form");
var company = new Company();
return CurrentTemplate(company);
}
that should be called from a form like this but nothing happens.
#using (Html.BeginForm("Home","Login", FormMethod.Post, new {name = "logon"}))
{
<label>User name </label><input type="text" id="username"/><br/>
<label>Password </label><input type="password" id="password"/><br/>
<input type="submit" value="Here"/>
}
I know it is possible to override the default controller but is there any point in doing so?
Old post, but I thought I'd add my findings from a current project I'm working in.
For posts, as you're referring to in your form, you'd need a SurfaceController for most instances as #Digbyswift said.
HOWEVER - this is where I had the most trouble. I have a form that posts to another page, which displays results, and then displays that data on a view. I wanted to get away from using query strings and putting data in session, etc, since we're using MVC. This also allowed me to return a custom view that also used CurrentPage, which you need to have RenderModel as the model. We're going to need to set up a few things:
1) Create your controller, view, and document type. In this instance, I was dealing w/ "members", so I created a Member doc type, MemberController, etc.
2) On the view that's posting to the form:
<form method="post" action="member">
<div class="form-group">
<input type="text" id="query" name="query" class="form-control" placeholder="Member Company or State" />
</div>
<input type="submit" value="Search" class="button button-green" />
</form>
You could also define a custom route in a class that inherits from the ApplicationEventHandler class and register the route in the ApplicationStarted event, but this way we'll just override the Index action.
3) On the controller, MemberController
public class MemberController : RenderMvcController
{
[EnsurePublishedContentRequest(2303)] // 2303 is your node ID
public ActionResult Index(string query)
{
// your code - assign to ViewBag
return View("~/Views/Member.cshtml", this.CreateRenderModel(Umbraco.TypedContent(2303)));
}
}
private RenderModel CreateRenderModel(IPublishedContent content)
{
var model = new RenderModel(content, CultureInfo.CurrentUICulture);
//add an umbraco data token so the umbraco view engine executes
RouteData.DataTokens["umbraco-doc-request"] = model;
return model;
}
You DO NOT have to do this if you do not have any macros that need to rendered. In my case I had this view that inherited a _layout.
Custom MVC Routes in Umbraco
We have to do (2) things here. One is to make sure that we get the PublishedContentRequest.
The second part is to get the RenderModel object. I read a lot of articles in which your base model should inherit from RenderModel, and add in some default Umbraco constructors, but I didn't have very much luck with this.
Hope this helps somebody.
In Umbraco, every request is routed through the Umbraco.Web.Mvc.RenderMvcController but you can override this and the documentation is here.
However, I would suggest that if you feel you need to do this then you are possibly over-complicating your implementation. You can still use your approach to render ChildActions which can be given a model independent of the Umbraco page model. See here. This is great for things like rendering paged search results, document listings and content you want to be able to control in a controller. I use this approach a lot but always try and pass back a model centered around the IPublishedContent interface (e.g. IEnumerable<IPublishedContent> for a page listing), that way in the View, you can still have access to the Umbraco content and API from the View instead of having to implement too many of your own properties in a model.
When it comes to posting forms it's a little more tricky because you have two options:
As Dan Diplo says, you can use the Html.BeginUmbracoForm() approach; or
You can post back to a [HttpPost] action in the standard MVC way, i.e. Html.BeginForm().
The challenge with (2) is that because all requests are passed through Umbraco.Web.Mvc.RenderMvcController, you cannot tell a form which page to post to. You can only post to itself or a non-Umbraco-controlled action. You could for example let the form post back to the same page and have a second ChildAction specifically for the [HttpPost]. The issue with this is that it would catch all posts regardless of the form being posted from.
Personally, I use approach (1) in most standard forms where I need to interact with Umbraco directly, e.g. enquiries, uploads etc. and I use approach (2) when I need more control. But this generally needs a lot more thought.
Do you really mean you want a custom controller, or do you actually just want to create a form in Umbraco using MVC? If it's the latter then you need to use a surface controller ie. ensure your controller inherits from Umbraco.Web.Mvc.SurfaceController and has the suffix 'SurfaceController'.
public class MySurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult Index()
{
return Content("hello world");
}
}
You then need to use the custom Umbraco Html helper to create your form tags:
#using(Html.BeginUmbracoForm("CreateComment", "BlogPostSurface"))
{
//
}
See http://our.umbraco.org/documentation/Reference/Mvc/forms

MVC C# - Simplest Possible Implementation

My first try of MVC. Am trying to implement a simple example. Inspiration from here. Have I got this pattern (yet!)?
View: "Hey, controller, the user just told me he wants the first person"
Controller: "Hmm, having checked his credentials, he is allowed to do that... Hey, model, I want you to get me the first person"
Model: "First person... got it. Back to you, Controller."
Controller: "Here, I'll collect the new set of data. Back to you, view."
View: "Cool, I'll show the first person to the user now."
View:
namespace WinFormMVC
{
public partial class Form1 : Form
{
controller cont = new controller();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = cont.checkPermissionsAndGetFirstPerson();
}
}
}
Controller:
public class controller
{
public string checkPermissionsAndGetFirstPerson()
{
string returnValue = "";
if (checkPermissions())
{
model m = new model();
returnValue = m.getFirstPerson();
}
return returnValue;
}
public bool checkPermissions()
{
return true;
}
}
Model:
public class model
{
public string getFirstPerson()
{
return "Bill Smith";
}
}
Hmm... I am not sure if I'd call this MVC... As with ASP.NET WebForm, this form is more like an MVP pattern.
As per my understanding, in MVC, controller is the one responsible for managing all resources and flow of the code. In your example, you basically creating a Windows Form first (the view) and then attach a controller to it which is more of a MVP sort of things.
In a classical MVC pattern, the Model, once instantiated, will be linked to the View and when the model changes, the view will get notified (possibly through Observer / PubSub pattern).
Button click, etc. from the View will be routed to the controller which will coordinate those sort of stuffs.
see: this.
I would describe MVC more like this:
Request (MVC url routing, some event passed from previous UI etc)
Controller - check credentials, get data, return Model
Model - represents the data passed back from the Controller
View - render the Model returned by the Controller. Depending on the Model may display UI to initialise new Controller actions. May also pass Model back to next Controller action.
I think it can be a little confused because in many Model implementations (such as Linq) they provide data definition and access, but it's still the Controller that knows where to start (even if it's the Model that knows how to save its own changes).
So, your code should be something like:
//Controller:
public class PersonController
{
public PersonAction Detail(int personId)
{
Person returnValue;
//get person from DB and populate returnValue
return new PersonAction( returnValue );
}
}
//Model:
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
}
//View:
public partial class PersonDetailView : MVCForm<Person>
{
public Form1( Person model ):base(model) {
textBox1.Text = model.FirstName + " " + model.LastName;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = model.FirstName + " " + model.LastName;
}
}
What this example is missing is the framework that makes this all possible - there are two significant parts to that:
Something that takes/parses parameters and based on that calls a controller's action method. For instance in Asp.net MVC this is the routing handlers - the call above would be the request url: ~/Person/Detail/personId
Something that takes the result from the action (PersonAction in the example above) and finds the correct view to display. In this example it would open a PersonDetailView form and pass the Person model to it.
There are lots of example frameworks for an MVC implementation for WinForms - one of them may be a good starting point.
To adopt the MVC pattern you want to implement the following:
The view hooks up to the Model and listens for changes.
The controller hooks up to the view and handles specific events i.e. button presses etc
The model processes requests made by the controller and notifies the view.
I agree with Jimmy you would want to integrate something like the Observer pattern to this type of system so its possible for the Model to inform the View when it changes so the View can then update itself accordingly.
The difference with MVP is it introduces a Presenter class which monitors the Model on behalf of the View, in other words the View doesn't know about the Model, it only knows about its Presenter. The Presenter responds to changes in the Model and updates the View accordingly.
I think there are a few corrections to be made to your narrative. Strinctly speaking the view does not contact the controller, the user contacts the controller directly. In a web app it looks like this.
User: Hey web app, can I have the resource at /people/1
Routing engine: That means you person controller. get the first user.
User controller: Model, I need you to get me user record for the authenticated user, and the person record for the first person.
User Controller: Right. I know the authenticated user is an admin, and person one does exist. So return the Admin view for person to the user.
Rendering engine: Build up the html from the view (template) and person object (data)
This is difficult to do in webforms. I guess the best way is to have a page for each controller, and have that page dynamiclly choose and display a user control. One for each view. It is important in MVC that the view is dumb. All requests are handled directly by controllers, which then select which view to use.
Your checkPermissionsAndGetFirstPerson method is probably doing too much. Authorization and fetching data should probably be separate operations.
Also, if your Form1 class is your view, it probably shouldn't be constructing the controller. That seems backwards.
Most information you'll find on MVC in .NET will be for ASP.NET MVC web applications. You might want to check out the WinFormsMVC project on CodePlex to see how someone else has tackled this problem.
http://winformsmvc.codeplex.com/
Here's another WinForms MVC implementation on CodePlex. Looks like it's got a bit more documentation.
http://koosserymvcwin.codeplex.com/
You might find this tutorial very helpful: http://nerddinnerbook.s3.amazonaws.com/Intro.htm. Written by Scott Guthrie, it explains the MVC workflow very well. Part 4 should have the basics you're looking for.

Categories

Resources