Post FormData (key, value): controller not receiving value when string - c#

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

Related

.net core Cannot deserialize the current JSON array into type 'Microsoft.AspNetCore.Http.IFormFile'

I am trying to upload a picture, send the picture by the front end (axios request), and then this error is returned by the server.
Cannot deserialize the current JSON array into type
'Microsoft.AspNetCore.Http.IFormFile' because the type requires a JSON
object to deserialize correctly. To fix this error either change the
JSON to a JSON object (e.g. {"name":"value"}) or change the
deserialized type to an array or a type that implements a collection
interface (e.g. ICollection, IList) like List that can be
deserialized from a JSON array. JsonArrayAttribute can also be added
to the type to force it to deserialize from a JSON array. Path 'file',
line 1, position 339."
[HttpPost("add")]
public async Task<IActionResult> Add(Post post, IFormFile file){............}
this is my axios request
const submit = useCallback(
async (values: PostExtensions) => {
debugger;
setLoading(true);
const tarih = dayjs(values.date);
values.tarih = dayjs(date, "YYYY-MM-DDTHH:mm:ss Z").format();
const formdata = new FormData();
formdata.append("postId", values.postId!);
formdata.append("file", values.file![0]);
formdata.append("userId", values.userId!);
formdata.append("projectId", values.projectId!);
formdata.append("date", values.date!);
await entitySubmit({
values: JSON.parse(JSON.stringify(values)),
dispatch: dispatch,
returndata: true,
headers: {
"Content-Type": "multipart/form-data"
},
links: [
{
type: 0,
link: "post/addpost",
actionType: ActionType.add_Post,
method: "POST",
},
{
type: 1,
link: "post/editpost",
actionType: ActionType.edit_Post,
method: "POST",
},
],
id: "postId",
});
return null;
},
[show, dispatch]
);
when I tried to post formdata, It does'nt submit.
EDIT 1: I found the problem where is,
formData send null file object something like this
formdata.get('file') // '[Object object]'
Not a direct answer to your question, but I've found that sending files via ajax is often times much easier when using base64 encoding. Here's a small sample of how you can achieve this:
//client side
//this function converts a file object to a base64 string
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
document.getElementById("upload").onclick = async () => {
const file = document.getElementById('myfile').files[0];
const base64 = await toBase64(file);
const response = await fetch('/home/upload', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( {base64 /*add any additional values you need here */})
})
const json = await response.json();
}
//server side
public class UploadData
{
public string Base64Data { get; set; }
//any additional properties you need here....
}
[HttpPost]
public IActionResult Upload([FromBody]UploadData data)
{
var base64 = data.Base64Data.Substring(data.Base64Data.IndexOf(",") + 1); //bit of a hack, need to remove the additional part of the base64 string that JS generates but that .net doesn't like
var bytes = Convert.FromBase64String(base64);
//bytes is now available to do whatever you need with
return Json(someObjectYouWantToReturn);
}
Hopefully this code will be a good enough starting point to get you where you need. Again, base64 might not be what you're after, but I use this approach a lot, I find it simple to deal with personally.

Parameter null is ASP controller

I posted an XmlRequest with native JavaScript to my controller, but it doesn't accept the request body as a parameter. I.e. it reads it as a null even if the parameter is meant to be a string.
The request:
request.open("POST", "https://localhost:44328/CommodityTypes/PostData");
//request.setRequestHeader('Content-type', 'text'); // maybe that makes the problem?
request.send("Message");
The controller:
[HttpPost]
public string PostData(string Text)
{
return JsonSerializer.Serialize(Text);
}
Will be happy to get any advice on it.
After getting deeper into the business I found, that one can specify 'object' as
a parameter to parse for ('[FromBody]' attribute stays), getting a curious object, that gives the JSON message invoking ToString() method. If you have a variable or uneven structure of incoming JSON you may use this aproach.
Though there must be some other, meant way of handling the issue.
You can create object data = { "text": "YourText" } and send JSON.stringify(data)
And need set header xmlhttp.setRequestHeader('Content-Type', 'application/json');
var uri = '/CommodityTypes/PostData';
var xmlhttp;
xmlhttp = new XMLHttpRequest();
var data = { "text": "YourText" };
var sendstr = JSON.stringify(data);
xmlhttp.open("POST", uri, true);
xmlhttp.setRequestHeader('Content-Type', 'application/json');
xmlhttp.send(sendstr);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
alert(xmlhttp.responseText);
}
in POST you have to send the Text parameter as FormData so it can be recognized and mapped by the method
var data = new FormData();
data.append("Text", "test");
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://localhost:44328/CommodityTypes/PostData");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(data);

