Get the raw values (not html) from AntiForgeryToken() - c#

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

Related

How to pass textbox value using #Url.Action in ASP.NET MVC Core 2.2

In my View i have the following code:
<input type="text" id="createdDate" placeholder="dd/mm/yyyy" />
Download
In my Control i have de following code:
[HttpGet]
public async Task<IActionResult> GetRoomAccessHistory(DateTime createdDate)
{
// TO DO
}
In this particular case, i want to pass the createdDate value that is inside the textbox (createdDate) to my Url.Action(...), so it could be passed as a queryString in my URL.
This action is invoked as a GET request, and in GetRoomAccessHistory control method, i should get my createdDate.
Thank you.
PS
I think the solution should be something like this:
<a href="#Url.Action("GetRoomAccessHistory", "Files", new { createdDate = ??? })" >Download</a>
I have got a possible answer:
<form method="post" enctype="multipart/form-data" asp-action="GetRoomAccessHistory" id="formGetRoomAccessHistory">
...
<button type="button" id="downloadRoomAccessHistory"</button>
</form>
<script>
var form = document.getElementById("formGetRoomAccessHistory");
document.getElementById("downloadRoomAccessHistory").addEventListener("click", function () {
form.submit();
});
</script>
This does exactly what i want and it works, but i was trying to find a more nice solution because my experience in ASP.NET MVC is low.
You're using the wrong tool for the job.
Since the Url.Action() helper runs on the server-side, it has already executed when the page was first loaded, and generated a fixed URL which is inserted into the page's HTML. It cannot know what the user later enters into the textbox.
If you want to capture data which a user has entered, it makes more sense to use a form. In this case I've used the BeginForm tag helper to generate a suitable HTML <form> tag:
<form asp-action="GetRoomAccessHistory" asp-controller="Files" method="get">
<input type="text" id="createdDate" name="createdDate" placeholder="dd/mm/yyyy" />
<input type="submit" value="Download"/>
</form>
When submitted, this will generate a GET request to the GetRoomAccessHistory action's URL, and append createdDate as a querystring variable, using the value from the textbox.
For Get request,try to use window.location.href.
<input type = "text" id="createdDate" placeholder="dd/mm/yyyy" />
<a onclick = "navigate()" >
< input type="button" value='Download' />
</a>
<script type = 'text/javascript' >
function navigate()
{
var createdDate = document.getElementById('createdDate').value;
var url = "/Files/GetRoomAccessHistory?createdDate=" + createdDate;
window.location.href = url;
}
</script>
And your solution could be simplified to
<form method = "get" asp-controller="Files" asp-action="GetRoomAccessHistory" id="formGetRoomAccessHistory">
<input type = "text" name="createdDate" placeholder="dd/mm/yyyy" />
<button type = "button" onclick="myFunction()">Download</button>
</form>
<script>
function myFunction()
{
document.getElementById("formGetRoomAccessHistory").submit();
}
</script>

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

How to make a POST with a GET as the promise

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();
});
});

Post a new variable in the same view by Ajax && Razor

I have an asp.net mvc4 application in which i'd to use the .post ajax's function to post a variable in my view. i had this problem and i posted it here : question, so i'm now trying to solve it by the use of Ajax
View : index.cshtml
<td>
Donner votre avis.
</td>
and
<form method="Post" action="/User/Validate_Expert_Decision" target="_parent">
<span>
<b style="color:red" >
Votre justification *
<b />
<br />
<br />
<textarea rows="10" cols="75" name="justification"></textarea>
</span>
<input type="hidden" name="element" value="#Request.Params["element"]" />
<p>
<input type="submit" name="submit">
<input type="button" name="cancel" value="Annuler" onClick="closebox()">
</p>
</form>
the javascript function
function openbox2(formtitle, fadin) {
var self = $(this);
var arr = self.data('arr');
$.post("/index.cshtml", { element: arr });
var box = document.getElementById('box');
document.getElementById('shadowing').style.display = 'block';
var btitle = document.getElementById('boxtitle');
btitle.innerHTML = formtitle;
if (fadin) {
gradient("box", 0);
fadein("box");
}
else {
box.style.display = 'block';
}
}
controller : Validate_Expert_Decision
public ActionResult Validate_Expert_Decision()
{
string id_element = Request.Params["element"];
return RedirectToAction("Display_Task_List", new { id_project = id_project});
}
the problem is that i always get an empty value of id_element in string id_element = Request.Params["element"];.
What are the reasons of this error? How can i fix it?
If you want to post an ajax request you must use urls in this pattern:
/Area/Controller/Action
in your ajax request you specify your view name, but url for ajax request must be:
$.post("#Url.Action("ActionName, ControllerName, new { Area = "AreaName" }")", { element: arr });
and if you don't have area, just remove last argumant.
Instead of Request.Params["element"] you can easily get your variables by argumant in your action method:
public ActionResult Validate_Expert_Decision(string element)
** If you want to learn ASP.Net MVC i suggest you to read this book:
Pro ASP.Net MVC 4
This is one of best books that i have read until now

