Trying to Post a file and some data to MVC 5 backend.
Issue is it's not mapping properly so it's returning a 404. The Http post request is being sent over as a multipart/form-data content type.
Here is the Http post from angular service
requestInputHeat: function (qty, date, camp, note, file1) {
return $http({
method: 'POST',
url: '/log/heat/request',
headers: {
'Content-Type': 'multipart/form-data'
},
data: {
Quantity : qty,
RequestDate: date,
CampaignDetail: camp,
Notes: note,
File: file1
},
transformRequest: function (data, headersGetter) {
var formData = new FormData();
angular.forEach(data, function (value, key) {
formData.append(key, value);
});
var headers = headersGetter();
delete headers['Content-Type'];
return formData;
}
})
}
Here is the controller MVC5 backend I have trying to receive this request (using mvcmapping atrributes)
[HttpPost]
[Route("log/heat/request")]
public ActionResult RequestPOHeat(string Quantity, string RequestDate, string CampaignDetail, string Notes, HttpPostedFileBase File)
{
......
}
Please try following
In angularJs
function requestInputHeat(qty, date, camp, note, file1) {
var request = {
method: 'POST',
//public ActionResult RequestPOHeat(string Quantity, string RequestDate, string CampaignDetail, string Notes)
//your Controller Action RequestPOHeat requires 4 query strings which was previously not send in your code which I had passed below. This was giving 404 error.
url: '/log/heat/request?Quantity=' + qty.toString() + '&RequestDate=' + date.toString() + '&CampaignDetail=' + camp.toString() + '&Notes' + note.toString(),// I don't know the datatype so had converted them to string.Please change them to corresponding type once it's working. Also don't forget to map type with RequestPOHeat method parameter of controller.
data: { file: file1 },
headers: {
//IMPORTANT!!! You might think this should be set to 'multipart/form-data'
// but this is not true because when we are sending up files the request
// needs to include a 'boundary' parameter which identifies the boundary
// name between parts in this multi-part request and setting the Content-type
// manually will not set this boundary parameter. For whatever reason,
// setting the Content-type to 'false' will force the request to automatically
// populate the headers properly including the boundary parameter.
'Content-Type': undefined
},
transformRequest: function (data) {
var formData = new FormData();
angular.forEach(data, function (value, key) {
formData.append(key, value);
});
return formData;
},
};
return $http(request).success(function (result) {
return result.data;
}).error(function () {
});
}
In MVC Controller
[HttpPost]
[Route("log/heat/request")]
public ActionResult RequestPOHeat(string Quantity, string RequestDate, string CampaignDetail, string Notes)
{
var file = HttpContext.Current.Request.Files.Count > 0 ? HttpContext.Current.Request.Files(0) : null;
if (file != null && file.ContentLength > 0) {
//If file is posted you will get here..
}
......
}
Hint: What is bounty Parameter?
The boundary parameter is set to a number of hyphens plus a random string at the end, but you can set it to anything at all. The problem is, if the boundary string shows up in the request data, it will be treated as a boundary.
Related
I want to call a view with an ajax call on my current view. The following is my Ajax call that calls a function of my controller.
$.ajax({
type: 'POST',
url: '#Url.Action("EditCertificateObservation", "Frühwarnsystem")',
data: {
serverName: '#Model[0].ServerName',
name: event.data.name,
thumbprint: event.data.thumbprint,
expiringDateStr: event.data.expiringDate,
isChecked: document.getElementById(store + event.data.index).checked,
model: data,
},
});
This code is my controller function that returns a view to load.
[HttpPost]
public ActionResult EditCertificateObservation(string serverName, string name, string thumbprint, string expiringDateStr, bool isChecked, string model)
{
var newModel = JsonConvert.DeserializeObject<List<Store>>(model);
var cert = new Certificate(serverName, name, thumbprint, expiringDateStr);
var server = new Server(serverName);
server.FetchIdByServerName();
if (isChecked)
{
cert.AddToObservation(server.Id);
}
else
{
cert.DeleteFromObservation();
}
return View("Index");
}
To know for you: I call the ajax call with a checkbox on my view, which is dynamically generated. If I debug the controller function get called and runs but the browser doesn't load the view I return.
If you need more information, just ask here.
Thank you for your help
If you want to open a view with after Ajax request than you just have to wait for the response of your controller then you can use success, but you can also use failure or error depend on your need, so your Ajax will be like this:
$.ajax({
type: 'POST',
url: '#Url.Action("EditCertificateObservation", "Frühwarnsystem")',
data: {
serverName: '#Model[0].ServerName',
name: event.data.name,
thumbprint: event.data.thumbprint,
expiringDateStr: event.data.expiringDate,
isChecked: document.getElementById(store + event.data.index).checked,
model: data,
},
success: function (response) {
alert(response.message);
window.location.href = "/Frühwarnsystem/Index";
// or with some parameter
window.location.href ="/Frühwarnsystem/Index?id=" + response.counter;
// or if you prefer with helper ...
window.location.href = '#Url.Action("Frühwarnsystem","Index")';
},
failure: function (response) { alert("failure"); },
error: function (response) { alert("error"); }
});
And to be a little more useful, your controller can send a Json response with some parameter for example, as follow:
[HttpPost]
public JsonResult EditCertificateObservation(string serverName, string name, string thumbprint, string expiringDateStr, bool isChecked, string model)
{
var newModel = JsonConvert.DeserializeObject<List<Store>>(model);
var cert = new Certificate(serverName, name, thumbprint, expiringDateStr);
var server = new Server(serverName);
server.FetchIdByServerName();
if (isChecked)
{
cert.AddToObservation(server.Id);
}
else
{
cert.DeleteFromObservation();
}
// Do some condition here to send an answer ...
string message = "";
int counter = 0;
var response = new { counter, message };
return Json(response);
}
You are calling an ajax POST HTTP request. It means you can download the result of the call and assign it into a javascript variable. This result will not be displayed in the browser as a page. Take a look at examples of $.post here.
The description for FormData.append() reads as follows:
(method) FormData.append(name: string, value: string | Blob, fileName?: string):void
The function below is simply just appending two string literals to a FormData object and posting it to a C# controller. The issue is that my controller is receiving the key but not the value.
I can successfully get a key and value when my value is a blob, such as file data. But I cannot get the value when it is a string (or string literal, as shown in the demo below). It is simply empty.
deleteSelectedFiles(url, data, files, method = "POST") {
var that = this;
let formData = new FormData();
for (let i = 0; i < data.length; i++) {
formData.append("test", "wow");
}
console.log(formData);
return this.http.fetch(url, {
method: method,
body: formData,
headers: new Headers()
}).then(response => response.json()
.then(function (response) {
if (response) {
that.refreshTable();
}
else {
console.log("upload() response: " + response);
}
}));
}
I have the following Http Fetch Client that I've been using to do many different kinds of tasks without any issues, so I don't believe the issue is with the configuration.
http = new HttpClient();
http.configure(config => {
config
.useStandardConfiguration()
.withBaseUrl(``);
I have an AJAX JS functions which calls a C# MVC HttpPost Controller. This will return an object but for the purposes of debugging I am just trying to return a string.
JS AJAX code:
function UpdateBlogFilters(month, year) {
var url = "/HttpPost/GetBlogPostsPerMonth";
if (month != null && month != "" && year != null && year != "") {
alert(month + " " + year);
$.ajax({
url: url,
data: { monthValue: month, yearValue: year },
cache: false,
type: "POST",
success: function (data) {
console.log("Data " + data);
var markup = "";
},
error: function (response) {
console.log(response.responseText);
console.log("Error: " + response);
}
});
console.log("1");
}
console.log("2");
}
Controller Code:
[HttpPost]
public ActionResult GetBlogPostsPerMonth(int monthValue, int yearValue)
{
.... the non-debug code
return Json("test");
}
This is returning an error, but the responseText and any error information is blank? I have verified that the controller is being called and it is reaching the return statement in the C# without error.
Any help?
Change your C# code to read something like this:
[HttpPost]
public ActionResult GetBlogPostsPerMonth(int monthValue, int yearValue)
{
.... the non-debug code
return Json(new { result = "test" });
}
Ultimately, you want to pass an object into Json(). You can pass an instance of a class, or an anonymous type, or dynamic.
This was the solution. The AJAX call in itself worked fine. All code posted here was correct as per Bhavik.
The issue was that this AJAX request was called onclick of a hyperlink and this hyperlink although having no href set, was refreshing the page and somehow cancelling off the AJAX request. Removing the href to leave
<a onclick="UpdateBlogFilters(5,2014)">
resulted in the object being returned from AJAX successfully.
Why do you have "/HttpPost/" in url? Is that name of a controller that is different from the one you are using to render the view? Can you try without /HttpPost/
var url = "GetBlogPostsPerMonth";
I am currently working with mvc. I have a page with more than one button on, which does complicate things a little. However the button I am using calls a jquery function to post data to the controller. This works as expected and data reaches the action and the action does everything expected except redirect.
The function posts data to the controller, or allows the controller to see the values and build up a url to then redirect to. However the redirect doesnt work, and the function calls a success function as expected. If I put the data return into an alert the code for the page is returned in the alert window not the url.
Here is the code:
<input type="submit" value="Assign" name="Assign" onclick="doRedirect()" />
function doRedirect() {
var action = '#Url.Action("Redirect")';
//alert(action);
var opt = {
type: "POST",
data: {
Team: $('#Team option:selected').val()
},
url: action,
success: RedirectSuccess
};
jQuery.ajax(opt);
}
function RedirectSuccess(data) {
if (data != undefined) {
data;
}
}
public ActionResult Redirect(string Team)
{
var hostName = "http://localhost/test/testpage.aspx?";
var team = "&Team=" + Team;
var filterUrl = Team;
return Redirect(filterUrl);**//this doesnt work**
}
Instead of sending back a redirect result from the action, try sending back the URL you want to redirect to. On the client side, you read the response of the request, and do the redirect by setting window.location.href to the URL you get back from the server.
In your action, you could return the URL as JSON for instance:
return Json(new { url: filterUrl });
And in your success callback, you do this to redirect:
if (data !== undefined && data.url !== undefined) {
window.location.href = data.url;
}
This is what I did instead.
public string Redirect(string Team)
{
var hostName = "http://localhost/test/testpage.aspx?";
var team = "&Team=" + Team;
var filterUrl = hostname + Team;
return filterUrl;
}
function RedirectSuccess(data) {
if (data != undefined) {
window.location = data;
}
}
and on my search success
I don´t know why my parameter "ParametroFiltro Filtro" is getting null, the other parameters "page" and "pageSize" is getting OK.
public class ParametroFiltro
{
public string Codigo { get; set; }
public string Descricao { get; set; }
}
My ApiController Get method:
public PagedDataModel<ParametroDTO> Get(ParametroFiltro Filtro, int page, int pageSize)
My ajax call:
var fullUrl = "/api/" + self.Api;
$.ajax({
url: fullUrl,
type: 'GET',
dataType: 'json',
data: { Filtro: { Codigo: '_1', Descricao: 'TESTE' }, page: 1, pageSize: 10 },
success: function (result) {
alert(result.Data.length);
self.Parametros(result.Data);
}
});
You are trying to send a complex object with GET method. The reason this is failing is that GET method can't have a body and all the values are being encoded into the URL. You can make this work by using [FromUri], but first you need to change your client side code:
$.ajax({
url: fullUrl,
type: 'GET',
dataType: 'json',
data: { Codigo: '_1', Descricao: 'TESTE', page: 1, pageSize: 10 },
success: function (result) {
alert(result.Data.length);
self.Parametros(result.Data);
}
});
This way [FromUri] will be able to pick up your complex object properties directly from the URL if you change your action method like this:
public PagedDataModel<ParametroDTO> Get([FromUri]ParametroFiltro Filtro, int page, int pageSize)
Your previous approach would rather work with POST method which can have a body (but you would still need to use JSON.stringify() to format body as JSON).
Provide the contentType property when you make the ajax call. Use JSON.stringify method to build the JSON data to post. change the type to POST and MVC Model binding will bind the posted data to your class object.
var filter = { "Filtro": { "Codigo": "_1", "Descricao": "TESTE" },
"page": "1", "pageSize": "10" };
$.ajax({
url: fullUrl,
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(filter),
success: function (result) {
alert(result.Data.length);
self.Parametros(result.Data);
}
});
It's also possible to access POST variables via a Newtonsoft.Json.Linq JObject.
For example, this POST:
$.ajax({
type: 'POST',
url: 'URL',
data: { 'Note': note, 'Story': story },
dataType: 'text',
success: function (data) { }
});
Can be accessed in an APIController like so:
public void Update([FromBody]JObject data)
{
var Note = (String)data["Note"];
var Story = (String)data["Story"];
}
If you append json data to query string, and parse it later in web api side. you can parse complex object too. It's useful rather than post json object, espeicaly in some special httpget requirement case.
//javascript file
var data = { UserID: "10", UserName: "Long", AppInstanceID: "100", ProcessGUID: "BF1CC2EB-D9BD-45FD-BF87-939DD8FF9071" };
var request = JSON.stringify(data);
request = encodeURIComponent(request);
doAjaxGet("/ProductWebApi/api/Workflow/StartProcess?data=", request, function (result) {
window.console.log(result);
});
//webapi file:
[HttpGet]
public ResponseResult StartProcess()
{
dynamic queryJson = ParseHttpGetJson(Request.RequestUri.Query);
int appInstanceID = int.Parse(queryJson.AppInstanceID.Value);
Guid processGUID = Guid.Parse(queryJson.ProcessGUID.Value);
int userID = int.Parse(queryJson.UserID.Value);
string userName = queryJson.UserName.Value;
}
//utility function:
public static dynamic ParseHttpGetJson(string query)
{
if (!string.IsNullOrEmpty(query))
{
try
{
var json = query.Substring(7, query.Length - 7); //seperate ?data= characters
json = System.Web.HttpUtility.UrlDecode(json);
dynamic queryJson = JsonConvert.DeserializeObject<dynamic>(json);
return queryJson;
}
catch (System.Exception e)
{
throw new ApplicationException("can't deserialize object as wrong string content!", e);
}
}
else
{
return null;
}
}
In .NET Core, the HttpClient sets the transfer-encoding: chunked header by default. This can cause the .NET Web API controller parameters to be null.
To get around this, you'll need to set the ContentLength header explicitly:
var json = JsonConvert.SerializeObject(myObject);
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentLength = json.Length;
var response = await client.PostAsync("http://my-api.com", content);
SO answer if you already know the transfer-encoding header is the issue: How to disable Chunked Transfer Encoding in ASP.Net C# using HttpClient
Related bug which won't be fixed, which gives some insight into the problem: https://github.com/dotnet/runtime/issues/30283