Can I forward to a HttpPost method without a BeginForm? - asp.net-mvc

I'm new to MVC and still haven't found a way to do this.
In the view I have this Clone button:
<a href="#Url.Action("Clone", "Game", new { pModelId = Model.Id })" class="btn btn-default btn-xs">
<i class="fa fa-plus"></i>Clone Game</a>
And in my Controller this:
[HttpPost]
public ActionResult Clone(int pGameId)
{
int lClonedGameId = mGameRepository.CloneGame(pGameId);
return RedirectToAction("Show", new { id = lClonedGameId, message = "Your game was cloned succesfully" });
}
I'm getting a 404 due to the [HttpPost] but I don't want to make it [HttpGet] since it writes to the DB. Is there a way to make that button go to the HttpPost method without a BeginForm or something like that?

If this is only a matter of having link instead of button to submit the form I suggest you adding a form and using javascript to submit a form when the link is pressed (below code assumes you are using jQuery):
#using (Html.BeginForm("Clone", "Game", FormMethod.Post, new {id = "form1"}))
{
//Your other elements go here
CloneGame
}
And hook the script:
$(function() {
$("a.cloneGameSubmit").click(function () {
$("#form1").submit();
});
});

Related

Wrong Function Being Called From MVC View

I have an MVC view, which is launched by a function in the controller. That view has a button that I want to use to submit data to a different function in that same controller, but it always go back to the function that launched it instead.
The controller is called, the ViewForPrepare view is launched from PrepareList, I hit the button on ViewForPrepare, and it submits to PrepareList again instead of RunList.
In the controller I have:
public ActionResult PrepareList(int Key)
{
return "ViewForPrepare";
}
public ActionResult RunList(int Key)
{
return "OtherView";
}
Then in the View:
<input type="button" value="Submit Report" id="submit">
<script type="text/javascript">
$(document).ready(function () {
$('#submit').click(function () { window.location ='#Url.RouteUrl("RunList", new { Key = #Model.caseNumber })' });
});
</script>
So I press the button to go to RunList, but it keeps going to PrepareList. I've checked the routing and it looks OK. What do I need to do to get the button to submit to RunList?
You're code right now basically says "When I click the submit button. Change the window's location to something else." If that is what you want, try using
#Url.Action("RunList", new { Key = Model.caseNumber })
instead of
#Url.RouteUrl("RunList", new { Key = #Model.caseNumber })
and try using a <button> element instead of an <input> element.
If what you want is to post the data from the form, you should wrap your button in a form tag (make sure to replace "ControllerName" below with your actual controller.)
#Html.BeginForm("RunList","ControllerName", new { Key = Model.caseNumber })
{
<input type="submit" value="Submit Report" id="submit">
}
and get rid of the javascript altogether as it isn't necessary in this case. Also you will have to mark your RunList action as HttpPost for this to work.
[HttpPost]
public ActionResult RunList(int Key)
{
return "OtherView";
}
Why dont you just use a RouteLink instead of the Input?
#Html.RouteLink("Submit Report", "RunList", new { Key = Model.caseNumber }, new {#class="btn" })
not sure if you're using bootstrap or jquery ui but there are css classes to make links look like buttons.
ActionLink works the same way.
#Html.ActionLink("Submit Report", "RunList", "ViewForPrepare ", new { Key = Model.caseNumber }, new { #class = "btn" })
Using VS2015 Pro I created a project using the MVC template.
HomeController.cs added:
public ActionResult PrepareList(int Key)
{
return View();
}
public ActionResult RunList(int Key)
{
return View(); ;
}
Index.cshtml added:
#Html.ActionLink("Submit Report", "RunList", new { Key = 4 }, new { #class = "btn" })
Put a break point in "RunList" and it worked!
Using
<input type="button" value="Submit Report" id="submit">
#section script{
<script type="text/javascript">
$(document).ready(function () {
$('#submit').click(function () { window.location ='#Url.RouteUrl("RunList", new { Key = 4 })' });
});
</script>
}
I got the meessage
A route named 'RunList' could not be found in the route collection.
when you need to send data, you must add the attribute HttpPost to this action:
[HttpPost]
public ActionResult RunList(int Key)
{
return "OtherView";
}