mvc PartialView with Dialog Partial View showing html

What I am trying to do is to open up a jquery dialog.
What is happening is that I see the following html text vs the rendering of the form when it tries to open up the PartialView:
<form action="/Plt/FileUpload" method="post"><input data-val="true" data-val-number="The field PlNum must be a number." data-val-required="The PlNum field is required." id="PlNum" name="PlNum" type="hidden" value="36028" /> <div id="errMsg" >
</div>
<p>File upload for Pl# 36028</p>
<input type="file" name="file" />
<input type="submit" value="OK" />
</form>
Here is the controller action:
public ActionResult FileUpload(int id)
{
var model = new FileUpload { PlNum = id };
return PartialView(model);
}
This is what the view looks like for the PartialView:
#model Ph.Domain.Lb.Models.FileUpload
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
#using (Html.BeginForm("FileUpload", "Plts", FormMethod.Post, null))
{
#Html.HiddenFor(a => a.PlNum)
<div id="errMsg" >
#if (TempData["ErrMessage"] != null)
{
#TempData["ErrMessage"]
}
</div>
<p>File upload for Pl# #Model.PlNum</p>
<input type="file" name="file" />
<input type="submit" value="OK" />
}
This is what my ajax call looks like:
var url = '#Url.Action("FileUpload", "Plt")' + '?id=' + encodeURIComponent(rowid);
$.ajax({
url: url,
type: 'GET',
success: function(result) {
if (result.success) {
$('#dialog').dialog('close');
} else {
// refresh the dialog
$('#dialog').html(result);
}
}
To recap, the ajax call does reach the ActionResult but not sure when it tries to show the partial view it shows HTML vs the rendered html.
The issue here is that you are trying to load razor view which has not been rendered into the dialog's innerHTML. Instead what you should be doing is setting href property of the dialog to the URL.Action link, when creating the dialog. See the link below for an example.
http://www.matthidinger.com/archive/2011/02/22/Progressive-enhancement-tutorial-with-ASP-NET-MVC-3-and-jQuery.aspx
The other option, which is not very maintainable IMO, but which will work with way you are currently doing, is to return the raw HTML from the action method.
I think the first solution is better because the controller is not polluted with HTML string concatenation.
jQuery won't let you use a script inside .html(). You can do this by two ways:
Native DOM HTML injection instead:
$('#dialog')[0].innerHTML = result;
.
Or, setting it as a data attribute and loading it manually:
In view:
<form action="/Plt/FileUpload" ...
data-script="#Url.Content("~/Scripts/jquery.validate.min.js")"
... />
In JS:
$('#dialog').html(result);
var dialogScript = $('#dialog').children().first().data("script");
if(!!dialogScript) { $.getScript(dialogScript); };
Reference: http://api.jquery.com/jQuery.getScript/
.
Another way is use the load method, as in:
$("#dialog").load(url, null, function() {
// on a side note, put $("#dialog") in a variable and reuse it
$("#dialog").dialog();
});
Reference: http://api.jquery.com/load/
.
In the very case of jQuery validation, I'd consider adding it to the parent page itself. You'd expect it to be used in fair number of situations.

Categories

Resources