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);
Related
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.
Here is my angular 6 code
saveQuestion(value : string) : Observable<string> {
console.log(value)
let params = new HttpParams();
params = params.set('id', value);
return this._http.post<string>(this.subjectAPi + 'api/Question/Save/', {params});
}
below is my .net core code
[HttpPost("[action]")]
public IActionResult Save([FromBody]string id)
{
Question ob = new Question();
ob.update(id) // id is always null
return Ok("1");
}
ID is always null. Suggestion will be really appreciated
The HttpParams is for preparing URL encoded parameters, if you'd like to post a body in JSON you don't need that. Try just simply create your data like:
let body = { id: value }
this._http.post<string>(this.subjectAPi + 'api/Question/Save/', body);
If you really want to post your body as URL encoded data try to set the Content-Type request header:
this._http.post<string>(this.subjectAPi + 'api/Question/Save/', params.toString(), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
If I'm not mistaking, ASP.NET Core does not work with camelCasing for JSON by default. Try renaming the id in the HttpParams to be Id.
saveQuestion(value : string) : Observable<string> {
let params = new HttpParams();
params = params.set('Id', value);
return this._http.post<string>(this.subjectAPi + 'api/Question/Save/', {params});
}
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'm using angular-file-upload as found in Here. It was working for what I needed, but now the need has changed, and I'd like to be able to send additional data about (object) along with the file. There isn't much documentation about it. With what I could see, when I used options directive as attribute and provided the data as object, it's not listed anywhere in FileUploader. Also, my controller gives me an error when I upload. I added a Model class in Post as argument, which is what causing the controller to break.
[HttpPost]
public Task<HttpResponseMessage> PostFile(QAFileAttribute QAFile)
{
this.QAFile = QAFile;
string newLocation = GetCurrentUploadDirectory();
HttpRequestMessage request = this.Request;
if(!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = UploadLocation; //newLocation;
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider)
.ContinueWith<HttpResponseMessage>(o =>
{
if (o.IsFaulted || o.IsCanceled)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, o.Exception);
}
foreach (MultipartFileData file in provider.FileData)
{
if(String.IsNullOrEmpty(file.Headers.ContentDisposition.FileName))
{
return Request.CreateResponse(HttpStatusCode.NotAcceptable, "Filename is not acceptable.");
}
UploadFile(file, QAFile);
}
return Request.CreateResponse(HttpStatusCode.OK);
});
return task;
}// end Post
So, how can I send the multi-part file along with additional data?
This link will sure help you , I have implemented this.
https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
app.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, additional, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$.each(additional, function(obj) {
fd.append(obj.key,obj.value);
});
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
I figured it out myself. This link helped a lot. Turned out, I didn't have to do a whole lot to provide additional data. I just needed to provide formData as request body to be processed inside the action; the controller action doesn't take any parameters. The problem with my code was that (I want to say due to improper documentation of angular-file-upload) I had misunderstood what formData is. Turned out, it's array of objects. It needed to be like this:
var uploader = new FileUploader({
url: 'api/upload',
formData: [{...}]
});
This sent the data to the controller action as request body. From there, I just had to access it as provider.FormData["somename"];
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.