My current web API already responding JSON data as below.
public HttpResponseMessage GetFieldInfo()
{
//....
return Ok(GetFieldsInstance()); //GetFieldsInstance returning with DTO class instance.
}
Now, I need to include, file along with JSON response. I could not find any link which shows, how to include filestream and JSON in single response.
For file stream, it will work as below but, not able to find way, how to include JSON object property with filestream.
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "FieldFile";
You can covert (serialize) the file to a base64 string and include it as a property in the JSON response.
public IHttpActionResult GetFieldInfo() {
//...
var model = new {
//assuming: byte[] GetBinaryFile(...)
data = Convert.ToBase64String(GetBinaryFile(localFilePath)),
result = "final",
//...other properties...
};
return Ok(model);
}
The client would then need to convert (desrialize) the base64 string back to your desired file to be used as desired.
Take note that depending on the size of the file it can drastically increase the size of the response and the client should take that into consideration.
Related
I have written a C# Web API in VS Studio and have created numerous DTO's that get serialized into JSON entities which any client can consume. One of my API calls is to return a PDF file, so after some online research, I set up one of the DTO's is set up in the following format. I read somewhere you can do this, but not 100% sure:
public class MyCustomResult
{
public bool Success;
public DateTime? LastRunDate;
public string ErrorCode;
public string Message;
public ByteArrayContent ReportBody;
}
I do not get any errors when return the object as an IHttpActionResult:
return Ok(result);
I can see on the server that the ByteSize of the report byte[] is approx 700K. However, when I retrieve the object on the client, the JSON entity is approx 400B and no byte content in the ByteContentStream. When I run the query in Postman, I get an empty Header, so it appears that the ByteContentStream can't be serialized by Newtonsoft JSON.
Are there any options I should consider?
Here is a scenario where you'd use ByteArrayContent:
using(var req = new HttpRequestMessage(HttpMethod.Post, new Uri("https://example.com"))
{
req.Content = new ByteArrayContent(...);
using(var resp = await _client.SendAsync(req))
{
var data = await resp.Content.ReadAsAsync<object>();
}
}
What you'd want to do is this:
public class MyCustomResult
{
public bool Success;
public DateTime? LastRunDate;
public string ErrorCode;
public string Message;
public byte[] ReportBody; // <-- change this to byte[]
}
var dataToSend = new MyCustomResult(); // fill this in
using(var req = new HttpRequestMessage(HttpMethod.Post, new Uri("https://example.com"))
{
req.Content = new StringContent(
JsonConvert.SerializeObject(dataToSend, Encoding.UTF8, "application/json"));
using(var resp = await _client.SendAsync(req))
{
var data = await resp.Content.ReadAsAsync<object>();
}
}
(note: this code is not tested)
So what will happen is SerializeObject will convert that byte array into a Base64 string then send it.
The consumer would then have to decode that Base64 string. If it's another Newtonsoft.Json client and the model definitions match, then it will automatically decode it for you.
I understand you are doing an API endpoint. The above examples are to show the use of ByteArrayContent and why it exists in .NET. How you are returning data is correct: return Ok(response); as long as you fix your model.
So to sum it up:
ByteArrayContent is an implementation of HttpContent which is supposed to be used as a response body only. It's not to be used in conjunction with a JSON response.
This piece of code will put all the data(string) as an XML format in the Postman. But when I try to save the response in the Postman so, it will save as response.xml, which I don't want. I want that Postman will save my XML content in the form of filename.xml.
So, how should I proceed with? I have to google it and found that something like attachment, content-type, and all must be used. But, didn't solve my case. Please help with your suggestion.
For simplicity, I have reduced the code. Here, graphics is string datatype.
public ActionResult GetXML()
{
string filename = "Demo";
return Content(graphics, "application/xml");
}
You need to set Content-Disposition header so the that browser prompts for saving response.
You can do that by using following approach.
public IActionResult Getxml()
{
var xmlData =
"<records><record><Name>Camacho, Sydnee Q.</Name><Id>1</Id><Age>19</Age><City>Podolsk</City></record><record><Name>Bowman, Lester V.</Name><Id>2</Id><Age>21</Age><City>Padang</City></record></records>";
//sampleFile.xml can be replaced by any filename of your choice.
var fileName = "sampleFile.xml";
Response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}");
return Content(xmlData, "application/xml", Encoding.UTF8);
}
With this change when this URL is browsed in the browser it will prompt save dialog with sampleFile.xml populated as filename.
Also when you save the response from postman it will show sampleFile.xml populated as file name in the dialog.
I hope this will help you solve your issue.
I would suggest to use the FileContentResult for a file download.
public ActionResult GetXML()
{
var fileName = "Demo.xml";
var xml = "...";
return new FileContentResult(Encoding.UTF8.GetBytes(xml), "application/xml; charset=utf-8")
{
FileDownloadName = fileName,
};
}
You need to set the response headers so that the download client automatically treats the content as a "file", the key is the ContentDisposition header, this is how we pass the filename back.
public HttpResponseMessage GetXMLFile()
{
string filename = "Demo";
var byteArray = System.Text.Encoding.UTF8.GetBytes(graphics);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(new MemoryStream(byteArray), byteArray.Length)
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") //attachment will force download
{
FileName = filename
};
return result;
}
I need to make a POST call from my Xamarin forms app where I need to upload a file object as it is to API using POST Call. Is there any way to make it possible?
if you send file object using Base64 or Byte[] then it will allowed only limited may be upto 2-4 Mb but if you have a larger image than that it will not support.
So, Solution is Post Stream Content Like,
var file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Full,
CompressionQuality = 100
});
Create Object of MediaFile like, public MediaFile AttachedImage; and Store file into it so Memory stream will not lost. Like,AttachedImage = file
Post Code on API,
HttpClient httpClient = new HttpClient();
MultipartFormDataContent mt = new MultipartFormDataContent();
AttachedImage.GetStream().Position = 0;
StreamContent imagePart = new StreamContent(AttachedImage.GetStream());
imagePart.Headers.Add("Content-Type", ImageType);
mt.Add(imagePart, String.Format("file"), String.Format("bk.jpeg"));
requestMessage.Content = mt;
var response = await httpClient.PostAsync("Your URL", mt);
if (response.IsSuccessStatusCode)
{
var responseString = await response.Content.ReadAsStringAsync();
var objRootObjectuploadImage = JsonConvert.DeserializeObject<RootObjectuploadImage>(responseString);
if (objRootObjectuploadImage != null)
{
}
else
{
}
}
else
{
Loading(ActIndicator, false);
await DisplayAlert(res.LAlert, "webserver not responding.", res.LOk);
}
NO, it is not possible to send file object. You can send as a json by converting the file in the Base64 string. This is the advised proven solution. This link has code for converting back and forth from Base64.
So I have a web api which returns a byte array like this
public byte[] Validate()
{
byte[] buffer = licensePackage.ToByteArray();
return buffer;
}
The thing is when I get it on client it is different size and different byte array, I googled and found this link helpful http://www.codeproject.com/Questions/778483/How-to-Get-byte-array-properly-from-an-Web-Api-Met.
But can I know why this happens? Also, what is an alternate way to send back that file contents from the server?
With the given information I think it must have something to do with content negotiation. I can't tell the reason, but what I'm sure it's that there is a different approach to serve files behind a Web Api.
var response = Request.CreateResponse(HttpStatusCode.OK);
var stream = new FileStream(pathToFile, FileMode.Open, FileAccess.Read);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return ResponseMessage(response);
With this solution, you serve the file contents returning an IHttpActionResult instance. And, returning a response with StreamContent you are returning a stream that must not be modified by Web Api Content Negotation.
I have a web api method that downloads a file:
public HttpResponseMessage DownloadDocument()
{
XDocument xDoc = GetXMLDocument();
MemoryStream ms = new MemoryStream();
xDoc.Save(ms);
ms.Position = 0;
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(ms),
};
// Sending metadata
// Is this the right way of sending metadata about the downloaded document?
response.Content.Headers.Add("DocumentName", "statistics.xml");
response.Content.Headers.Add("Publisher", "Bill John");
return response;
}
Is this the correct way of sending metadata about the StreamContent i return? or should i return a different type of Content?
For the filename you'd better use the Content-Disposition response header which is specifically designed for this purpose. As far as the Publisher is concerned, you could indeed use a custom HTTP header (as you did) or simply include it as some sort of metadata tag directly inside the payload. For example:
public HttpResponseMessage Get()
{
XDocument xDoc = GetXMLDocument();
var response = this.Request.CreateResponse(
HttpStatusCode.OK,
xDoc.ToString(),
this.Configuration.Formatters.XmlFormatter
);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "statistics.xml"
};
response.Headers.Add("Publisher", "Bill John");
return response;
}