How to make a POST with a GET as the promise - c#

right now I can select a Job that is already in the database and send it to a web api controller to be exported to a PDF. I am now needing to create a Job and send it to get converted at the same time. So I need some help on how the best way to do this is? Would I need to POST it, then have a function to call a Action that GETS the newest Job created in the db? Or could I somehow turn the form into a object that can be passed to the Api controller, before it makes a POST? I would think the latter would be easier but I the Action that sends the object to the api controller is a GET call? So here is how I am sending a job that is already in the db
<div class="inline-fields">
<label>JobId:</label>
<input ng-model="currentItem.JobId" type="text">
<label>JobName:</label>
<input ng-model="currentItem.JobName" type="text">
</div>
<input ng-click="EmailPdf(currentItem)" type="button" value="Email"/>
Controller
$scope.EmailPdf = function () {
var id = $scope.currentItem.JobId
$http.get('/api/Pdf/' + id).success(function () {
$scope.PrintPreviewModal();
});
}
Api Controller
public string Get(int? id)
{
if (!id.HasValue)
{
return string.Empty;
}
JobDataAdapter adapter = new JobDataAdapter();
Job job = new Job();
job = adapter.GetJob(id);
if (job == null)
{
return string.Empty;
}
try
{
This is how I create the Job
<div class="inline-fields">
<label>Number:</label>
<input ng-model="currentItem.JobNumber" type="text">
</div>
<div class="inline-fields">
<label>Address:</label>
<input ng-model="currentItem.CustomerAddress" type="text">
</div>
<div class="inline-fields">
<label>Name:</label>
<input ng-model="currentItem.JobName" type="text">
</div>
<input type="submit" value="Save" />
<input ng-click="EmailPdf()" type="button" value="Email" />
//Post New Job
$scope.submitJob = function () {
var data = {
JobNumber: $scope.currentItem.JobNumber,
JobName: $scope.currentItem.JobName,
CustomerAddress: $scope.currentItem.CustomerAddress,
}
$http.post('/api/apiJob/PostNewJob', data).success(function (data, status, headers) {
console.log(data); window.top.location.reload();
});
};

If I understand what you're trying to do, you can make a call to your pdf function from the success of your PostNewJob call. Something like:
$http.post('/api/apiJob/PostNewJob', data).success(function (data, status, headers) {
console.log(data);
// use the new object to call the Pdf api
$http.get('/api/Pdf/' + data.id).success(function () {
$scope.PrintPreviewModal();
window.top.location.reload();
});
});

Related

Upload files using .Net 6 MVC model biding serialization by jQuery Ajax/JSON

Need to upload files using MVC model biding by AJAX jQuery/JSON.
I was uploading with a normal submit form, but now I need to change to AJAX.
How can I do this? I mean, biding using MVC and AJAX, serializing my form or something like that.
Now, my imagemPro and imagemPre, on Controller, are always 'null'.
At my View:
#model Ri.Models.Produto
<form class="settings-form" id="frmAdd" enctype="multipart/form-data">
<label for="setting-input-1" class="form-label">Título</label>
<input asp-for="#Model.TituloProduto" type="text" class="form-control" required>
<input asp-for="#Model.ImagemProduto" type="file" class="form-control" required>
<label for="setting-input-1" class="form-label">Premio</label>
<input asp-for="#Model.TituloPremio" type="text" class="form-control" required>
<input asp-for="#Model.ImagemPremio" type="file" class="form-control" required>
<input type="button" value="Criar" class="btn app-btn-primary" id="btnAdd">
</form>
#section scripts{
<script src="~/admin/js/produtoAdd.js"></script>
}
At my Controller:
[HttpPost("/api/ProdutoAdd")]
public async Task<IActionResult> ProdutoAdd([Bind("TituloProduto,ImagemProduto,TituloPremio,ImagemPremio")] Produto produto, IFormFile imagemPro, IFormFile imagemPre)
{
if (!ModelState.IsValid)
{
return Json(new { success = false, msg = "1" });
}
if (imagemPro != null)
{
var name = Path.Combine(_enviroment.WebRootPath + "/imgs", System.IO.Path.GetFileName(imagemPro.FileName));
await imagemPro.CopyToAsync(new FileStream(name, FileMode.Create));
produto.ImagemProduto = imagemPro.FileName;
}
if (imagemPro != null)
{
var name = Path.Combine(_enviroment.WebRootPath + "/imgs", System.IO.Path.GetFileName(imagemPre.FileName));
await imagemPro.CopyToAsync(new FileStream(name, FileMode.Create));
produto.ImagemPremio = imagemPre.FileName;
}
_context.Add(produto);
await _context.SaveChangesAsync();
return Json(new { success = true });
}
My script:
$(function () {
$("#btnAdd").click(function (e) {
e.preventDefault();
var _this = $(this);
var _form = _this.closest("form");
var isvalid = _form.valid();
if (isvalid) {
Create();
}
else {
//alert('false');
}
});
Create = function () {
var options = {};
options.type = "POST";
options.url = "/api/ProdutoAdd";
options.dataType = "JSON";
options.cache = true;
options.async = true;
contentType = "application/json; charset=utf-8",
options.data = $('#frmAdd').serialize();
options.beforeSend = function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val());
};
options.success = function (data) {
};
options.error = function (res) {
};
$.ajax(options);
};
});
I recommend you to create a viewModel
public class ProdutoViewModel
{
public Produto Produto {get; set;}
public IFormFile ImagemPro {get; set;}
public IFormFile ImagemPre {get; set;}
}
action ( remove Bind attribute too)
[HttpPost("/api/ProdutoAdd")]
public async Task<IActionResult> ProdutoAdd(ProdutoViewModel model )
I recommend you to use a submit button instead of ajax, it would be much easier for you
#model ProdutoViewModel
<form class="settings-form" id="frmAdd" enctype="multipart/form-data">
....
<input type="submit" value="Criar" class="btn app-btn-primary">
</form>
1.Model Binding binds the property by name attribute, your parameter name here is imagemPro/imagemPre.
2.The jQuery serialize() method will not include input file elements. So file user selected is not going to be included in the serialized value.
You need create a FormData object, append the files to that then append the form field values as well to this same FormData object. You may simply loop through all the input field and add it.
Here is a whole working demo you could follow:
#model Produto
//add `method="post"` in form tag
//otherwise it will not generate token input
<form class="settings-form" id="frmAdd" enctype="multipart/form-data" method="post">
<label for="setting-input-1" class="form-label">Título</label>
<input asp-for="#Model.TituloProduto" type="text" class="form-control" required>
<input asp-for="#Model.ImagemProduto" type="file" class="form-control" required>
<label for="setting-input-1" class="form-label">Premio</label>
<input asp-for="#Model.TituloPremio" type="text" class="form-control" required>
<input asp-for="#Model.ImagemPremio" type="file" class="form-control" required>
<input type="button" value="Criar" class="btn app-btn-primary" id="btnAdd">
</form>
#section scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/admin/js/produtoAdd.js"></script>
}
JS:
<script>
$(function () {
$("#btnAdd").click(function (e) {
e.preventDefault();
var _this = $(this);
var _form = _this.closest("form");
var isvalid = _form.valid();
if (isvalid) {
Create();
}
else {
}
});
Create = function () {
var fdata = new FormData();
var fileInput1 = $('#ImagemProduto')[0];
var file1 = fileInput1.files[0];
fdata.append("imagemPro", file1);
var fileInput2 = $('#ImagemPremio')[0];
var file2 = fileInput2.files[0];
fdata.append("imagemPre", file2);
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
var options = {};
options.type = "POST";
options.url = "/api/ProdutoAdd";
options.dataType = "JSON";
options.contentType = false; //change here...
options.processData= false; //add this...
options.data = fdata;
options.beforeSend = function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val());
};
options.success = function (data) {
};
options.error = function (res) {
};
$.ajax(options);
};
});
</script>
Your payload and your content type don't match. jQuery.serialize encodes your form data as application/x-www-form-urlencoded, but you're telling the server to expect a content type of application/json. Easiest solution is to change the content type.
Also, you might want to use the form submit event of the form, rather than a 'click' event on a button. The reason for this is that browsers will submit forms in other ways besides clicking the button, (e.g. pressing "enter" key while in a text input). Submit will handle all of these methods.
Another side note: your method of creating your options object will work, but that syntax is a little awkward. The most common "best practice" is to declare the properties inline with the object:
var options = {
type: 'POST',
url: '/api/ProdutoAdd',
// etc ...
success: function (data) {
},
// etc ...
};
That keeps you from having to type options. before every property.

