Response Content-Length mismatch: too few bytes written - c#

My ASP.NET Core app uses "out-of-box" external login authentication. What I want to implement - on facebook challenge I want to wrap redirect url and return it as json to consume in jquery frontend. But after request ends I see 500 error in browser and next error in application console:
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HLV651D6KVJC", Request id "0HLV651D6KVJC:00000005": An unhandled exception was thrown by the
application. System.InvalidOperationException: Response Content-Length
mismatch: too few bytes written (0 of 470).
My external login action, nothing special to look at
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
Facebook authentication configuration:
services.AddAuthentication().AddFacebook(facebookOptions =>
{
facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
async (x) =>
{
UTF8Encoding encoding = new UTF8Encoding();
var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });
byte[] bytes = encoding.GetBytes(content);
x.Response.StatusCode = 200;
x.Response.ContentLength = bytes.Length;
x.Response.ContentType = "text/plain";
x.Response.Body = new MemoryStream();
await x.Response.WriteAsync(content);
// at this point I see that x.Response.Body.Length == 470, but message states there are 0 of 470 written
};
});
Is there any way I could make it work?

This can also happen when using new C# using syntax like this:
using var ms = new MemoryStream();
using var writer = new StreamWriter(ms);
writer.WriteLine("my content");
memoryStream.Position = 0;
return File(ms, "text/plain");
in this case, the MemoryStream is accessed before the StreamWriter is flushed. Either use old syntax for the StreamWriter:
using var ms = new MemoryStream();
using (var writer = new StreamWriter(ms, Encoding.UTF8, -1, true))
{
writer.WriteLine("my content");
}
memoryStream.Position = 0;
return File(ms, "text/plain");
or flush the writer:
using var ms = new MemoryStream();
using var writer = new StreamWriter(ms);
writer.WriteLine("my content");
writer.Flush();
memoryStream.Position = 0;
return File(ms, "text/plain");

Changed code to write to original response stream and it works now.
facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
async (x) =>
{
var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });
x.Response.StatusCode = 200;
x.Response.ContentType = "text/plain";
await x.Response.WriteAsync(content);
};

You can use something like this:
var stream = new MemoryStream();
/// writing to the stream
if (stream.CanSeek)
{
stream.Seek(0, SeekOrigin.Begin);
}
/// then read stream

Related

Not receiving JSON data from Microsoft FaceAPI

I am trying to get JSON data from a picture using Microsoft's FaceAPI. I am receiving a StatusCode OK, but am not getting anything significant back. I have verified that the MemoryStream has the right data (which I am getting from an Image control) by saving it to a file.
private async Task<string> GetJSON()
{
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "mykey");
// Request parameters
queryString["returnFaceId"] = "true";
queryString["returnFaceLandmarks"] = "false";
var uri = "https://api.projectoxford.ai/face/v1.0/detect?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = ImageToByte();
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response = await client.PostAsync(uri, content);
}
return "";
}
private byte[] ImageToByte()
{
using (MemoryStream stream = new MemoryStream())
{
videoBox.Dispatcher.Invoke(delegate
{
var encoder = new PngBitmapEncoder();
var flippedBitmap = new TransformedBitmap();
flippedBitmap.BeginInit();
flippedBitmap.Source = (BitmapSource)videoBox.Source;
var transform = new ScaleTransform(-1, 1);
flippedBitmap.Transform = transform;
flippedBitmap.EndInit();
encoder.Frames.Add(BitmapFrame.Create(flippedBitmap));
encoder.Save(stream);
});
using (FileStream test = new FileStream("snapshot.bmp", FileMode.Create))
{
stream.Position = 0;
stream.CopyTo(test);
}
return stream.ToArray();
}
}
You'll want to call await response.Content.ReadAsStringAsync() to get the JSON.
Alternatively, you can use the Microsoft.ProjectOxford.Face NuGet package which does the plumbing for you, plus provide C# types thereby relieving you the tedium of parsing the JSON.
I am not a c# programmer but after looking at your code, method GetJSON is returning hard coded empty string that might be the cause you are not getting anything back from the server after invoking this method or second reason could be your asynchronous server configuration is not working properly thus its returning blank first and doing actual operation later.

Web Api 2 not returning ByteArrayContent to HTTPClient