MVC5 404 Error On File & Input Post Angularjs

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.

Ajax post JSON data to MVC getting error Unexpected token P

This has been driving me nuts. I have a page where I need to make a JSON post to a controller, it will process it and return an excel file for download. So far it appears to be running correctly, but when it returns to the ajax call, I get an parsererror and the message "Unexpected token P." I have tried so many different configurations and call methods (standard MVC ActionRequest to WebApi post) and none of them change. Here is the code I'm running.
JavaScript:
var treatmentplan = {"PlanRecordStatusId":"1","PlanRecordDateBegin":"","PlanRecordDateEnd":"","ClientClaimNumber":"","PatientNumber":0,"requestAction":3};
$.ajax({
//global: true,
//url: '/home/ExcelRpt',
url: '/api/TreatmentPlanExcel',
type: 'POST',
dataType: 'json',
data: treatmentplan,
//contentType: 'application/json; charset=utf-8',
success: function (data) {
//var msg = data.Message;
//$('#results').html(msg);
$("#tmpFrame").attr('src', 'URL-TO-EXCEL-FILE');
}
, error: function (jqXHR, exception, error) {
if (jqXHR.status === 0) {
alert('Not connect.n Verify Network.');
} else if (jqXHR.status == 404) {
alert('Requested page not found. [404]');
} else if (jqXHR.status == 500) {
alert('Internal Server Error [500].');
} else if (exception === 'parsererror') {
alert('Requested JSON parse failed.');
} else if (exception === 'timeout') {
alert('Time out error.');
} else if (exception === 'abort') {
alert('Ajax request aborted.');
} else {
alert('Uncaught Error.n' + jqXHR.responseText);
}
$('#log').html(error.message);
}
});
Here is the C# code (both WebApi and MVC controller version), I am not going to include my ToExcel extension, I know this part works it's just a matter of getting it to download when it's returned. It is reaching this code, generates data and returning. I just need to see what the heck is going on. If there is a prefered way of making the call, let me know as well (WebApi or MVC)
C#
Web Api version
public HttpResponseMessage Post(TreatmentPlanRequest tpRequest) {
tpRequest.Verify();
List<TreatmentPlan> tpl = DataAccess.GetReportDap(tpRequest).ToList();
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var package = tpl.ToExcel("TreatmentReport");
var fileStream = new MemoryStream();
package.SaveAs(fileStream);
fileStream.Position = 0;
result.Content = new StreamContent(fileStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
Here is the MVC controller version
[HttpPost]
public ActionResult ExcelRpt(TreatmentPlanRequest tpRequest) {
tpRequest.Verify();
List<TreatmentPlan> tpl = DataAccess.GetReportDap(tpRequest).ToList();
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var package = tpl.ToExcel("TreatmentReport");
var fileStream = new MemoryStream();
package.SaveAs(fileStream);
fileStream.Position = 0;
var fsr = new FileStreamResult(fileStream, contentType);
fsr.FileDownloadName = "TreatmentReport";
return fsr;
}
From here, I have no clue as to why this isn't working. I have searched high and low on Google on how to do this in MVC (I use to do this with web forms and never had an issue). I am guessing my issue is on the return or
Change your Success method to open a new window with the URL instead of setting a frame in the current window.
So this:
$("#tmpFrame").attr('src', 'URL-TO-EXCEL-FILE');
becomes:
window.open('URL-TO-EXCEL-FILE');
In the vast majority of cases, this should do exactly what you're looking for. Occasionally, depending on specific browser settings, users may get a "Popup Blocked" message, but that rarely happens in this scenario in the apps that I've worked with.
EDIT:
After additional clarification, there is a second issue. Data returned from the server must be in the same format as the .ajax() method is expecting it, in this case 'JSON'. Instead of returning a FileStreamResult from your Action, try returning a JSON object which has the URL you'll need to call to generate the Excel file.
return Json(new { URL = 'URL-TO-EXCEL-FILE' });
Then, follow the suggestion in my original response.
I would like to thank Kris Hatcher for the solution on this. He suggested making two ActionResults. One that builds a query string from the parameters of the initial request. It returns a full URL with the parameters. It then does a Window.Open() using the returned url.
With all the examples I found, this was the only one that worked for me. Here's how the code works.
JavaScript:
function TestWebApiReport() {
var reportData = GetReport();
$.ajax({
url: '/home/ExcelResults'
, data: reportData
, type: 'POST'
, dataType: 'json'
, success: function (data) {
window.open(data.URL);
}, error: function (jqXHR, exception, error) {
alert("GRRRRRR!!!")
}
});
}
It creates the JSON data, then posts it to a JsonResult. Here's the controller code.
C#
[HttpPost]
public JsonResult ExcelResults(ReportRequest tpRequest) {
StringBuilder sb = new StringBuilder();
bool firstIn = true;
sb.AppendFormat("{0}/Home/ExcelRpt", Request.Url.Scheme + "://" + Request.Url.Authority);
foreach (var prop in tpRequest.GetType().GetProperties()) {
if (prop.GetValue(tpRequest, null) != null) {
if (firstIn) {
sb.AppendFormat("?");
firstIn = false;
} else {
sb.AppendFormat("&");
}
sb.AppendFormat("{0}={1}", prop.Name, prop.GetValue(tpRequest, null));
}
}
return Json(new { URL = sb.ToString() });
}
You go back to the JavaScript, you'll see the return data uses the URL to do a Window.Open(). Then the excel file is created. Here's the last call (ActionResult).
[HttpGet]
public ActionResult ExcelRpt(ReportRequest tpRequest) {
if (tpRequest.requestAction != RequestAction.Report) {
throw new Exception("Did not use action request type of 'Report'.");
}
tpRequest.requestAction = RequestAction.Report;
List<Report> tpl = DataAccess.GetReportDap(tpRequest).ToList();
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var package = tpl.ToExcel("Report");
var fileStream = new MemoryStream();
package.SaveAs(fileStream);
fileStream.Position = 0;
var fsr = new FileStreamResult(fileStream, contentType);
fsr.FileDownloadName = "TreatmentReport.xlsx";
return fsr;
}
ReportRequest is a class I have and ToExcel(), I extended the List item. Now this works pretty well.

How to get the value that is returned from the kendoui upload success or complete function

I am using Kendo UI upload control. I have defined the Kendo UI upload like this:
<input type="file" name="resume" />
$("#file").kendoUpload({
async: {
saveUrl: "/Home/SaveResume",
autoUpload: true
},
complete: function (e)
{
// here i want to get the text that is returned from the controller
}
});
The controller code is like:
public ActionResult SaveResume(HttpPostedFileBase resume)
{
var text;
// code for the file to convert to text and assign it to text
return Json(text, JsonRequestBehavior.AllowGet);
}
After returning the code I want to retrieve the code in complete function. How can I do that?
You can get the response to success function like this
function onSuccess(e)
{
var response = e.response.data();
}
where the return json could be
return Json(new { data = text }, "text/plain");
If you just passing a string back you should be able to do:
function onSuccess(e) {
var text = e.XMLHttpRequest.responseText;
}
You could also pass back a more complex object, if required:
// Object
public class MyObject
{
public int ID { get; set; }
public string Text { get; set; }
}
// Controller Action
public virtual ActionResult Upload(HttpPostedFileBase file)
{
return this.Json(new MyObject(), "text/plain");
}
// Javascript Handler
function onSuccess(e) {
var response = jQuery.parseJSON(e.XMLHttpRequest.responseText);
var id = response.ID;
var text = response.Text;
}
I'll add my answer alongside the other valid answers here. First though you will want to get the returned response in the success function instead of the complete function:
$("#files").kendoUpload({
async: {
saveUrl: url,
removeUrl: removeUrl,
autoUpload: true
},
select: onFileSelect, // function for when a file is selected
success: onFileSuccess, // function that returns response after upload
complete: onFileComplete, // function after success
remove: onFileRemove, // function for when a file is removed
});
The on success function returns an object (normally people name it e)
function onFileSuccess(e) {
console.log("e.response", e.response);
console.log("e.operation", e.operation);
console.log("e.XMLHttpRequest.status", e.XMLHttpRequest.status);
//e.operation is upload or remove
if (e.operation === "upload") {
// a file was added, get the response
var fileid = e.response;
} else {
// Do something after a file was removed
}
}
My console.log entries return this data:
console.log values
This is how I return my data from the server:
public HttpResponseMessage InsertTempFile()
{
HttpPostedFile file = System.Web.HttpContext.Current.Request.Files[0];
//........
// Code that adds my file to the database
// and generates a new primary key for my file
//.........
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(myNewId.ToString());
return response;
}
The response.Content returns my new Id in e.response
The HttpStatusCode.Ok returns my status of 200. There's a bunch of other data that is returned as well if you inspect the response.
Note that to use HttpResponseMessage and HttpStatuseCode you need to include the following namespaces in your class:
using System.Net.Http;
using System.Net;

Categories