ASP.NET Core MVC 2
I need to send big raw data (the byte[]). My POST-request contains the Content-Type: application/octet-stream header.
This is the controller action:
[HttpPost]
public async Task<IActionResult> RawBinaryDataManual()
{
using (var ms = new MemoryStream())
{
await Request.Body.CopyToAsync(ms);
var bytes = ms.ToArray();
return StatusCode((int) HttpStatusCode.OK);
}
}
It works fine when I post 22Mb file, but I get the Exception thrown: 'Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException' error if I try to send the 38Mb file.
How can I fix this problem?
UPD
Thanks to phuzi - he wrote the link in the comments which helped to me to solve my problem.
Related
I'm trying to drag and drop file upload with a progress bar.
I have a div which is listening to files being dropped on which is working perfectly.
I'm then..
//Setting up a XmlHttpRequest
xhr = new XMLHttpRequest();
//Open connection
xhr.open("post", "api/ImageUpload", true);
// Set appropriate headers
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Type", uf.type);
xhr.setRequestHeader("X-File-Name", uf.name);
xhr.setRequestHeader("X-File-Size", uf.size);
This sends fine, with the stream as the body of the request to the Web API (not async).
[System.Web.Mvc.HttpPost]
public string Post()
{
Stream stream = HttpContext.Current.Request.InputStream;
String filename = HttpContext.Current.Request.Headers["X-File-Name"];
FileModel file = uploadService.UploadFile(stream, filename);
return file.Id.ToString();
}
I'm trying to chance the request to "public async Task< string> Post(){ }
If the method was using a multipart form on the page instead of XmlHttpRequest I would have used "await Request.Content.ReadAsMultipartAsync(provider)" but this doesn't seem to be populated at the time I need it.
So what is the correct was to handle and an Async call from XmlHttpRequest on a Web API in order to record progress during the request with XHR's progress event?
I have looked at a great deal of pages so far to find a solution but this is the page I have used primarily.
http://robertnyman.com/html5/fileapi-upload/fileapi-upload.html
Thanks for any help
Oliver
It looks like someone else had the same question with you and got an answer yet. please have a look at ASP.NET MVC 4 Web Api ajax file upload.
And here is an example from microsoft http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2.
I combined the two above solution together and worked for me (just adjust a little bit)
one line change in Javascritp
xhr.open("post", "api/upload", true);
Save the file using stream
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var fileName = Path.Combine(root, Request.Headers.GetValues("X-File-Name").First());
try
{
var writer = new StreamWriter(fileName);
await Request.Content.CopyToAsync(writer.BaseStream);
writer.Close();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}
I get this error in my client (an ASP.NET MVC application) from a call to my ASP.NET Web API. I checked and the Web API is returning the data alright.
No MediaTypeFormatter is available to read an object of type
'IEnumerable`1' from content with media type 'text/plain'.
I believe that I can inherit from DataContractSerializer and implement my own serializer which can attach the Content-Type HTTP header as text/xml.
But my question is: is that necessary?
Because if it was, it would mean that the default DataContractSerializer does not set this essential header. I was wondering if Microsoft could leave such an important thing out. Is there another way out?
Here's the relevant client side code:
public ActionResult Index()
{
HttpClient client = new HttpClient();
var response = client.GetAsync("http://localhost:55333/api/bookreview/index").Result;
if (response.IsSuccessStatusCode)
{
IEnumerable<BookReview> reviews = response.Content.ReadAsAsync<IEnumerable<BookReview>>().Result;
return View(reviews);
}
else
{
ModelState.AddModelError("", string.Format("Reason: {0}", response.ReasonPhrase));
return View();
}
}
And here's the server side (Web API) code:
public class BookReviewController : ApiController
{
[HttpGet]
public IEnumerable<BookReview> Index()
{
try
{
using (var context = new BookReviewEntities())
{
context.ContextOptions.ProxyCreationEnabled = false;
return context.BookReviews.Include("Book.Author");
}
}
catch (Exception ex)
{
var responseMessage = new HttpResponseMessage
{
Content = new StringContent("Couldn't retrieve the list of book reviews."),
ReasonPhrase = ex.Message.Replace('\n', ' ')
};
throw new HttpResponseException(responseMessage);
}
}
}
I believe (because I don't have time to test it now) that you need to explicitly set the Status Code on the responseMessage you are passing to HttpResponseException. Normally, HttpResponseException will set the status code for you, but because you are providing a responsemessage explicitly, it will use the status code from that. By default, `HttpResponseMessage has a status code of 200.
So what is happening is you are getting an error on the server, but still returning a 200. Which is why your client is trying to deserialize the text/plain body produced by StringContent, as if it were an IEnumerable.
You need to set
responseMessage.StatusCode = HttpStatusCode.InternalServerError
in your exception handler on the server.
How about just using ReadAsStringAsync if your WebAPI is expecting to return content in plain text?
response.Content.ReadAsStringAsync().Result;
I am using Web api controller as follows:
[HttpPost]
public HttpResponseMessage PostMethod(string filename)
{
Stream downloadStream = BL.method(fileName);
HttpResponseMessage response = new HttpResponseMessage();
response.content= new StreamContent(downloadStream);
return response;
}
When I try to call the above method using fiddler I am getting an exception saying
'downloadStream.ReadTimeout' threw an exception of type
'System.InvalidOperationException'.
Can the stream be set in response and sent? Is there any modification for the above code?
There seems to be a problem with your stream. Without knowing how stream is generated it is difficult to say. If you replace BL.method(fileName); with just loading the file yourself using FileStream this should work (I just tested it myself).
On the side note, there are a few problems with your approach:
You use POST. Since you are not changing anything GET is better.
You are not setting ContentType header so client can have problem using resource
You are not disposing the stream so this stream could stay in limbo and generally not good.
Try using the PushStreamContent, maybe by not buffering the file in memory, you might avoid your timeout.
[HttpPost]
public HttpResponseMessage PostMethod(string filename)
{
Stream downloadStream = BL.method(fileName);
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new PushStreamContent((responseStream, httpContent, tc) => {
downloadStream.CopyTo(responseStream);
responseStream.Close();
}, "application/octet-stream");
return response;
}
I am trying to manually write my response stream and close it so that I can continue doing some stuff after the response is closed. I acheive this successfully by doing the following:
Response.StatusCode = 200
Response.ContentType = "application/json; charset=utf-8"
Response.Write(j)
Response.Flush()
Response.Close()
DOWORK()
This works perfect in most instances, however for Chrome / Flash, there is a bug in flash were it is causing this to assume it's an IO error. When analyzing the headers, the difference between manually sending a response with Return Json(results) and how I did it above is that when I return the data normally it uses a header of:
Content-Length: 44
Where as when I send it with the code above I get:
Transfer-Encoding: chunked
Is it possible to do what I want but not have the data chunked? I know this is not ASP.net specific, rather a bug in flash for chrome, but I would like to solve this.
Following my comment, I was thinking about something like:
public class CustomResult : JsonResult
{
private Action afterAction;
private object obj = null;
public CustomResult(object obj, Action afterAction) : base()
{
this.JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet;
this.Data = obj;
this.afterAction = afterAction;
}
public override void ExecuteResult(ControllerContext context)
{
base.ExecuteResult(context);
afterAction();
}
}
Now you could call it in controller action:
return new CustomResult(obj, () => { //custom code here, will be executed later });
I am trying to implement restful protocol on jetty server. I have runnable server and i can access it from my rest client. My server side project is a maven project. I have a problem about the character encoding.When i check response, before send it from controller, there is no encoding problem. But after i return response to client, i see broken data. Response header is UTF-8. Also i have a listener for this problem and i am setting to request and response to UTF-8. I guess problem happens when i try to write my response data to response.
#GET
#Path("/")
#Produces({"application/xml;charset=UTF-8","application/json;charset=UTF-8"})
public String getPersons(#Context HttpServletRequest request, #Context HttpServletResponse response) {
List<Person> persons = personService.getPersons(testUserId, collectionOption, null);
if (persons == null) {
persons = new ArrayList<Person>();
}
String result = JsonUtil.listToJson(persons);
//result doesnt has any encoding problem at this line
response.setContentType("application/json");
response.setContentLength(result.length());
response.setCharacterEncoding("utf-8");
//i guess problem happen after this line
return result;
}
Is there any jetty configuration or resteasy configuration for it? Or is there any way to solve this problem? Thanks for your helps.
Which resteasy version are you using? There is a known issue (RESTEASY-467) with Strings in 2.0.1 an prior.
These are your options:
1) force the encoding returning byte[]
public byte[] getPersons
and then
return result.getBytes("UTF8");
2) return List (or create a PersonListing if you need it)
public List<Person> getPersons
and let resteasy handle the json transformation.
3) return a StreamingOutput
NOTE: with this option the "Content-Length" header will be unknown.
return new StreamingOutput()
{
public void write(OutputStream outputStream) throws IOException, WebApplicationException
{
PrintStream writer = new PrintStream(outputStream, true, "UTF-8");
writer.println(result);
}
};
4) upgrade to 2.2-beta-1 or newer version.