I am putting together a test application using WebApi2 and HttpClient in a win forms app.
I have come accross an issue where my HttpClient request to a WebApi2 controller which returns an HttpResponseMessage doesnt return the ByteArrayContent.
WebApiController Code
[HttpGet]
public HttpResponseMessage DownloadFilePart(string fileName)
{
var path = Server.MapPath("~/App_Data/uploads/" + fileName);
var fileArray = System.IO.File.ReadAllBytes(path);
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(fileArray)
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue(System.Web.MimeMapping.GetMimeMapping(fileName));
response.Content.Headers.ContentLength = fileArray.Length;
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
return response;
}
WinForms Code using HttpClient
static async void GetFilePart(string hostrUri)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri(hostrUri)
};
var request = new HttpRequestMessage(HttpMethod.Get, "/Home/DownloadFilePart/?fileName=Test.txt");
var responseMessage = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var memoryStream = new MemoryStream();
var stream = await responseMessage.Result.Content.ReadAsByteArrayAsync();
var fileToWriteTo = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\Temp\\Test.txt";
using (var fileStream = new FileStream(fileToWriteTo, FileMode.Create, FileAccess.Write, FileShare.None))
{
//copy the content from response to filestream
fileStream.Write(stream, 0, stream.Length);
}
}
When the request return from the WebApi and I write the bytes to file all that is written into the file is the actual headers from the WebApi response. Has anyone any ideas what the issue could be here?
Thanks
Your problem is here
httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
HttpCompletionOption.ResponseHeadersRead is summarized as
The operation should complete as soon as a response is available and headers are read. The content is not read yet.
This would explain why you only get the headers in your response.
Either remove it completely or change it to HttpCompletionOption.ResponseContentRead
static async void GetFilePart(string hostrUri)
{
var httpClient = new HttpClient
{
BaseAddress = new Uri(hostrUri)
};
var request = new HttpRequestMessage(HttpMethod.Get, "/Home/DownloadFilePart/?fileName=Test.txt");
var responseMessage = await httpClient.SendAsync(request);
var byteArray = await responseMessage.Content.ReadAsByteArrayAsync();
var fileToWriteTo = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + "\\Temp\\Test.txt";
using (var fileStream = new FileStream(fileToWriteTo, FileMode.Create, FileAccess.Write, FileShare.None))
{
//copy the content from response to filestream
fileStream.Write(byteArray, 0, byteArray.Length);
}
}

Not Receiving Data from Route C#

I'm attempting to return an image from a server route, but I'm getting one that is 0 bytes. I suspect it has something to do with how I'm using the MemoryStream. Here's my code:
[HttpGet]
[Route("edit")]
public async Task<HttpResponseMessage> Edit(int pdfFileId)
{
var pdf = await PdfFileModel.PdfDbOps.QueryAsync((p => p.Id == pdfFileId));
IEnumerable<Image> pdfPagesAsImages = PdfOperations.PdfToImages(pdf.Data, 500);
MemoryStream imageMemoryStream = new MemoryStream();
pdfPagesAsImages.First().Save(imageMemoryStream, ImageFormat.Png);
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new StreamContent(imageMemoryStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = pdf.Filename,
DispositionType = "attachment"
};
return response;
}
Through debugging I have verified that the PdfToImages method is working and that imageMemoryStream gets filled with data from the line
pdfPagesAsImages.First().Save(imageMemoryStream, ImageFormat.Png);
However in running it, I receive an attachment that is properly named but is 0 bytes. What do I need to change in order to receive the whole file? I think it's something simple but I'm not sure what. Thanks in advance.
After writing to the MemoryStream, Flush it then set Position to 0:
imageMemoryStream.Flush();
imageMemoryStream.Position = 0;
You should rewind MemoryStream to beginning before passing it to response. But you'd better use PushStreamContent:
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new PushStreamContent(async (stream, content, context) =>
{
var pdf = await PdfFileModel.PdfDbOps.QueryAsync(p => p.Id == pdfFileId);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = pdf.Filename,
DispositionType = "attachment"
};
PdfOperations.PdfToImages(pdf.Data, 500).First().Save(stream, ImageFormat.Png);
}, "image/png");
return response;

Create file to Onedrive programmatically from C#?