MVC RedirectToAction not working as expected

I have web page with the following HTML:
<div class="row">
#Html.ActionLink("Delete Study", "DeleteStudy", "Study", new {topic = #Model.Study.PartitionKey, subtopic = #Model.Study.RowKey}, new { #class = "btn btn-primary" })
#Html.ActionLink("View Studies", "StudyList", "Study", null, new { #class = "btn btn-primary" })
</div>
When the DeleteStudy link is clicked, the following controller method is called:
[Authorize]
public void DeleteStudy(string topic, string subtopic)
{
...
...
RedirectToAction("StudyList");
}
The DeleteStudy method is called and executes successfully, except for the Redirect. No redirect occurs. The StudyList method (which has an Authorization attribute) is never called. Am I doing something wrong?
You need to change
RedirectToAction("StudyList");
to
return RedirectToAction("StudyList");
However I recommend you make your Delete action a POST rather that a GET. You don't want this added to the browser history or allow the user to enter it in the address bar. At best it's just making an unnecessary call to delete something which no longer exists, and at worst may throw an exception depending on your code
#using (Html.BeginForm("DeleteStudy", "Study", new {topic = Model.Study.PartitionKey, subtopic = Model.Study.RowKey }))
{
#Html.AntiForgeryToken()
<input type="submit" value="Delete Study" /> // style it to look like a link if that's what you want
}
and change the method to
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult DeleteStudy(string topic, string subtopic)

ASP.NET MVC render partial from the POST method