TempData Dropping Data

I'm trying to persist a TempData value on a return PartialView(). It works correctly in one Post but not another and I'm stymied.
In the following action it works correctly and the value gets passed, via javascript redirect to the action that is using the value:
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult DeletetheFile(int attachmentid, string issueId)
{
string response = _adoSqlService.DeleteAttachment(attachmentid);
TempData["ID"]= issueId;
TempData.Keep();
return PartialView("_DeleteFile");
}
In the following action it is getting set properly (see the first image), but by the time it gets to the same action as the first one I showed it has changed (see second image).
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult EditFile(IFormCollection collection)
{
AttachmentModel model = new AttachmentModel();
model.attachmentId = Convert.ToInt32(collection["attachmentId"]);
model.aIssueAttachmentDescription = collection["aIssueAttachmentDescription"];
string response = _adoSqlService.EditFileDescription(model);
TempData.Remove("ID");
TempData["ID"] = collection["issueId"];
TempData.Keep();
return PartialView("_EditFile");
}
When it gets to the where I need it is now returning [string1] instead of the 20-003.
Both of the above actions run against a partial view in a modal pop-up. The following javascript captures the modal action and redirects the results to the Issue/Edit controller/action.
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
if (isValid) {
$('body').find('#modal-container').modal('hide');
window.location.href = "/Issue/Edit";
}
});
})
The redirection seems to happen since the edit action is being called in both cases. It's just that in the second case it is not getting the correct value of the TempData value. Here is the start of the Edit action which resides in a different controller than the 2 actions above:
public ActionResult Edit(string id)
{
if (id == null)
{
id = TempData["ID"].ToString();
}
----------More Comments
So after working on this for the past 4 hrs I've come up with a work-around. I'm not sure this is the correct technique or not. What I ended up doing was adding a hidden input field on the partial views and then parsing the ajax response for that value.
var issueid = $(response).find('[name="issueidSaved"]').val();
window.location.href = "/Issue/Edit/?id=" + issueid
My concern now is that the issueid is now included in the query string and is visible in the URL.
Is this the correct method or should I go back to using TempData and trying to get that to work?
UPDATE
Hopefully I can explain this better. The goal is to get a TempData value into the following action in my Issues Controller which is tied to my Edit Page (Issues/Edit):
public ActionResult Edit(string id)
{
if (id == null)
{
id = TempData["ID"].ToString();
}
I have a modal on my Edit page that is populated with different partial views depending upon what is populating it. The partial views use the Attachment controller, while the Edit view uses the Issues Controller. I use Javascript/ajax to capture the submit from the partial views to close the modal and redirect to the Edit view so a refresh of the data affected by the partial views is reflected on the Edit view. The TempData value is working correctly when I submit the _DeleteFile, but not when I submit the _EditFile partial views in the modal.
Here is the common Javascript/ajax on the Edit view:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
//Using this to scroll the page on the close of the modal/page refresh
$(document).ready(function () {
var JumpTo = '#ViewBag.JumpToDivId';
if (JumpTo != "") {
$(this).scrollTop($('#' + JumpTo).position().top);
}
});
//Using this to Capture the click that opens the modals
$('body').on('click', '.modal-link', function () {
var actionUrl = $(this).attr('href');
$.get(actionUrl).done(function (data) {
$('body').find('.modal-content').html(data);
});
$(this).attr('data-target', '#modal-container');
$(this).attr('data-toggle', 'modal');
});
//Using this to Capture the click that Submits the _EditFile,_DeleteFile,_CreateEdit forms on the modal
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
var issueid = "";
issueid = $('body').find('[name="issueidSaved"]').val();
var jumpto = $('body').find('[name="jumpto"]').val();
if (isValid) {
$('body').find('#modal-container').modal('hide');
if (issueid == "")
{
window.location.href = "/Issue/Edit/?id=" + issueid + "&jumpto=" + jumpto;
}
}
});
})
//Using this to Capture the click that Submits the _UploadFile form on the modal
$(function () {
$('body').on('click', '.fileupload', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var fdata = new FormData();
$('input[name="file"]').each(function (a, b) {
var fileInput = $('input[name="file"]')[a];
if (fileInput.files.length > 0) {
var file = fileInput.files[0];
fdata.append("file", file);
}
});
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$("form input[type='hidden']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$.ajax({
url: actionUrl,
method: "POST",
contentType: false,
processData: false,
data: fdata
}).done((response, textStatus, xhr) => {
var isValid = $(response).find('[name="IsValid"]').val() == 'True';
var issueid = $(response).find('[name="issueidSaved"]').val();
var jumpto = $(response).find('[name="jumpto"]').val();
if (isValid) {
$('body').find('#modal-container').modal('hide');
window.location.href = "/Issue/Edit/?id=" + issueid + "&jumpto="+jumpto;
}
});
})
});
$('body').on('click', '.close', function () {
$('body').find('#modal-container').modal('hide');
});
$('#CancelModal').on('click', function () {
return false;
});
$("form").submit(function () {
if ($('form').valid()) {
$("input").removeAttr("disabled");
}
});
</script>
Here is the _DeleteFile Partial view and the AttachmentController code that handles the submit:
<!--Modal Body Start-->
<div class="modal-content">
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
<input name="issueidSaved" type="hidden" value="#ViewBag.ID" />
<input name="jumpto" type="hidden" value="#ViewBag.JumpToDivId" />
<!--Modal Header Start-->
<div class="modal-header">
<h4 class="modal-title">Delete File</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<!--Modal Header End-->
<form asp-action="DeletetheFile" asp-route-attachmentid="#ViewBag.id" asp-route-issueId="#ViewBag.issueId" asp-controller="Attachment" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div class="modal-body form-horizontal">
Are you sure you want to delete the #ViewBag.title File?
<!--Modal Footer Start-->
<div class="modal-footer">
<button data-dismiss="modal" id="cancel" class="btn btn-default" type="button">No</button>
<input type="submit" class="btn btn-success relative" id="btnSubmit" data-save="modal" value="Yes">
</div>
<div class="row">
</div>
</div> <!--Modal Footer End-->
</form>
</div>
<script type="text/javascript">
$(function () {
});
</script>
<!--Modal Body End-->
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult DeletetheFile(int attachmentid, string issueId)
{
string response = _adoSqlService.DeleteAttachment(attachmentid);
ViewBag.ID = issueId;
ViewBag.JumpToDivId = "upload";
TempData["ID"]= issueId;
TempData.Keep();
return PartialView("_DeleteFile");
}
Here is the _EditFile partial view and the AttachmentController code:
Edit File
×
<form asp-action="EditFile" asp-controller="Attachment" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div class="modal-body form-horizontal">
<input name="issueId" type="hidden" value="#ViewBag.issueId" />
<input name="attachmentId" type="hidden" value="#ViewBag.attachmentId" />
<label class="control-label">#ViewBag.aFileName</label><br />
Make changes to description then select "Save Changes".<br />
<input name="aIssueAttachmentDescription" class="form-control formtableborders" id="titletext" value="#ViewBag.aIssueAttachmentDescription" />
<!--Modal Footer Start-->
<div class="modal-footer">
<button data-dismiss="modal" id="cancel" class="btn btn-default" type="button">No</button>
<input type="submit" class="btn btn-success relative" id="btnSubmit" data-save="modal" value="Save Changes">
</div>
<div class="row">
</div>
</div> <!--Modal Footer End-->
</form>
</div>
<script type="text/javascript">
$(function () {
});
</script>
<!--Modal Body End-->
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult EditFile(IFormCollection collection)
{
AttachmentModel model = new AttachmentModel();
model.attachmentId = Convert.ToInt32(collection["attachmentId"]);
model.aIssueAttachmentDescription = collection["aIssueAttachmentDescription"];
string response = _adoSqlService.EditFileDescription(model);
ViewBag.ID = collection["issueId"];
ViewBag.JumpToDivId = "upload";
TempData.Remove("ID");
TempData["ID"] = collection["issueId"];
TempData.Keep();
return PartialView("_EditFile");
}
When I inspect the TempData at the Issue/Edit action after the _DeleteFile submit it displays the following (which is correct):
When I inspect the TempData at the Issue/Edit action after the _EditFile submit it displays the following (which is incorrect):
You need to change
TempData["ID"] = collection["issueId"];
to
TempData["ID"] = collection["issueId"].ToString();
Because collection["issueId"] is Type Microsoft.Extensions.Primitives.StringValues rathar than Type String.
Here is a picture to show the Type:

