Asp.Net Core Unobtrusive Ajax throwing 400 error on live server - asp.net-mvc

My Every form is of type
<form id="addform" asp-area="Admin" asp-controller="Departments" asp-action="Add" data-ajax="true" data-ajax-method="post" data-ajax-success="onSuccess" data-ajax-failure="onFailure" data-ajax-begin="onBegin">
</form>
I have also appended RequestVerificationToken on every ajaxSend request
$(document).ajaxSend(function (e, xhr, options) {
debugger;
if (options.type.toUpperCase() == "POST") {
var token = $("input[name='__RequestVerificationToken']").val();
xhr.setRequestHeader("__RequestVerificationToken", token);
}
});
My controllers are like this
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Add(DepartmentViewModel departmentViewModel)
{
return View();
}
Now, it is working fine locally but not working correctly on the live server.
Some requests are working fine and after few request it return 400 bad request error.
I have tried many things but all in vain. I need this security otherwise I would have skipped the same

The header name should be RequestVerificationToken without the leading underscores. The version with the underscores is the name you should use if you are posting it as a form value. You can configure this to something else:
https://www.learnrazorpages.com/security/request-verification#configuration

Related

How to do File Upload Without Page Refresh or redirect in ASP.NET MVC

I am uploading a file using via a view linked to a controller however after the upload is uploaded the application is either trying to refresh or redirect and I need to prevent this. May you please point me in the right direction to avoid redirection and refresh? I have done a bit of reading and I suspect that this line action="/api/BulkUpload">might be causing the problem.
My Controller
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using Repositories.BulkUpload;
using Repositories.Interfaces.BulkUpload;
namespace SimSentinel.Controllers
{
//[Authorize]
public class BulkUploadController : ApiController
{
private readonly IBulkUploadRepository _bulkUploadRepository;
public async Task<HttpResponseMessage> PostFile()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/Files");
var provider = new FormDataStreamer(root);
try
{
StringBuilder sb = new StringBuilder(); // Holds the response body
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the form data.
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
sb.Append(string.Format("{0}: {1}\n", key, val));
}
}
// This illustrates how to get the file names for uploaded files.
foreach (var file in provider.FileData)
{
FileInfo fileInfo = new FileInfo(file.LocalFileName);
sb.Append(string.Format("Uploaded file: {0} ({1} bytes)\n", fileInfo.Name, fileInfo.Length));
}
return new HttpResponseMessage()
{
Content = new StringContent(sb.ToString())
};
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
public class FormDataStreamer : MultipartFormDataStreamProvider
{
public FormDataStreamer(string rootPath) : base(rootPath) { }
public FormDataStreamer(string rootPath, int bufferSize) : base(rootPath, bufferSize) { }
public override string GetLocalFileName(HttpContentHeaders headers)
{
var srcFileName = headers.ContentDisposition.FileName.Replace("\"", "");
return Guid.NewGuid() + Path.GetExtension(srcFileName);
}
}
}
}
MY HTML
<form name="form1" method="post" enctype="multipart/form-data" action="/api/BulkUpload">
<div>
<label for="caption">Image Caption</label>
<input name="caption" type="text" />
</div>
<div>
<label for="image1">Image File</label>
<input name="image1" type="file" />
</div>
<div>
<input type="submit" value="ok" />
</div>
</form>
You're correct. When you submit the form, the file is sent to the controller via a HTTP POST request and the page is either, necessarily, refreshed or redirected. If you don't want to the page to refresh or redirect, then you'll have to use AJAX to post the file to the controller.
From a Mozilla Developer document on HTTP requests,
The GET method requests a representation of the specified resource.
Requests using GET should only retrieve data.
The POST method is used to submit an entity to the specified
resource, often causing a change in state or side effects on the
server.
From these notes on Web Programming from Nanyang Technological University,
[The] POST request method is used to "post" additional data up to the server
(e.g., submitting HTML form data or uploading a file). Issuing an HTTP
URL from the browser always triggers a GET request. To trigger a POST
request, you can use an HTML form with attribute method="post" or
write your own network program. For submitting HTML form data, POST
request is the same as the GET request except that the URL-encoded
query string is sent in the request body, rather than appended behind
the request-URI.
So, you can see that since you're posting a file to the server using a standard HTTP request, it is necessarily going to refresh or redirect in some way.
To avoid this, you can use jQuery to asynchronously post the file to the server without refreshing the page. There are plenty of articles on how to do this. I suggest you give it a try and post another question if you get stuck.
Upload file using jQuery and post it to Controller
ASP Snippets - Upload file using jQuery AJAX in ASP.Net MVC
C# Corner - File Upload Through JQuery AJAX In ASP.NET MVC
Thanks so much for the help it guided me in the right direction. I eventually got my answer from this [How to submit html form without redirection? . The Iframe approach is the simplest approach it is a temporary fix seeing as some articles are saying that although it is still supported by most modern browsers it has been deprecated.

MVC receive post from another website - Chrome says Post Cancelled

I am posting via a plain html text file on c:\ drive into my mvc website running on my machine:
<body>
<a id="testPost" href="./post_files/post.htm">test post</a>
<script type="text/javascript">
$("#testPost").click(function () {
$.post("http://hml.backend/Helix/Authorisation",
{
ClientIP: "192.168.20.34"
}, function (resultData) {
alert(resultData);
});
return false;
});
the controller is setup thus:
[HttpPost]
public ActionResult Authorisation(string ClientIP)
{
string result = _videoSecurityService.CheckHelixAuthorisation(ClientIP);
return Content(result);
}
The controller event gets hit in debug and there is no exception but Chrome says
'POST: Cancelled' in the debug window
Any ideas why?
This is a cross domain call and is not allowed in a lot of browsers, since it's a possible security risk. You could try to redirect your call on the server-side. (make the call to your own application and handle the request-response to the other website there)

Redirect after POST doesn't change URL

I'm using the "Redirect After Post" (http://en.wikipedia.org/wiki/Post/Redirect/Get) pattern to solve the problems with refreshing that it solves, but I'm not seeing the URL change after the POST and subsequent GET.
Here is my setup:
I have a form with some pretty extensive client-side validation, then submit.
#using (Html.BeginForm("AddItem", "Order", FormMethod.Post, new { Id = "addItemForm" }))
{
// form stuff
}
Client-side validation:
$('#addToOrder').click(function () {
// do a bunch of validation stuff.
}
if (criteriaMet) {
$('#addItemForm').submit();
}
"AddItem" controller:
public class OrderController {
[HttpPost]
public ActionResult AddItem(long? orderId, long menuItemId)
{
if (oneConditionIsTrue)
{
return RedirectToRoute("NamedRoute1", new { RouteValueDictionary values });
}
else
{
return RedirectToRoute("NamedRoute2", new { RouteValueDictionary values });
}
}
public class NamedRouteController
{
public ActionResult NamedRouteAction
{
// do some stuff
if (mobile)
{
return View("MobileView", model);
}
else
{
return View("RegularView", model);
}
}
After redirecting from the POST action (AddItem), I can step things through the GET action to the return (either one). I would expect the URL in the browser after all of this to be http://mydomain.com/NamedRoute/NamedRouteAction but it's http://mydomain.com/Order/AddItem. Why is this? Shouldn't the RedirectToRoute change the URL?
What am I missing?
I suspect that the controller action is somehow invoked with an AJAX request. For example this could happen if you are using jQuery Mobile or something. Or maybe there's some other script that you have written doing this - it hijacks the form submission and sends an AJAX request instead. And since it is an AJAX request, you cannot possibly expect that the url in the client browser would ever change - that's the whole point of AJAX - stay on the same page.
This could be very easily verified by using a javascript debugging tool such as FireBug. Simply look at the Network tab and see if the POST request was an AJAX request. In the Net tab find the request and see if there's the following request header:
X-Requested-With: XMLHttpRequest
jQuery appends this HTTP header to all AJAX requests it sends.
So basically if you expect a redirect to happen after a POST request you shouldn't use AJAX to submit your form. Or to be more precise: the redirect happens on the server side (once again you will be able to see it in FireBug - the 302 status code) and then the XMLHttpRequest simply follows this redirect but the client browser will not change its current location.

What are the security reasons for not allowing get requests with MVC/ajax? [duplicate]

As part of the ASP.NET MVC 2 Beta 2 update, JSON GET requests are disallowed by default. It appears that you need to set the JsonRequestBehavior field to JsonRequestBehavior.AllowGet before returning a JsonResult object from your controller.
public JsonResult IsEmailValid(...)
{
JsonResult result = new JsonResult();
result.Data = ..... ;
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return result;
}
What is the reasoning behind this? If I am using JSON GET to try and do some remote validation, should I be using a different technique instead?
The reason for the DenyGet default is on MSDN with a link to Phil Haack's blog for further details. Looks like a Cross-Site scripting vulnerability.
HTTP GET is disabled by default as part of ASP.NET's Cross-Site Request Forgery (CSRF/XSRF) protections. If your web services accept GET requests, then they can be vulnerable to 3rd party sites making requests via <script /> tags and potentially harvesting the response by modifying JavaScript setters.
It is worth noting however that disabling GET requests is not enough to prevent CSRF attacks, nor is it the only way to protect your service against the type of attack outlined above. See Robust Defenses for Cross-Site Request Forgery for a good analysis of the different attack vectors and how to protect against them.
I also had your problem when I migrated my MVC website from Visual Studio 2008 to Visual Studio 2010.
The main aspx is below, it has an ViewData which calls a Category Controller in order to fill up ViewData["Categories"] with SelectList collection. There's also a script to call a Subcategory Controller to fill up the second combo with javascript. Now I was able to fix it adding up AlloGet attribute on this second controller.
Here's the aspx and javascript
<head>
<script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#CategoryId").change(function () {
var categoryId = $(this)[0].value;
$("#ctl00_MainContent_SubcategoryId").empty();
$("#ctl00_MainContent_SubcategoryId").append("<option value=''>-- select a category --</option>");
var url = "/Subcategory/Subcategories/" + categoryId;
$.getJSON(url, { "selectedItem": "" }, function (data) {
$.each(data, function (index, optionData) {
$("#ctl00_MainContent_SubcategoryId").append("<option value='" + optionData.SubcategoryId + "'>" + optionData.SubcategoryName + "</option>");
});
//feed our hidden html field
var selected = $("#chosenSubcategory") ? $("#chosenSubcategory").val() : '';
$("#ctl00_MainContent_SubcategoryId").val(selected);
});
}).change();
});
</script>
<body>
<% using (Html.BeginForm()) {%>
<label for="CategoryId">Category:</label></td>
<%= Html.DropDownList("CategoryId", (SelectList)ViewData["Categories"], "--categories--") %>
<%= Html.ValidationMessage("category","*") %>
<br/>
<label class="formlabel" for="SubcategoryId">Subcategory:</label><div id="subcategoryDiv"></div>
<%=Html.Hidden("chosenSubcategory", TempData["subcategory"])%>
<select id="SubcategoryId" runat="server">
</select><%= Html.ValidationMessage("subcategory", "*")%>
<input type="submit" value="Save" />
<%}%>
here's my controller for subcategories
public class SubcategoryController : Controller
{
private MyEntities db = new MyEntities();
public int SubcategoryId { get; set; }
public int SubcategoryName { get; set; }
public JsonResult Subcategories(int? categoryId)
{
try
{
if (!categoryId.HasValue)
categoryId = Convert.ToInt32(RouteData.Values["id"]);
var subcategories = (from c in db.Subcategories.Include("Categories")
where c.Categories.CategoryId == categoryId && c.Active && !c.Deleted
&& c.Categories.Active && !c.Categories.Deleted
orderby c.SubcategoryName
select new { SubcategoryId = c.SubcategoryId, SubcategoryName = c.SubcategoryName }
);
//just added the allow get attribute
return this.Json(subcategories, JsonRequestBehavior.AllowGet);
}
catch { return this.Json(null); }
}
I don't know if this is the reason they chose to change that default, but here's my experience:
When some browsers see a GET, they think they can cache the result. Since AJAX is usually used for small requests to get the most up-to-date information from the server, caching these results usually ends up causing unexpected behavior. If you know that a given input will return the same result every time (e.g. "password" cannot be used as a password, no matter when you ask me), then a GET is just fine, and browser caching can actually improve performance in case someone tries validating the same input multiple times. If, on the other hand, you expect a different answer depending on the current state of the server-side data ("myfavoriteusername" may have been available 2 minutes ago, but it's been taken since then), you should use POST to avoid having the browser thinking that the first response is still the correct one.

Redirect to an ASP.NET MVC page problem after POST request from Silverlight

In an ASP.NET MVC application I have a CartController with this AddToCart action:
public RedirectToRouteResult AddToCart(Cart cart, decimal productId,
string returnUrl)
{
Product product = productsRepository.Products
.FirstOrDefault(p => p.prodID == productId);
cart.AddItem(product);
return RedirectToAction("Index", new { returnUrl });
}
When a user submits a POST request ("Add to Cart" button) to this action from a plain ASP.NET MVC view, everything goes well: the Product is added to the Cart and the user is automatically redirected to a Cart/Index page.
If the product is submitted from a Silverlight app (which is inside an ASP.NET MVC view) it is successfully added to the Cart as well, but the there is no redirection in this case.
What is the problem? Maybe it is due to the fact that all requests from a Silverlight are asynchronous (if I'm not mistaken), and the request from a general ASP.NET MVC view is synchronous by nature? How it can affect the redirection?
In any case, how this problem could be solved?
Edited (added):
My code for sending a post request from a Silverlight app:
//first build a "paramstring" in the format "productId=126504" and then post it using this
WebClient wc = new WebClient();
wc.Headers["Content-type"] = "application/x-www-form-urlencoded";
wc.UploadStringAsync(new Uri("http://localhost:10930/Cart/AddToCart"), "POST", paramstring, "http://localhost:10930/Products");
The WebClient you are using to send the POST request will automatically follow the redirects performed on the server and return the HTML and everything ends in the success callback. If you want to redirect the user browser to this page you shouldn't use WebClient. You need javascript to submit a <form>. Silverlight allows you to execute javascript, so you could use it to dynamically generate and submit a form, or if the form already exists in the DOM set the values of the input fields and submit it.
Here's an example of how you could do this. Add the following javascript function to the same page hosting the Silverlight application:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
function addToCart(productId, returnUrl) {
var form = $(document.createElement('form'))
.attr('action', '/products/addtocart')
.attr('method', 'post')
.append(
$(document.createElement('input'))
.attr('type', 'hidden')
.attr('name', 'productId')
.val(productId)
)
.append(
$(document.createElement('input'))
.attr('type', 'hidden')
.attr('name', 'returnUrl')
.val(returnUrl)
);
$('body').append(form);
form.submit();
}
</script>
And then inside your Silverlight application whenever you decide to invoke the POST action:
HtmlPage.Window.Invoke("addToCart", "123", "http://example.com/someReturnUrl");
You may add other parameters if necessary.

Resources