Problem
I have Telerik TabControl and each tab content is a partial view. Everything works smoothly when request is GET:
//
// GET: /ProductVersion/Translations
public ActionResult Translations(Guid id)
{
VersionEditTabViewModel model = CreateTranslationsViewModel(id);
return PartialView("Translations", model);
}
Now the problem is that on some tabs I have a Form that has controls that trigger submit event.
[HttpPost]
public ActionResult Translations(Guid id)
{
FormCollection formCollection = new FormCollection(Request.Form);
string message = string.Empty;
int languageId = int.Parse(formCollection["TranslationsLanguageList"]);
string action = formCollection["TranslationAction"];
if(action == Constants.translation_save)
{
_translationModel.SaveTranslations(formCollection);
message = "Translation information saved";
}
else if (action == Constants.translation_language_changed)
{
/*
PROBLEM: causes whole page to render, not partial
*/
return PartialView("Translations", model);
}
return RedirectToAction( ... updates the complete page not only partial ...);
}
My question is: how to render partial from the POST method? Because now with that source code tab content will be loaded to the WHOLE page, not inside tab.
Solution
I had to put DIV outside of the Ajax.Form and also I had incorrect submit on my DropDownList. What I did was that I created hidden submit button with Id and then I used jQuery to execute it's click event.
For additional reference, please refer to this question on SO:
MVC - Using Ajax to render partial view
This shows a complete implementation of the Ajax.BeginForm with surrounding DIV and inner form controls. You should be able to place this entire setup (DIV + Form + HTML Form Elements) in the Telerik Tab, like this:
<% Html.Telerik().TabStrip()
.Name("TabStrip")
.Items(tabstrip =>
{
tabstrip.Add()
.Text("Your Tab Text")
.Content(() =>
{%>
<div id="containerDiv" align="left">
<% using (Ajax.BeginForm("Example", "Controller/Action", new AjaxOptions { UpdateTargetId = "containerDiv" })){ %>
<%-- Render Partial here -->
<% } %>
</div>
<%});
Hope that helps.
I did my trough ajax form:
using (Ajax.BeginForm("*ActionName*", new { *parameter = ID* }, new AjaxOptions { UpdateTargetId = (*div i will update*), OnSuccess = "*JavaScript that executes on success*", OnComplete = "s*ame as on success*", InsertionMode = InsertionMode.Replace }))
and then i have
return PartialView("*PartialViewName*", model);
in post Action
And it works just fine, on post, action returns partial view and then ajax form replaces the content of the div specified in the UpdateTargetId with InsertionMode.Replace

ASP.NET MVC ActionLink and post method

Can anyone tell me how can I submit values to Controller using ActionLink and POST method?
I don't want to use buttons.
I guess it has something with jquery.
If you're using ASP MVC3 you could use an Ajax.ActionLink(), that allows you to specify a HTTP Method which you could set to "POST".
You can't use an ActionLink because that just renders an anchor <a> tag.
You can use a jQuery AJAX post.
Or just call the form's submit method with or without jQuery (which would be non-AJAX), perhaps in the onclick event of whatever control takes your fancy.
You can use jQuery to do a POST for all your buttons. Just give them the same CssClass name.
Use "return false;" at the end of your onclick javascript event if you want to do a server side RedirectToAction after the post otherwise just return the view.
Razor Code
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
#Html.ActionLink("Save", "SaveAction", "MainController", null, new { #class = "saveButton", onclick = "return false;" })
}
JQuery Code
$(document).ready(function () {
$('.saveButton').click(function () {
$(this).closest('form')[0].submit();
});
});
C#
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveAction(SaveViewModel model)
{
// Save code here...
return RedirectToAction("Index");
//return View(model);
}
#Aidos had the right answer just wanted to make it clear since it is hidden inside a comment on his post made by #CodingWithSpike.
#Ajax.ActionLink("Delete", "Delete", new { id = item.ApkModelId }, new AjaxOptions { HttpMethod = "POST" })
Here was an answer baked into the default ASP.NET MVC 5 project I believe that accomplishes my styling goals nicely in the UI. Form submit using pure javascript to some containing form.
#using (Html.BeginForm("Logout", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
<a href="javascript:document.getElementById('logoutForm').submit()">
<span>Sign out</span>
</a>
}
The fully shown use case is a logout dropdown in the navigation bar of a web app.
#using (Html.BeginForm("Logout", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<div class="dropdown">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="ma-nav-text ma-account-name">#User.Identity.Name</span>
<i class="material-icons md-36 text-inverse">person</i>
</button>
<ul class="dropdown-menu dropdown-menu-right ma-dropdown-tray">
<li>
<a href="javascript:document.getElementById('logoutForm').submit()">
<i class="material-icons">system_update_alt</i>
<span>Sign out</span>
</a>
</li>
</ul>
</div>
}
ActionLink will never fire post. It always trigger GET request.
Use the following the Call the Action Link:
<%= Html.ActionLink("Click Here" , "ActionName","ContorllerName" )%>
For submitting the form values use:
<% using (Html.BeginForm("CustomerSearchResults", "Customer"))
{ %>
<input type="text" id="Name" />
<input type="submit" class="dASButton" value="Submit" />
<% } %>
It will submit the Data to Customer Controller and CustomerSearchResults Action.
This is taken from the MVC sample project
#if (ViewBag.ShowRemoveButton)
{
using (Html.BeginForm("RemoveLogin", "Manage"))
{
#Html.AntiForgeryToken()
<div>
#Html.Hidden("company_name", account)
#Html.Hidden("returnUrl", Model.returnUrl)
<input type="submit" class="btn btn-default" value="Remove" title="Remove your email address from #account" />
</div>
}
}
Use this link inside Ajax.BeginForm
#Html.ActionLink(
"Save",
"SaveAction",
null,
null,
onclick = "$(this).parents('form').attr('action', $(this).attr('href'));$(this).parents('form').submit();return false;" })
;)
My Solution to this issue is a fairly simple one. I have a page that does a customer search one by the whole email and the other by a partial, the partial pulls and displays a list the list has an action link that points to a actionresult called GetByID and passes in the id
the GetByID pulls the data for the selected customer then returns
return View("Index", model);
which is the post method
This has been a difficult problem for me to solve. How can I build a dynamic link in razor and html that can call an action method and pass a value or values to a specific action method? I considered several options including a custom html helper. I just came up with a simple and elegant solution.
The view
#model IEnumerable<MyMvcApp.Models.Product>
#using (Html.BeginForm()) {
<table>
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Quantity</td>
</tr>
</thead>
#foreach (Product p in Model.Products)
{
<tr>
<td>#p.Name</td>
<td>#p.Price.ToString()</td>
<td>#p.Quantity.ToString()</td>
</tr>
}
</table>
}
The action method
public ViewResult Edit(Product prod)
{
ContextDB contextDB = new ContextDB();
Product product = contextDB.Products.Single(p => p.ProductID == prod.ProductId);
product = prod;
contextDB.SaveChanges();
return View("Edit");
}
The point here is that Url.Action does not care whether the action method is a GET or a POST. It will access either type of method. You can pass your data to the action method using
#Url.Action(string actionName, string controllerName, object routeValues)
the routeValues object. I have tried this and it works. No, you are not technically doing a post or submitting the form but if the routeValues object contains your data, it doesnt matter if its a post or a get. You can use a particular action method signature to select the right method.
I have done the same issue using following code:
#using (Html.BeginForm("Delete", "Admin"))
{
#Html.Hidden("ProductID", item.ProductID)
<input type="submit" value="Delete" />
}
This is my solution for the problem.
This is controller with 2 action methods
public class FeedbackController : Controller
{
public ActionResult Index()
{
var feedbacks =dataFromSomeSource.getData;
return View(feedbacks);
}
[System.Web.Mvc.HttpDelete]
[System.Web.Mvc.Authorize(Roles = "admin")]
public ActionResult Delete([FromBody]int id)
{
return RedirectToAction("Index");
}
}
In View I render construct following structure.
<html>
..
<script src="~/Scripts/bootbox.min.js"></script>
<script>
function confirmDelete(id) {
bootbox.confirm('#Resources.Resource.AreYouSure', function(result) {
if (result) {
document.getElementById('idField').value = id;
document.getElementById('myForm').submit();
}
}.bind(this));
}
</script>
#using (Html.BeginForm("Delete", "Feedback", FormMethod.Post, new { id = "myForm" }))
{
#Html.HttpMethodOverride(HttpVerbs.Delete)
#Html.Hidden("id",null,new{id="idField"})
foreach (var feedback in #Model)
{
if (User.Identity.IsAuthenticated && User.IsInRole("admin"))
{
#Html.ActionLink("Delete Item", "", new { id = #feedback.Id }, new { onClick = "confirmDelete("+feedback.Id+");return false;" })
}
}
...
</html>
Point of interest in Razor View:
JavaScript function confirmDelete(id) which is called when the link generated with #Html.ActionLink is clicked;
confirmDelete() function required id of item being clicked. This item is passed from onClick handler confirmDelete("+feedback.Id+");return false; Pay attention handler returns false to prevent default action - which is get request to target. OnClick event for buttons could be attached with jQuery for all buttons in the list as alternative (probably it will be even better, as it will be less text in the HTML page and data could be passed via data- attribute).
Form has id=myForm, in order to find it in confirmDelete().
Form includes #Html.HttpMethodOverride(HttpVerbs.Delete) in order to use the HttpDelete verb, as action marked with the HttpDeleteAttribute.
In the JS function I do use action confirmation (with help of external plugin, but standard confirm works fine too. Don't forget to use bind() in call back or var that=this (whatever you prefer).
Form has a hidden element with id='idField' and name='id'. So before the form is submitted after confirmation (result==true), the value of the hidden element is set to value passed argument and browser will submit data to controller like this:
Request URL:http://localhost:38874/Feedback/Delete
Request Method:POST Status Code:302 Found
Response Headers
Location:/Feedback
Host:localhost:38874
Form Data X-HTTP-Method-Override:DELETE id:5
As you see it is POST request with X-HTTP-Method-Override:DELETE and data in body set to "id:5". Response has 302 code which redirect to Index action, by this you refresh your screen after delete.
I would recommend staying pure to REST principles and using an HTTP delete for your deletes. Unfortunately HTML Specs only has HTTP Get & Post. A tag only can a HTTP Get. A form tag can either do a HTTP Get or Post. Fortunately if you use ajax you can do a HTTP Delete and this is what i recommend. See the following post for details: Http Deletes
Calling $.post() won't work as it is Ajax based. So a hybrid method needs to be used for this purpose.
Following is the solution which is working for me.
Steps:
1. Create URL for href which calls the a method with url and parameter
2. Call normal POST using JavaScript method
Solution:
In .cshtml:
View
Note: the anonymous method should be wrapped in (....)()
i.e.
(function() {
//code...
})();
postGo is defined as below in JavaScript.
Rest are simple..
#Url.Action("View") creates url for the call
{ 'id': #receipt.ReceiptId } creates parameters as object which is in-turn converted to POST fields in postGo method. This can be any parameter as you require
In JavaScript:
(function ($) {
$.extend({
getGo: function (url, params) {
document.location = url + '?' + $.param(params);
},
postGo: function (url, params) {
var $form = $("<form>")
.attr("method", "post")
.attr("action", url);
$.each(params, function (name, value) {
$("<input type='hidden'>")
.attr("name", name)
.attr("value", value)
.appendTo($form);
});
$form.appendTo("body");
$form.submit();
}
});
})(jQuery);
Reference URLs which I have used for postGo
Non-ajax GET/POST using jQuery (plugin?)
http://nuonical.com/jquery-postgo-plugin/
jQuery.post() will work if you have custom data. If you want to post existing form, it's easier to use ajaxSubmit().
And you don't have to setup this code in the ActionLink itself, since you can attach link handler in the document.ready() event (which is a preferred method anyway), for example using $(function(){ ... }) jQuery trick.
Came across this needing to POST from a Search (Index) page to the Result page. I did not need as much as #Vitaliy stated but it pointed me in the right direction. All I had to do was this:
#using (Html.BeginForm("Result", "Search", FormMethod.Post)) {
<div class="row">
<div class="col-md-4">
<div class="field">Search Term:</div>
<input id="k" name="k" type="text" placeholder="Search" />
</div>
</div>
<br />
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-default">Search</button>
</div>
</div>
}
My Controller had the following signature method:
[HttpPost]
public async Task<ActionResult> Result(string k)

asp.net mvc ajax.BeginForm Redirecting

I dont think i quite get the Ajax functions in mvc, because i get this wierd problem.
I got the following code which makes my ajax call, it is placed in a partial view with a productList:
<% using(Ajax.BeginForm("AddToBasket", "Basket",
new { productID = item.Id },
new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "Basket", OnSuccess = "productAdded(" + item.Id + ")" })) { %>
<input type="image" src="/Content/addToCart.png" />
<% } %>
I have a <div id="Basket"></div> on my masterpage
And this method in BasketController, which returns a partial view that is found in Basket/BasketList.ascx:
[HttpPost]
public ActionResult AddToBasket(int productID)
{
// DO STUFF
return PartialView("BasketList");
}
When i am logged in using the default asp.net membership it all works fine, it updates the basket and it is all async, but when i am logged out and is clicking the addToCart, it redirects me to Basket/AddToBasket?productID=1, which is a partial view.
Does anyone know why this happens?
I have a similar problem with an ajax.actionlink
<%= Ajax.ActionLink("Gem", "SaveBasket", "Basket", new AjaxOptions { HttpMethod = "Post" })%>
where it says "The resource cannot be found." when it should fire, which is placed in the BasketController
[HttpPost]
public void SaveBasket()
{
// DO STUFF
}
It sounds like you have a javascript error somewhere that is blocking the AJAX stuff that should be happening. Can't say why that would only happen when logged out though.
Do you have any errors in the error console/firebug?
Are you sure all your pages are including the Microsoft Ajax libraries? And in the correct order?

Resources