I have a form that that is not being passed to my controller.
It is hitting the controller method, but fails in passing any parameters, 'jsonString' is always NULL.
** Note: I have also tried using #razor w. model binding but was having the same issues.
Form:
<form id="CarreersForm">
<div class="form-group">
#Html.Label("Name")<br />
#Html.TextBoxFor(m => m.name)
</div>
<div class="form-group">
#Html.Label("Phone")<br />
#Html.TextBoxFor(m => m.phone)
#Html.ValidationMessage("Phone", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.Label("Email")<br />
#Html.TextBoxFor(m => m.email)
</div>
<div class="form-group">
<input type="file" id="filepath" name="filepath" value="Upload Resume" class="btn btn-default" />
<input type="submit" name="submit" id="submit" value="submit" />
</form>
JS/AJAX:
<script>
$(function () {
$("#submit").click(function (e) {
e.preventDefault();
var formData = {
"name": $("#CarreersForm #name").val(),
"phone": $("#CarreersForm #phone").val(),
"email": $("#CarreersForm #email").val(),
"filepath": $("#CarreersForm #filepath").val(),
};
var jsonString = JSON.stringify(formData);
console.log(jsonString);
$.ajax({
type: 'POST',
data: jsonString,
url: "#Url.Action("CarreersForm")",
dataType: "json",
contentType: "application/json; charset=utf-8",
cache: false,
success: function (response) {
if (response == 'True') {
alert("The form was successfully submitted.");
$("#contactUs form input").val("");
}
if (response == 'False') {
alert("There was an error submitting the form. Fill in all of the form fields with no errors.");
}
},
error: function (response) {
alert("There was an error submitting the form. Fill in all of the form fields with no errors.");
}
});
});
});
</script>
Controller:
//Current Method
[HttpPost]
public bool CarreersForm(string jsonString)
{
return false;
}
Copy of HTTP POST
NEW REQUEST
POST http://localhost:51721/Transportation/CarreersForm
REQUEST HEADERS:
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:51721/Transportation/Careers
Content-Length: 95
Cache-Control: no-cache
REQUEST BODY
{"name":"Mark","phone":"123456789","email":"email#emailaccount.com","filepath":"logo_main.png"}
CareersModel:
public class CareersModel
{
[Required]
public string name { get; set; }
public string job { get; set; }
[Required]
[EmailAddress]
public string email { get; set; }
[Phone]
public string phone { get; set; }
[Required]
public HttpPostedFileBase filepath { get; set; }
}
I have also tried passing the model directly:
[HttpPost]
public bool CarreersForm(ContactForm model)
{
if (!ModelState.IsValid){
return false;
}
return true;
}
I'm fairly certain that this is because you haven't set the correct encoding type on your form. Change it to the following to allow uploading files, this'll allow successful submission of your model.
<form id="CarreersForm" enctype="multipart/form-data">
Also: You won't be able to upload your documents using a jQuery AJAX request like that. It'd have to be either a standard post request, or using a plugin that allows ajaxified file uploads.
var formData = new FormData($("#CarreersForm")[0]);
use in ajax to data pass "data: jsonString,"
e.preventDefault() is the problem. It should come last with respect to your code arrangement as it only prevents the default action of the form from following the link provided in the form action attribute.
e.preventDefault() can prevent your form from passing the form parameters because it returns false and the rest of your commands have not been executed.
Also remember that e.preventDefault() method is supposed to be called on the form object itself, wherein you called it on the click event handler of the submit button.
Also setting request headers can prevent your Ajax from passing the form parameters to the PHP processing file.
Related
I want to retrieve the response from my ASP.NET Core Controller through AJAX. Here's the example of my code
public IActionResult Submit(ViewModel model) {
var isValid = true;
if (isValid) {
return Json(new {
success = true
});
}
return Json(new {
success = false
});
}
CSHTML part
<form asp-action="Submit" asp-controller="Home" id="formSubmit" name="formSubmit" method="post" enctype="multipart/form-data">
<input type="text" id="Name" name="Name">
<input type="text" id="Address" name="Address">
<input type="text" id="JobDescription" name="JobDescription">
</form>
$("#formSubmit").on('submit', function(e) {
var datas = {
Name: $("input[name='Name']").val(),
Address: $("input[name='Address']").val(),
JobDescription: $("input[name='JobDescription']").val()
};
var formAction = $(this).attr("action");
$.ajax({
method: "POST",
url: formAction,
data: JSON.stringify(datas),
dataType: "json",
contentType: 'application/json',
success: function(response) {
if (response.success) {
alert("Test");
return true;
} else {
alert("Invalid/Error");
e.preventDefault();
}
});
});
The problem in this code it redirect/loading to page showing the {"success":false}.
My ViewModel
public class ViewModel{
public string Name { get; set; }
public string Address { get; set; }
public string JobDescription { get; set; }
}
it seems there are a few issues present here
<form asp-action="Submit" asp-controller="Home" id="formSubmit" name="formSubmit" method="post" enctype="multipart/form-data">
You are specifying asp-action and asp-controller. Omit all of these properties. Start with just:
<form>...</form>
The reason being, when you set those attributes it uses the old-school form submission mechanism which redirects (one of the side affects you listed).
Also the name type seems to be a mismatch, you use ViewModel but in your example the type name is TFAViewModel
Try the following, above your controller (for each method) or above the method itself add
[Consumes("application/json")]
[Produces("application/json")]
public IActionResult Submit([FromBody]ViewModel model)
{
ModelState.IsValid; //use to inspect. You will also see any violations
....
}
In your JS code ensure to do the following (as has been commented)
e.preventDefault(); //stops redirect
Have you use [HttpPost] attribute on the Submit action? You need to set a specific url like "/Home/Submit" and have a reference to jquery.
Action:
[HttpPost]
[Consumes("application/json")]
[Produces("application/json")]
public IActionResult Submit(ViewModel model)
{
var isValid = true;
if (isValid)
{
return Json(new
{
success = true
});
}
return Json(new
{
success = false
});
}
View:
<form id="formSubmit" name="formSubmit" method="post" enctype="multipart/form-data">
<input type="text" id="Name" name="Name">
<input type="text" id="Address" name="Address">
<input type="text" id="JobDescription" name="JobDescription">
<input type="submit" value="Create" class="btn btn-default" />
</form>
#section Scripts{
<script src="~/lib/jquery/dist/jquery.js"></script>
<script>
$("#formSubmit").on('submit', function (e) {
var datas = {
Name: $("input[name='Name']").val(),
Address: $("input[name='Address']").val(),
JobDescription: $("input[name='JobDescription']").val()
};
e.preventDefault();
//var formAction = $(this).attr("action");
$.ajax({
method: "POST",
url: "/Home/Submit",
data: JSON.stringify(datas),
dataType: "json",
contentType: 'application/json',
success: function (response) {
if (response.success) {
alert("Test");
return true;
} else {
alert("Invalid/Error");
// e.preventDefault();
}
}
});
});
</script>
}
Now it only get the AuctionId value in to the controller the other props are either null or 0...
Here is the form:
<form id="createBid">
<div id="frmBid" class="form-inline">
<input name="Bidder" asp-for="#bidModel.Bidder" value="#User.Identity.Name" type="hidden" />
<input name="AuctionId" asp-for="#bidModel.AuctionId" value="#Model.AuctionId" type="hidden" id="auctionId" />
<label asp-for="#bidModel.Amount" />
<input name="Amount" asp-for="#bidModel.Amount" />
<button type="submit" id="submitBtn" class="btn btn-primary">Lägg</button>
</div>
</form>
Here is the action in the controller:
public async Task<IActionResult> AddBid(BidModel Bid)
{
var result = await _bidBusinessInterface.CreateBidAsync(Bid, Bid.AuctionId);
if (result)
{
ViewBag.Message = "Bud lagt!";
}
else
{
ViewBag.Message = "Bud förlågt!";
}
return RedirectToAction("ViewDetails");
}
And then we have the actual AJAX call:
$('#createBid').on('submit', function (e)
{
e.preventDefault();
var $form = $(this);
$.ajax({
url: '#Url.Action("AddBid")',
type: 'POST',
dataType: 'html',
data: $form.serialize(),
success: function (html)
{
$('#frmBid').html(html);
}
});
});
I'm posting the model here aswell, if it is needed to see where it goes wrong:
public class BidModel
{
[JsonProperty("BudID")]
public string BidId { get; set; }
[JsonProperty("Summa")]
public int Amount { get; set; }
[JsonProperty("AuktionID")]
public string AuctionId { get; set; }
[JsonProperty("Budgivare")]
public string Bidder { get; set; }
}
I'm very grateful for every answer! This has been bugging me for 2 hours..
You are using your asp helpers wrong.
let's take this sample of code:
<input name="Bidder" asp-for="#bidModel.Bidder" value="#User.Identity.Name" type="hidden" />
you set the name to "Bidder" this is what the data will be bound to, when you need "Bid.Bidder" since "Bid" is the name of the object that the action receives.
Now you DO NOT need to set the name attribute since asp-for helper does it for you, it will automatically generate name, id attributes for you.
now make sure at the top of you page you have #model YourNamespace.BidModel
and reference it like so:
<input asp-for="#Model.Bidder" value="#User.Identity.Name" type="hidden" />
the attributes will be generated automatically and the model should be bound correctly.
I'm unable to pass the RequestVerificationToken from webpage to server using AngularJs.
My AngularJs Code is:
var app = angular.module('validation', []);
app.controller('SignUpController', function ($scope, $http) {
$scope.model = {};
$scope.email = {};
$scope.sendEmail = function () {
$http({
method: 'POST',
url: '/Contact/Test',
data: $scope.email,
headers: {
'RequestVerificationToken': $scope.antiForgeryToken
}
}).success();
};
});
Custom Attribute Code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
private void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = String.Empty;
string formToken = String.Empty;
string tokenValue = request.Headers["RequestVerificationToken"];
if (!String.IsNullOrEmpty(tokenValue))
{
string[] tokens = tokenValue.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
try
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
ValidateRequestHeader(filterContext.HttpContext.Request);
}
else
{
AntiForgery.Validate();
}
}
catch (HttpAntiForgeryException e)
{
throw new HttpAntiForgeryException("Anti forgery token cookie not found");
}
}
}
Form is:
#functions{
public string GetAntiForgeryToken()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
<div ng-app="validation" ng-controller="SignUpController">
<form role="form" id="frmContact" action="#Url.Action("Index", "Contact")" method="POST">
<input id="antiForgeryToken" ng-model="antiForgeryToken" type="hidden" ng-init="antiForgeryToken='#GetAntiForgeryToken()'" />
<fieldset class="form-group">
#Html.LabelFor(x => x.EmailTitle)
#Html.TextBoxFor(x => x.EmailTitle, new { placeholder = #Resource.EmailTitle, #class = "form-control", data_ng_model = "new.email.title" })
</fieldset>
<fieldset class="form-group">
#Html.LabelFor(x => x.EmailAddress)
#Html.TextBoxFor(x => x.EmailAddress, new { placeholder = #Resource.EmailAddress, #class = "form-control", data_ng_model = "new.email.address" })
</fieldset>
<fieldset class="form-group">
#Html.LabelFor(x => x.EmailMessage)
#Html.TextAreaFor(x => x.EmailMessage, new { placeholder = #Resource.EmailMessage, #class = "form-control", data_ng_model = "new.email.message" })
</fieldset>
<div>
<button type="submit" name="btnEmailForm" id="btnEmailForm" class="btnLogin" ng-click="sendEmail()" value="sendMessage">#Resource.ContactFormSendMessageButton</button>
</div>
<div id="errorMessages" class="error">{{message}}</div>
</form>
</div>
I have read the following posts, but cannot seem to solve the problem, and also took code from https://github.com/techbrij/angularjs-asp-net-mvc which works in that example but not in my MVC application:
http://techbrij.com/angularjs-antiforgerytoken-asp-net-mvc
https://parthivpandya.wordpress.com/2013/11/25/angularjs-and-antiforgerytoken-in-asp-net-mvc/
AngularJS Web Api AntiForgeryToken CSRF
http://bartwullems.blogspot.co.uk/2014/10/angularjs-and-aspnet-mvc-isajaxrequest.html
Where exactly to put the antiforgeryToken
http://www.ojdevelops.com/2016/01/using-antiforgerytokens-in-aspnet-mvc.html
Can anyone help with this problem
At this case you perform form submit and $scope.sendEmail operations and they may conflict one with another, to prevent this behavior you can use ng-submit directive. And also add attributes: name= '__RequestVerificationToken' and ng-value="antiForgeryToken" to corresponding input.
Important: Default behavior of [ValidateAntiForgeryToken] expects __RequestVerificationToken token in form values. To send request to server in form values format one requires content-type to be set to application/x-www-form-urlencoded. But I didn't have this option unfortunately and my content-type was application/json. Hence I took this custom path.
Let me explain the approach I took which worked.
Step 1: Declare #Html.AntiForgeryToken() in your view (.cshtml) as shown below:
<form id="inputForm" name="inputForm" ng-submit="submit(broker)" novalidate>
#Html.AntiForgeryToken()
/* other controls of form */
</form>
Step 2: #Html.AntiForgeryToken() will render an hidden field which will hold the token value as:
<input name="__RequestVerificationToken" type="hidden" value="GvTcz2tTgHOS2KK_7jpHvPWEJPcbJmHIpSAlxY1">
Step 3: Create custom attribute for Anti-forgery token verification as
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class HbValidateAntiForgeryToken : FilterAttribute, IAuthorizationFilter, IExceptionFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
try
{
var antiForgeryCookie = filterContext.HttpContext.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(antiForgeryCookie != null ? antiForgeryCookie.Value : null,
filterContext.HttpContext.Request.Headers["__RequestVerificationToken"]);
}
catch (Exception ex)
{
throw new SecurityException("Unauthorised access detected and blocked");
}
}
public void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception != null &&
filterContext.Exception is System.Security.SecurityException)
{
filterContext.Result = new HttpUnauthorizedResult();
// Handle error page scenario here
}
}
}
Step 4: Declare the above attribute wherever required (Only on HttpPost methods of controller. DO NOT declare on HttpGet)
[HttpPost]
[HbValidateAntiForgeryToken]
public JsonResult IsUsernameExists(string username)
{
}
Step 5: In AngularJS, in factory pass __RequestVerificationToken as header.
hbServices.factory('RegistrationService', ['$resource',
function ($resource) {
return $resource(applicationPath + 'api/MyUserMembership/:dest', {}, {
createNewUser: { method: 'POST', isArray: false, params: { dest: 'CreateNewUser' },
headers: {
'__RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
}
},
isUsernameExists: { method: 'POST', isArray: false, params: { dest: 'IsUsernameExists' },
headers: {
'__RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
}
}
});
}]);
Please note the way I am passing value of __RequestVerificationToken read from hidden field that was rendered by ASP.NET MVC's #Html.AntiForgeryToken().
My application was using jquery and has reference of jquery already so reading the value was easy. You can try other methods to read this value
Summary
The AntiForgery.Validate() does the magic of validating the value of forgery token here and is wonderful so far. Hope this helps!
this is the HTML and JS I have:
</form>
<div class="form-group">
<div class="col-md-4">
<button id="previewButton" class="btn btn-primary">Preview</button>
</div>
</div>
</form>
<script>
$("#previewButton").click(function () {
var request = $.ajax({
url: "/Product/GetRules/",
method: "GET",
dataType: "JSON"
});
request.done(function (data) {
console.log(data);
});
request.fail(function (jqXHR, textStatus) {
console.log(textStatus);
alert("Request failed.");
});
});
</script>
This is the controller code:
public ActionResult GetRules()
{
ProductVM sVM = new ProductVM();
sVM.ProductId = "test123";
sVM.Rules = new List<string>() { ""20/2/15"", "10/2/15" };
return Json(sVM, JsonRequestBehavior.AllowGet);
}
and the Model:
public class ProductVM
{
public string ProductId { get; set; }
public List<string> Rules { get; set; }
}
It hits the controller on debugging and I can see the correct object in VS.
I placed an alert in the .done promise and it works by showing an alert with [object object]
so that means the AJAX call is working fine.
I want to look at the returned JSON so added the log statement but the browser is re-loading the page after the call and there is nothing in the console.
What am I doing wrong here?
Any help is greatly appreciated.
Thanks in advance.
The form is submitting because you need to cancel the default behaviour:
$("#previewButton").click(function (event) {
event.preventDefault();
event.preventDefault()
Description: If this method is called, the default action of the event will not be triggered.
<form> <!-- </form> replaced with <form> -->
<div class="form-group">
<div class="col-md-4">
<button id="previewButton" class="btn btn-primary">Preview</button>
</div>
</div>
</form>
<script>
$("#previewButton").click(function(e) {
e.preventDefault();/* Added prevention */
var request = $.ajax({
url: "/Product/GetRules/",
method: "GET",
dataType: "JSON"
});
request.done(function (data) {
console.log(data);
});
request.fail(function (jqXHR, textStatus) {
console.log(textStatus);
alert("Request failed.");
});
});
</script>
I have a checkbox list and dropdown list. I want to pass selected value from dropdownlist and checked checkboxes to my MVC3 Controller and use model binding.
ViewModel for checkbox:
public class StateViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
public IEnumerable<TransitionViewModel> Transitions { get; set; }
}
Editor template for StateViewModel /Views/Home/EditorTemplates/StateViewModel.cshtml:
#model Mcs.Sibs.UI.Web.Models.StateViewModel
#Html.HiddenFor(x => x.Id)
#Html.HiddenFor(x => x.Name)
<div>
#Html.CheckBoxFor(x => x.Checked)
#Html.LabelFor(x => x.Checked, Model.Name)
</div>
My view:
#using (Html.BeginForm("GetSchedules", "Home", FormMethod.Post, new { id = "checkboxFrm" }))
{
#Html.EditorForModel()
<input id="ShowReportScheduleBtn" type="button" value="Show schedule" onclick="ShowReportSchedules()" />
}
Button click handler which is sending data to controller:
function ShowReportSchedules() {
var selectedGenerationId = $('#SelectedGenerationId').val();
var statesData = JSON.stringify($('#checkboxFrm'));
var statesData2 = $('#checkboxFrm').serialize(); //I also tried this
$.ajax({
type: 'POST',
url: '/Home/GetSchedules',
data: { "generationId": selectedGenerationId, "states": statesData },
dataType: "json",
contentType: 'application/json; charset=utf-8'
});
};
Finally, my controller:
[HttpPost]
public ActionResult GetSchedules(int generationId, IEnumerable<StateViewModel> states)
{
return View("Index");
I can't pass values to my controller. I've managed to pass only statesData object without jQuery and using type="submit" in my form. When I tried to pass only statesData with jQuery and type="button" I got "Invalid JSON primitive" error in FireBug.
When I tried to pass integer + statesData object, IE 9 is crashing and Firefox hangs.
I've tried various solutions but without success.
Try like this:
function ShowReportSchedules() {
var selectedGenerationId = $('#SelectedGenerationId').val();
$.ajax({
type: 'POST',
url: '#Url.Action("getschedules", "home")?generationId=' + selectedGenerationId,
data: { states: $('#checkboxFrm').serialize() },
success: function(result) {
// Do something with the results
}
});
};
var a = $("#a").serialize();
var b = $("#b").serialize();
var c = $("#c").serialize();
$.ajax({
url: '#Url.Content("~/Controller/Method1")',
type: 'POST',
data: a+b+c,
success: function (success) {
// do something
}
});
// in Controller
[HttpPost]
public ActionResult Method1(abc a, bcd b, xyz c)
{
}
// where abc, bcd xyz are class