Post a form to api endpoint and get the response in a javascript callback

Here is my current setup to upload a file to my webapp:
HTML:
<iframe name="myframe" id="frame1" class="hidden"></iframe>
<form target="myframe" method="post" enctype="multipart/form-data" action="/api/User/collection">
<input type="file" name="file" />
<input type="submit" value="Upload" />
</form>
Controller:
// POST api/user/collection
[HttpPost("collection")]
public ActionResult<IResponse> SetCollection(IFormFile file)
{
var collection = fileDeflator.UnzipAndGetCollection(file);
if (collection == null)
return BadRequest(new BaseResponse("The collection file is invalid."));
return base.SetUserCollection(collection);
}
It's working, except that there is no feedback at all for the client.
I would prefer that the JSON returned by the server be caught in a javascript callback on the web page (not the iframe) and be parsed to refresh a section on the page.
Is that possible with the nature of form submit ?
I ended with something working as I wanted with the helpful resources that Amy provided.
Here is the solution:
<form id="formSendCollection">
<input type="file" id="fileCollection" name="fileCollection" />
<span onclick="submitCollection();" class="button is-primary">Submit</span>
</form>
function submitCollection() {
var fdata = new FormData();
var fileCollection = document.getElementById('fileCollection').files[0];
fdata.append("fileCollection", fileCollection);
sendAjax('/api/User/Collection', fdata, function (body) {
vueApp.modelUser.collection = JSON.parse(body);
});
}
function sendAjax(url, body, callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === XMLHttpRequest.DONE) {
callback(xmlhttp.responseText);
}
};
xmlhttp.open('POST', url, true);
xmlhttp.send(body);
}