I want to create a doc, docx, pptx or excel file from C# direct to my Onedrive account.
I have try this but it's not working for me. Anybody have any idea what I did wrong ? Thanks
public async Task<ActionResult> CreateWordFile()
{
LiveLoginResult loginStatus = await authClient.InitializeWebSessionAsync(HttpContext);
if (loginStatus.Status == LiveConnectSessionStatus.Connected)
{
var fileData = new Dictionary<string, object>();
fileData.Add("name", "Document.docx");
fileData.Add("Content-Type", "multipart/form-data; boundary=A300x");
fileData.Add("type", "file");
LiveOperationResult getResult = await connectedClient.PostAsync("me/skydrive/files", fileData);
}
return View();
}
EDITED:
The error that I get is this one:
"The header 'Content-Type' is missing the required parameter: 'boundary'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: Microsoft.Live.LiveConnectException: The header 'Content-Type' is missing the required parameter: 'boundary'."
A couple of things:
The dictionary provided to PostAsync only populates fields in the request body, and so adding Content-Type in there doesn't have any effect
File resources are mean to be created using the UploadAsync method, which requires content. I don't believe there's an API you can call to tell the service to create a blank Office document.
I have something else. I created an HttpWebRequest and I set some parameters. Now it create the file to my onedrive account as a docx but when I try to open the file from my account an error message appear and it's saying something like "Something wrong has happened. We could not open the file". The file exist but it can't be open.
The code that I wrote is this. Any suggestions ?
public async Task<ActionResult> CreateWordFile()
{
string body = "--A300x\r\n"
+ "Content-Disposition: form-data; name=\"file\"; filename=\"csm.docx\"\r\n"
+ "Content-Type: application/octet-stream\r\n"
+ "\r\n"
+ "This is some content\r\n"
+ "\r\n"
+ "--A300x--\r\n";
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(body);
Stream stream = new MemoryStream(fileBytes);
LiveLoginResult loginStatus = await authClient.InitializeWebSessionAsync(HttpContext);
if (loginStatus.Status == LiveConnectSessionStatus.Connected)
{
connectedClient = new LiveConnectClient(this.authClient.Session);
string url = "https://apis.live.net/v5.0/me/skydrive/files?access_token=" + this.authClient.Session.AccessToken;
HttpWebRequest httpWebRequest2 = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest2.ContentType = "multipart/form-data; boundary=A300x";
httpWebRequest2.Method = "POST";
httpWebRequest2.KeepAlive = true;
httpWebRequest2.Credentials = System.Net.CredentialCache.DefaultCredentials;
httpWebRequest2.ContentLength = fileBytes.Length;
Stream stream2 = httpWebRequest2.GetRequestStream();
stream2.Write(fileBytes, 0, fileBytes.Length);
WebResponse webResponse2 = httpWebRequest2.GetResponse();
}
return View();
}
Finally I create a docx file from c#. I put here the solution (the code from the method is not refactored so it can be split in a severeal methods).
public async Task<ActionResult> CreateWordFile()
{
LiveLoginResult loginStatus = await authClient.InitializeWebSessionAsync(HttpContext);
if (loginStatus.Status == LiveConnectSessionStatus.Connected)
{
connectedClient = new LiveConnectClient(this.authClient.Session);
string url = "https://apis.live.net/v5.0/me/skydrive/files?access_token=" + this.authClient.Session.AccessToken;
MemoryStream streamDoc = new MemoryStream();
DocX doc = DocX.Create(streamDoc);
string headlineText = "Constitution of the United States";
string paraOne = ""
+ "We the People of the United States, in Order to form a more perfect Union, "
+ "establish Justice, insure domestic Tranquility, provide for the common defence, "
+ "promote the general Welfare, and secure the Blessings of Liberty to ourselves "
+ "and our Posterity, do ordain and establish this Constitution for the United "
+ "States of America.";
// A formatting object for our headline:
var headLineFormat = new Formatting();
headLineFormat.FontFamily = new System.Drawing.FontFamily("Arial Black");
headLineFormat.Size = 18D;
headLineFormat.Position = 12;
// A formatting object for our normal paragraph text:
var paraFormat = new Formatting();
paraFormat.FontFamily = new System.Drawing.FontFamily("Calibri");
paraFormat.Size = 10D;
doc.InsertParagraph(headlineText, false, headLineFormat);
doc.InsertParagraph(paraOne, false, paraFormat);
doc.Save();
var docFile = File(streamDoc, "application/octet-stream", "FileName.docx");
MemoryStream streamFile = new MemoryStream();
docFile.FileStream.Position = 0;
docFile.FileStream.CopyTo(streamFile);
var bites = streamFile.ToArray();
Stream stream2 = new MemoryStream(bites);
try
{
LiveOperationResult getResult = await connectedClient.UploadAsync("me/skydrive", docFile.FileDownloadName, stream2, OverwriteOption.Overwrite);
}
catch(WebException ex)
{
}
}
return View("~/Views/Auth/EditFile.cshtml");
}
I also foud the answer to create an xlsx file.
public async Task<ActionResult> CreateExcelFile()
{
LiveLoginResult loginStatus = await authClient.InitializeWebSessionAsync(HttpContext);
if (loginStatus.Status == LiveConnectSessionStatus.Connected)
{
connectedClient = new LiveConnectClient(this.authClient.Session);
string url = "https://apis.live.net/v5.0/me/skydrive/files?access_token=" + this.authClient.Session.AccessToken;
XSSFWorkbook wb = new XSSFWorkbook();
// create sheet
XSSFSheet sh = (XSSFSheet)wb.CreateSheet("Sheet1");
// 10 rows, 10 columns
for (int i = 0; i < 100; i++)
{
var r = sh.CreateRow(i);
for (int j = 0; j < 100; j++)
{
r.CreateCell(j);
}
}
MemoryStream stream = new MemoryStream();
wb.Write(stream);
stream.Dispose();
var arrBites = stream.ToArray();
MemoryStream newStream = new MemoryStream(arrBites);
var docFile = File(newStream, "application/octet-stream", "Excel.xlsx");
MemoryStream streamFile = new MemoryStream();
docFile.FileStream.Position = 0;
docFile.FileStream.CopyTo(streamFile);
var bites = streamFile.ToArray();
Stream stream2 = new MemoryStream(bites);
try
{
LiveOperationResult getResult = await connectedClient.UploadAsync("me/skydrive", docFile.FileDownloadName, stream2, OverwriteOption.Overwrite);
}
catch (WebException ex)
{
}
}
return View();
}

