I want to upload an image as a HttpPost to a method.
I tried the following code:
[HttpPost]
public HttpResponseMessage Image(int id)
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count == 1) //do something
return Request.CreateResponse(HttpStatusCode.Created);
}
Then i use postman and added a image as a binary to the body.
However httpRequest.Files.Count is 0.
What am i doing wrong?
Try this above code.
Try in the google chrome rest client.. easy to test
i hope that may help you
public async Task<HttpResponseMessage> Image(int id)
{
string filepath = #"e:\testing\file";
if (Request.Content.IsMimeMultipartContent())
{
var streamProvider = new MultipartFormDataStreamProvider(filepath);
await Request.Content.ReadAsMultipartAsync(streamProvider);
foreach (MultipartFileData fileData in streamProvider.FileData)
{
if (string.IsNullOrEmpty(fileData.Headers.ContentDisposition.FileName))
{
return Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted");
}
string fileName = fileData.Headers.ContentDisposition.FileName;
if (fileName.StartsWith("\"") && fileName.EndsWith("\""))
{
fileName = fileName.Trim('"');
}
if (fileName.Contains(#"/") || fileName.Contains(#"\"))
{
fileName = Path.GetFileName(fileName);
}
File.Move(fileData.LocalFileName, Path.Combine(filepath, fileName));
}
return Request.CreateResponse(HttpStatusCode.OK);
}
For More details check this Tutorial
You should verify that your Content-Type is multipart/form-data when you do the post (and application/octet-stream in the data part) as well as proper boundaries. It will also help if you add headers/body of your request to the question.
This one works for me:
Header:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrhNuQUPgZKD8RxKq
Payload:
------WebKitFormBoundaryrhNuQUPgZKD8RxKq
Content-Disposition: form-data; name="filename"; filename="filename.txt"
Content-Type: application/octet-stream
------WebKitFormBoundaryrhNuQUPgZKD8RxKq--
Related
So I have to make a method in asp.net for an API that accepts 2 files via PUT (1 json and 1 xml for processing data, not saving- because I have to, OK? :) ), sending the request via fiddler..
Right now I'm sending the request like this on fiddler (PUT METHOD):
Content-Type: multipart/form-data
Authorization: XXXXX
Host: XXXX
Request body:
<#INCLUDE *C:\Users\ZZZZ.json*#>
<#INCLUDE *C:\Users\XXXX.xml*#>
Here's what I've tried inside the controller so far:
[HttpPut, Route("api/Student/{studentId}/Classes/{classId}")]
public async Task<string> Put(int studentId, int classId)
{
var file = HttpContext.Current.Request.Files.Count > 0 ?
HttpContext.Current.Request.Files[0] : null;
Stream fileContent = await this.Request.Content.ReadAsStreamAsync();
MediaTypeHeaderValue contentTypeHeader = this.Request.Content.Headers.ContentType;
if (fileContent != null)
return "ok";
return "not ok";
}
So far the file is not being uploaded nor appears within the request (everything's null). I've also tried the "Request" variable and HttpContext.
Tried the exact same thing but with a POST Method (including the boundaries) and the same happens.
What would you do in order to make this work? I really have to send a json object and another in xml, I really can't change languages or send everything in json ('cause that I could make it work)...
PS: The files don't have a defined structure, it has to be dynamic
PS2 : How would you then attempt to read those files without actually saving them?
You don't have to use a stream to read the file content. You can just try using the HttpPostedFile.
[HttpPut, Route("api/student/{studentId}/classes/{classId}")]
public async Task<string> Put(int studentId, int classId)
{
if (HttpContext.Current.Request.Files.Count == 0)
throw new HttpResponseException(new HttpResponseMessage()
{
ReasonPhrase = "Files are required",
StatusCode = HttpStatusCode.BadRequest
});
foreach (string file in HttpContext.Current.Request.Files)
{
var postedFile = HttpContext.Current.Request.Files[file];
if (!(postedFile.ContentType == "application/json" || postedFile.ContentType == "application/xml"))
{
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage()
{
ReasonPhrase = "Wrong content type",
StatusCode = HttpStatusCode.BadRequest
});
}
}
return "OK";
}
POSTMAN
My POSTMAN:
enter image description here
Fiddler
My HttpClient sends the image with PostAsync.
I am not really sure what to do now since this is my first REST Api and I can't really adapt the things I sorted out in other posts yet.
Hopefully you guys can help me out and can give me a direction.
public async Task SendImage(string fullFileName, string fileName)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://x.x.x.x");
var content = new StringContent(fullFileName, Encoding.UTF8, "image/jpg");
HttpResponseMessage response = await client.PostAsync($"/values/file/{fileName}", content);
}
I have several questions about the POST function.
First of all I can successfully access it with PostMan and the fileName is correct.
How do I read the image data and write it to a file?
[HttpPost("file/{fileName}")]
public void Upload(string fileName)
{
Debug.Write(fileName);
}
EDIT:
I setup my environment and I can now send a post via the internet to my published Web Api.
On my app just nothing happens.
For now I just tried to get something of a message to work on but I dont getting one.
[HttpPost("file/{fileName}")]
public HttpResponseMessage Upload(UploadedFile fileName)
{
Debug.Write(fileName);
if (!ModelState.IsValid)
{
}
if (fileName == null)
{
}
string destinationPath = Path.Combine(#"C:\", fileName.FileFullName);
System.IO.File.WriteAllBytes(destinationPath, fileName.Data);
HttpResponseMessage rm = new HttpResponseMessage();
rm.StatusCode = HttpStatusCode.OK;
return rm;
}
1.Your controller should look like this:
//For .net core 2.1
[HttpPost]
public IActionResult Index(List<IFormFile> files)
{
//Do something with the files here.
return Ok();
}
//For previous versions
[HttpPost]
public IActionResult Index()
{
var files = Request.Form.Files;
//Do something with the files here.
return Ok();
}
2.To upload a file you can also use Multipart content:
public async Task UploadImageAsync(Stream image, string fileName)
{
HttpContent fileStreamContent = new StreamContent(image);
fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileStreamContent);
var response = await client.PostAsync(url, formData);
return response.IsSuccessStatusCode;
}
}
3.If you are uploading large files you should consider streaming the files instead, you can read about it here
You're going fine until you're trying to retrieve the image data, I'm afraid.
According to your question:
How do I read the image data and write it to a file?
All you want to do is getting the file's data and its file name and sending it to your service.
I would personally create an UploadedFile class on both sides (client and service side), having the file's name and its data, so:
public class UploadedFile
{
public string FileFullName { get; set; }
public byte[] Data { get; set; }
public UploadedFile(string filePath)
{
FileFullName = Path.GetFileName(Normalize(filePath));
Data = File.ReadAllBytes(filePath);
}
private string Normalize(string input)
{
return new string(input
.Normalize(System.Text.NormalizationForm.FormD)
.Replace(" ", string.Empty)
.ToCharArray()
.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
.ToArray());
}
}
Then you will need, for example, the NewtonSoft's JsonConvert in order to serialize the object and send it through.
So now you would be able to send your data async:
public async Task SendDataAsync(string fullFilePath)
{
if (!File.Exists(fullFilePath))
throw new FileNotFoundException();
var data = JsonConvert.SerializeObject(new UploadedFile(fullFilePath));
using (var client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
await client.UploadStringTaskAsync(new Uri("http://localhost:64204/api/upload/"), "POST", data);
}
}
Now, make sure you correctly handle the request on the server side. If for whatever reason the parameters doesn't match it won't enter into the method (remember having the same model/class - UploadedFile on the service as well).
On the service, just change the arguments of your method and perform something "like this":
[HttpPost]
public HttpResponseMessage Upload(UploadedFile file)
{
if (!ModelState.IsValid)
...
if (file == null)
...
string destinationPath = Path.Combine(_whateverPath, file.FileFullName);
File.WriteAllBytes(destinationPath, file.Data);
}
Hope it helped you having an idea about what to do and what you're actually doing wrong. I've exposed something similar based in my experience.
EDIT: I've actually uploaded an example with both sides working: a simple .NET Core console app which retrieves a file and sends it through POST and a basic WebAPI2 service with a simple controller to retrieve the data. Both ready to go and tested working! Download it here.
Enjoy.
My HttpClient sends the image with PostAsync.
I am not really sure what to do now since this is my first REST Api and I can't really adapt the things I sorted out in other posts yet.
Hopefully you guys can help me out and can give me a direction.
public async Task SendImage(string fullFileName, string fileName)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://x.x.x.x");
var content = new StringContent(fullFileName, Encoding.UTF8, "image/jpg");
HttpResponseMessage response = await client.PostAsync($"/values/file/{fileName}", content);
}
I have several questions about the POST function.
First of all I can successfully access it with PostMan and the fileName is correct.
How do I read the image data and write it to a file?
[HttpPost("file/{fileName}")]
public void Upload(string fileName)
{
Debug.Write(fileName);
}
EDIT:
I setup my environment and I can now send a post via the internet to my published Web Api.
On my app just nothing happens.
For now I just tried to get something of a message to work on but I dont getting one.
[HttpPost("file/{fileName}")]
public HttpResponseMessage Upload(UploadedFile fileName)
{
Debug.Write(fileName);
if (!ModelState.IsValid)
{
}
if (fileName == null)
{
}
string destinationPath = Path.Combine(#"C:\", fileName.FileFullName);
System.IO.File.WriteAllBytes(destinationPath, fileName.Data);
HttpResponseMessage rm = new HttpResponseMessage();
rm.StatusCode = HttpStatusCode.OK;
return rm;
}
1.Your controller should look like this:
//For .net core 2.1
[HttpPost]
public IActionResult Index(List<IFormFile> files)
{
//Do something with the files here.
return Ok();
}
//For previous versions
[HttpPost]
public IActionResult Index()
{
var files = Request.Form.Files;
//Do something with the files here.
return Ok();
}
2.To upload a file you can also use Multipart content:
public async Task UploadImageAsync(Stream image, string fileName)
{
HttpContent fileStreamContent = new StreamContent(image);
fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileStreamContent);
var response = await client.PostAsync(url, formData);
return response.IsSuccessStatusCode;
}
}
3.If you are uploading large files you should consider streaming the files instead, you can read about it here
You're going fine until you're trying to retrieve the image data, I'm afraid.
According to your question:
How do I read the image data and write it to a file?
All you want to do is getting the file's data and its file name and sending it to your service.
I would personally create an UploadedFile class on both sides (client and service side), having the file's name and its data, so:
public class UploadedFile
{
public string FileFullName { get; set; }
public byte[] Data { get; set; }
public UploadedFile(string filePath)
{
FileFullName = Path.GetFileName(Normalize(filePath));
Data = File.ReadAllBytes(filePath);
}
private string Normalize(string input)
{
return new string(input
.Normalize(System.Text.NormalizationForm.FormD)
.Replace(" ", string.Empty)
.ToCharArray()
.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
.ToArray());
}
}
Then you will need, for example, the NewtonSoft's JsonConvert in order to serialize the object and send it through.
So now you would be able to send your data async:
public async Task SendDataAsync(string fullFilePath)
{
if (!File.Exists(fullFilePath))
throw new FileNotFoundException();
var data = JsonConvert.SerializeObject(new UploadedFile(fullFilePath));
using (var client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
await client.UploadStringTaskAsync(new Uri("http://localhost:64204/api/upload/"), "POST", data);
}
}
Now, make sure you correctly handle the request on the server side. If for whatever reason the parameters doesn't match it won't enter into the method (remember having the same model/class - UploadedFile on the service as well).
On the service, just change the arguments of your method and perform something "like this":
[HttpPost]
public HttpResponseMessage Upload(UploadedFile file)
{
if (!ModelState.IsValid)
...
if (file == null)
...
string destinationPath = Path.Combine(_whateverPath, file.FileFullName);
File.WriteAllBytes(destinationPath, file.Data);
}
Hope it helped you having an idea about what to do and what you're actually doing wrong. I've exposed something similar based in my experience.
EDIT: I've actually uploaded an example with both sides working: a simple .NET Core console app which retrieves a file and sends it through POST and a basic WebAPI2 service with a simple controller to retrieve the data. Both ready to go and tested working! Download it here.
Enjoy.
Current code
if (!(context.Exception is exception))
HttpContent requestContent = context.Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
Jsoncontent returns null here as well as
context.request = context.HttpRequestMessage
Seeing below output in local window
context.Request.Content.Headers {
Content-Length: 1458
Content-Type: application/json
}
Allow: {}
ContentDisposition: null
ContentEncoding: {}
ContentLanguage: {}
ContentLength: 1458
ContentLocation: null
ContentMD5: null
ContentRange: null
ContentType: { application/json }
Expires: null
LastModified: null
Results View: Expanding the Results View will enumerate the IEnumerable
context.Request.Content { System.Net.Http.StreamContent }
Headers: {
Content-Length: 1458
Content-Type: application/json
}
How do I retrieve content from header?
If you want content-type and length as json string, you can use
JsonConvert.SerializeObject(Request.Content.Headers);
// Sample output
// "[{\"Key\":\"Content-Length\",\"Value\":[\"29\"]},{\"Key\":\"Content-Type\",\"Value\":[\"application/json\"]}]"
requestContent.ReadAsStringAsync() return empty string because body can only be read once. If you want to read body using ReadAsStringAsync then remove parameter from actions (this will prevent default model binding).
// This will have Request.Content.ReadAsStringAsync as ""
public async Task<IHttpActionResult> Post(MyModel model)
Change it to
// This will have Request.Content.ReadAsStringAsync as json string
public async Task<IHttpActionResult> Post()
Blog explains this in more detail https://blogs.msdn.microsoft.com/jmstall/2012/04/16/how-webapi-does-parameter-binding/
Hope this help
Update
If you need request details in exception filter then you can use following options
Options 1 (easier and I would advise to use this)
Use
context.ActionContext.ActionArguments
Example
var requestJson = $"RequestUrl: {context.Request.RequestUri} " +
$"ActionName: {context.ActionContext.ActionDescriptor.ActionName} " +
$"Arguments: {JsonConvert.SerializeObject(context.ActionContext.ActionArguments)}";
Debug.WriteLine(requestJson);
Options 2 copy RequestStream to MemoryStream and reread from position 0 (copied from link)
string request;
using (var memoryStream = new MemoryStream())
{
var requestStream = await context.Request.Content.ReadAsStreamAsync();
requestStream.Position = 0;
requestStream.CopyTo(memoryStream);
memoryStream.Position = 0;
using (StreamReader streamReader = new StreamReader(memoryStream))
{
request = streamReader.ReadToEnd();
}
}
Debug.WriteLine(request)
So, i'm trying to pass multiple parameters from fiddler to my web api, using FormDataCollection.ReadAsNameValueCollection().
Problem is everytime is send my data, formData comes back as null. I'm not sure what I'm doing wrong here. I've tried decorating formData with a [FromBody] attribute. Also registered JsonMediaTypeFormatter() in the global.asax class.
Any help would be much appreciated.
Please see code below:
[HttpPost]
public HttpResponseMessage PostAccount([FromBody]FormDataCollection formData)
{
if (formData != null)
{
var nValueCol = formData.ReadAsNameValueCollection();
var account = new Account()
{
Email = nValueCol["email"],
Password = nValueCol["password"],
AgreedToTerms = Convert.ToBoolean(nValueCol["agreesToTerms"]),
//LocationAccountCreated = DbGeography.FromText(nValueCol["userLocation"])
};
var userProfile = new UserProfile()
{
FirstName = nValueCol["firstName"],
LastName = nValueCol["lastName"],
DateOfBirth = Convert.ToDateTime(nValueCol["dateOfBirth"])
};
var newAcc = _accountService.CreateAccount(account.Email, userProfile.FirstName, userProfile.LastName,
userProfile.DateOfBirth, account.Email, account.AgreedToTerms,
account.LocationAccountCreated);
var response = Request.CreateResponse(HttpStatusCode.Created);
return response;
}
else
return Request.CreateResponse(HttpStatusCode.NotAcceptable);
}
Sample request:
FormDataCollection is normally associated with application/x-www-form-urlencoded media type.
Your screen shot shows you are trying to send json data. If you don't have a concrete data type for your data and you want to send it as Json you can use an IDictionary<string,string> which will be mapped by the model binder successfully.
You action will look something like...
[HttpPost]
public HttpResponseMessage PostAccount([FromBody]IDictionary<string, string> formData) {
if (formData != null) {
var nValueCol = formData;
//...other code removed for brevity, but can basically stay the same
var response = Request.CreateResponse(HttpStatusCode.Created);
return response;
} else
return Request.CreateResponse(HttpStatusCode.NotAcceptable);
}
Based on your code and the information from your fiddler screen shot, a TestController was created, a request was tested with fiddler like...
POST http://localhost:35979/api/account/create HTTP/1.1
User-Agent: Fiddler
Host: localhost:35979
Content-Type: application/json
Content-Length: 76
{"email":"myemail#email.com",
"firstname":"myFName",
"lastName":"myLName"}
...and the formData was populate with the 3 fields and their data.