500 Interval Error MVC

i want to send my data from view to controller using ajax but I can't send the data to controller.
Thanks for your help.
That's my View code,
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>SignUp</title>
<link href="../../css/bootstrap.min.css" rel="stylesheet">
<link href="../../css/signin.css" rel="stylesheet">
<script type="text/javascript" src="../../js/jquery-1.11.0.min.js"></script>
<script type="text/javascript">
$(document).on("click", "#btnSignUp", function () {
var tcNo = document.getElementById('inputTcn').value;
var nameSurname = document.getElementById("Name").value;
var eMail = document.getElementById('Email').value;
var number = document.getElementById("PhoneNumber").value;
var secretQuestionAnswer = document.getElementById("inputSecretQuestionAnswer").value;
var password = document.getElementById('inputPassword').value;
var passwordVerify = document.getElementById("passwordVerify").value;
//var stateValue = document.getElementById("viewStates").value;
$.ajax({
type: 'POST',
url: '/Home/SignUp',
dataType: 'json',
data: {
'tcNo': tcNo,
'nameSurname': nameSurname,
'eMail': eMail,
'number': number,
'secretQuestionAnswer': secretQuestionAnswer,
'password': password,
'passwordVerify': passwordVerify,
'stateValue': stateValue
},
success: function (msg) {
alert("bsg");
},
error: function (msg) {
alert("2");
}
});
});
</script>
</head>
<body>
<div class="container">
<form class="form-signin">
<h2 class="form-signin-heading"></h2>
<input id="inputTcn" class="form-control" placeholder="T.C. NO GİRİNİZ" required="" autofocus="">
<input id="Name" class="form-control" placeholder="ADINIZI SOYADINIZI GİRİNİZ" required="">
<input id="Email" class="form-control" placeholder="E-MAIL GİRİNİZ" required="">
<input id="PhoneNumber" class="form-control" placeholder="GSM NUMARANIZI GİRİNİZ" required="">
<input id="inputSecretQuestionAnswer" class="form-control" placeholder="ÖZEL SORUNUZUN CEVABINI GİRİN">
<input type="password" id="inputPassword" class="form-control" placeholder="ŞİFRENİZİ GİRİNİZ" required="">
<input type="password" id="passwordVerify" class="form-control" placeholder="ŞİFRENİZİ TEKRAR GİRİNİZ" required="">
#Html.DropDownList("viewStates")
<a id="btnSignUp" class="btn btn-lg btn-primary btn-block btn-danger">KAYIT OL</a>
</form>
</div>
</body>
</html>
and here that's my Controller,
[HttpPost]
public ActionResult SignUp(string tcNo, string nameSurname, string eMail, string number,
string secretQuestionAnswer, string password, string passwordVerify, string stateValue)
{
return View();
}
and I add data to my Dropdownlist at here,
[HttpGet]
public ActionResult SignUp()
{
var database = new KargoDB();
List<SelectListItem> stateList = (from s in database.States
select new SelectListItem
{
Text = s.Description,
Value = (s.State_id).ToString()
}).ToList();
ViewBag.viewStates = stateList;
return View();
}
500 error code means Internal server error. That means your server side code is crashing in the HttpPost Signup method.
If you open the network tab of your browser,and click on the response of the request you made, you will be able to see the response (Exception details returned by asp.net mvc). That will help you to identify the issue.
In your HttpPost action method, you called return View() ,which is going to return the Signup.cshtml razor view. But in the signup view, similar to our GET action, It is going to look for ViewBag.viewStates to render the state dropdown. But we did not set that in our HttpPost Signup method. So it will end up with a null reference exception when razor tries to render the view.
Remember, Http is stateless. One request does not have no idea what happened in the previous request. That means, the request for HttpPost action does not have any idea of the ViewBag items you set in the previous request(GET request).
Typically, for action methods which serves ajax posts, It is good to return a JSON response.
So instead of return View(), return a json structure.
[HttpPost]
public ActionResult SignUp(string tcNo, string nameSurname, string eMail,
string number,string secretQuestionAnswer, string password,
string passwordVerify, string stateValue)
{
return Json(new {status = "success"});
}
This is going to return a json structure like
{"status" : "success"}
And you can read that in your success event handler
success: function (msg) {
console.log(msg);
if(msg.status==="success")
{
alert("Saved");
}
}
Also, looks like you have a lot of parameters being posted to the Signup method, instead of writing so many params, you should use a view model.
Here is an example on how to do that.
Instead of returning a view from the controller return a JSON result and parse the result and bind it to the dropdown.
Since you are trying to retrieve data from the HTTPGet controller, you should be using the HTTP method of Get.
[HttpGet]
public JSONResult SignUp()
{
return Json(resultset, JsonRequestBehavior.AllowGet);
}
success: function (msg) {
// parse msg and bind to dropdown
},
Try using JSON.NET
For a detailed read
As mentioned above, http 500 is because of an exception in .Net code.Are you getting this in development. If so give the details of exception. You may attached to W3Wp.exe if you are doing it in some different way.
If this is hosted in dev / QA servers, use remote debugging mechanism. In production look at windows eventviewer. Sometimes you will see what is the exception. Once the exception is visible, just google or post it here.
http://www.codeproject.com/Articles/38132/Remote-IIS-Debugging-Debug-your-ASP-NET-Applicatio