Image upload from WP7 to Web Api

I have seen few other examples online doing the same, but I am not sure why its not working for me.
I have created a simple windows phone 7 app, which uses PhotoChooserTask.
It sends image to the server using Web Api.
Here is the code in windows phone project:
void selectphoto_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
var image = new Image();
image.Source = new BitmapImage(new Uri(e.OriginalFileName));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:59551/api/controllername");
request.Method = "POST";
request.ContentType = "multipart/form-data";
//private method to convert bitmap image to byte
byte[] str = BitmapToByte(image);
// Getting the request stream.
request.BeginGetRequestStream
(result =>
{
// Sending the request.
using (var requestStream = request.EndGetRequestStream(result))
{
using (StreamWriter writer = new StreamWriter(requestStream))
{
writer.Write(str);
writer.Flush();
}
}
// Getting the response.
request.BeginGetResponse(responseResult =>
{
var webResponse = request.EndGetResponse(responseResult);
using (var responseStream = webResponse.GetResponseStream())
{
using (var streamReader = new StreamReader(responseStream))
{
string srresult = streamReader.ReadToEnd();
}
}
}, null);
}, null);
}
On the Web API I got the following code for the POST method:
public Task<HttpResponseMessage> Post()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
// Read the form data and return an async task.
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Image img = Image.FromFile(file.LocalFileName);
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
});
return task;
}
}
However I am not sure why IsMimeMultipartContent is returning false always. Even if I bypass this check, no file is saved in the App_Data folder.
Can anyone please help. Thanks.
EDITED
Based on Darrel's response I have modified the POST method in ApiController. But I still do not get any data. A blank image is created on the server. Here is my code:
public HttpResponseMessage Post()
{
var task = Request.Content.ReadAsStreamAsync();
task.Wait();
Stream requestStream = task.Result;
string root = HttpContext.Current.Server.MapPath("~/App_Data");
root = System.IO.Path.Combine(root, "xyz.jpg");
try
{
FileStream fs = System.IO.File.OpenWrite(root);
requestStream.CopyTo(fs);
fs.Close();
}
catch (Exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Created;
return response;
}
You are not sending a representation that is multipart/form. You are just sending a stream of bytes which is application/octet-stream. Just use Request.Content.ReadAsStreamAsync() on the server and copy the stream to a file.

Categories