Get the raw values (not html) from AntiForgeryToken()

This beautiful abstraction lets you place #Html.AntiForgeryToken() in cshtml file which is magically expanded to something like;
<input name="__RequestVerificationToken" type="hidden" value="JjMHm5KJQ/qJsyC4sgifQWWX/WmADmNvEgHZXXuB07bWoL84DrmQzE6k9irVyFSJ5VSYqeUIXgl4Dw4NHSotLwflGYTyECzLvrgzbtonxJ9m3GVPgUV7Z6s2Ih/klUB78GN7Fl4Gj7kxg62MEoGcZw175eVwTmkKJ0XrtEfD5KCVvYIMHNY8MT2l+qhltsGL87c9dII42AVoUUQ2gTvfPg==" />
By mvc before the page is served. However my page has some JavaScript making ajax calls which don't include the token even though it's been added to the form. They are currently getting the expected [HttpAntiForgeryException]: A required anti-forgery token was not supplied or was invalid. because they don't have the token. I'm aware I could parse the value out of the DOM but I shouldn't have to. Are there other ways of accessing/getting this value? To be clear, I mean I'd like an overload of the method that returns just the value as a string or some kind of object that has the name and value both.
To provide a bit more context my form and the relevant JS looks a little like this;
<form action="/settings" method="post"><input name="__RequestVerificationToken" type="hidden" value="JjMHm5KJQ/qJsyC4sgifQWWX/WmADmNvEgHZXXuB07bWoL84DrmQzE6k9irVyFSJ5VSYqeUIXgl4Dw4NHSotLwflGYTyECzLvrgzbtonxJ9m3GVPgUV7Z6s2Ih/klUB78GN7Fl4Gj7kxg62MEoGcZw175eVwTmkKJ0XrtEfD5KCVvYIMHNY8MT2l+qhltsGL87c9dII42AVoUUQ2gTvfPg==" /> <fieldset>
<h3>User Settings</h3>
<ul>
<li>
label for="password">Password</label>
Edit
<div id="password_section" class="inlineedit">
<div>
<span for="existing_password">Current password</span> <input autocomplete="off" class="required" id="existing_password" name="existing_password" type="password" />
</div>
<div>
<span for="new_password">New password</span> <input autocomplete="off" class="required" id="new_password" name="new_password" type="password" />
<span id="password_strength" />
</div>
<div>
<span for="confirm_password">Confirm password</span> <input autocomplete="off" class="required" id="confirm_password" name="confirm_password" type="password" />
</div>
<div class="inlinesave">
<input type="button" value="Change" onclick="onPostChangePassword();"/>
Cancel
</div>
</div>
</li>
// a bunch more of these that call their own onPostChangeSetting method
onPostChangePassword() does some input validation then;
if(validPWD && validNewPWD && validConfirmPWD && current_pwd != new_pwd){
// Post the password change
var currentAjaxRequest = $.ajax({
type: "POST",
url: "/settings/preferences/changepassword",
cache: false,
data: {password: $('#new_password').val(), current: $('#existing_password').val(),confirm: $('#confirm_password').val()},
success: password_success,
error: password_error,
dataType: "json"
});
return true;
}
Which ideally (since this is verbatim in a cshtml file) would be modified with something like this;
data: {password: $('#new_password').val(), current: $('#existing_password').val(),confirm: $('#confirm_password').val(),
__RequestVerificationToken:#Html.AntiForgeryValue() }
tl;dr is there a way to interact with the AntiForgeyToken before it's turned into an string of html?
You can use code like this (in for example _Layout.cshtml) to add the AntiForgery header to all Ajax POST requests, or you could adapt it for a specific request. (Code assumes you are using jQuery)
#functions{
private static string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
<script type="text/javascript">
$(document).ajaxSend(function (event, jqxhr, settings) {
if (settings.type == "POST") {
jqxhr.setRequestHeader('#ValidateHttpAntiForgeryTokenAttribute.RequestVerificationTokenName',
'#TokenHeaderValue()');
}
});
</script>
On the server side for these Ajax calls, you then want to call the overload of AntiForgery.Validate that takes the cookie and form token, which you would enable by adding this attribute to the action methods called via Ajax (explicitly, or by parent controller, or via a filter)
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed class ValidateHttpAntiForgeryTokenAttribute
: FilterAttribute, IAuthorizationFilter
{
public const string RequestVerificationTokenName = "RequestVerificationToken";
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
ValidateRequestHeader(filterContext.HttpContext.Request);
}
else
{
AntiForgery.Validate();
}
}
private static void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = string.Empty;
string formToken = string.Empty;
var tokenValue = request.Headers[RequestVerificationTokenName];
if (string.IsNullOrEmpty(tokenValue) == false)
{
string[] tokens = tokenValue.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
Look at the Html.AntiForgeryToken() in ILSpy and copy out the code that builds an input tag and create your own extension method that returns only the token string.
Short answer: there is not a native way to do what you're trying to, but as a previous answer states, you can create an HTML helper method.
This guy seems to have a working solution: https://stackoverflow.com/a/16057568/724222

Categories

Resources