Returning a JSON stream from an ApiController - asp.net-mvc

I've got a large JSON Object which I need to return from an MVC ApiController.
I can't simply return the JSON Object as the internal serializer barfs on the serialized contents being too long.
I don't want to serialize to string and then return the string as we could be dealing 5-6MB of data and that seems like a waste of memory for no purpose.
What I want to do is use the Newtonsoft JSON Serializer to write to the response stream directly.
Dim Ret = Client.Search(Model) ''Get the results object
Dim Response As New HttpResponseMessage()
Using MemoryStream As New IO.MemoryStream
Using StreamWriter As New StreamWriter(MemoryStream)
Using JsonWriter As JsonWriter = New JsonTextWriter(StreamWriter)
Dim Serializer As New JsonSerializer
Serializer.Serialize(JsonWriter, Ret)
End Using
End Using
Response.Content = New StreamContent(MemoryStream)
Return Response
End Using
Unfortunately, this results in the connection being reset unexpectedly - I'm guessing this is due to the stream being disposed of before it's finished sending the result?
Even if I strip out all the Using blocks and just leave everything for the GC as here:
Dim Ret = Client.Search(Model)
Dim Response As New HttpResponseMessage()
Dim MemoryStream As New IO.MemoryStream
Dim StreamWriter As New StreamWriter(MemoryStream)
Dim JsonWriter As JsonWriter = New JsonTextWriter(StreamWriter)
Dim Serializer As New JsonSerializer
Serializer.Serialize(JsonWriter, Ret)
Response.Content = New StreamContent(MemoryStream)
Return Response
I get a 0-byte response instead. Can someone please point me in the right direction?

Have you tried checking timeout settings in web.config/machine.config? Maybe IIS decides that it's taking too long to get Response.Content.
Anyhow, you should check this issue out with Failed Requests Using Tracing - that should point you in the right direction.

It turns out this was a silly mistake on my part... I wasn't flushing the writer and resetting the stream position...
JsonWriter.Flush()
MemoryStream.Seek(0, StreamPosition.Beginning)
Solved the problem

Related

how to access ViewBag together with Json

I am implementing autocomplete functionality by returning database values with the help of Json and Linq.I need to implement edit functionality. For edit functionality I need to return viewBag data to my view. But I couldn't access my viewBag while returning Json result.
public JsonResult Index(string Prefix,int id = 0)
{
SqlConnection sqcon2 = new SqlConnection(conn);
SqlCommand cmd2 = new SqlCommand();
SqlDataAdapter sd2 = new SqlDataAdapter(cmd2);
DataTable dt2 = new DataTable();
cmd2.Connection = sqcon2;
cmd2.CommandText = "sps_userLocationByID";
cmd2.CommandType = System.Data.CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("#id", id);
sqcon2.Open();
sd2.Fill(dt2);
sqcon2.Close();
foreach (DataRow dr2 in dt2.Rows)
{
ViewBag.cityName = dr2["CityName"].ToString();
ViewBag.Name = dr2["Name"].ToString();
}
SqlConnection sqcon = new SqlConnection(conn);
SqlCommand cmd = new SqlCommand();
SqlDataAdapter sd = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
cmd.Connection = sqcon;
cmd.CommandText = "sps_userCity";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
sqcon.Open();
sd.Fill(dt);
sqcon.Close();
List<CityModel> ObjList = new List<CityModel>();
foreach (DataRow dr in dt.Rows)
{
CityModel st = new CityModel();
st.CityName = dr["CityName"].ToString();
ObjList.Add(st);
}
var CityName = (from N in ObjList
where
N.CityName.ToLower().StartsWith(Prefix.ToLower())
select new { N.CityName });
return Json(CityName, JsonRequestBehavior.AllowGet);
}
you can use viewbag if you call another Actionresult method via Ajax.
ViewBag only exists in the context of a Razor view, which itself only exists if you're returning View(), PartialView(), etc. If you're returning JSON, that's all you get. You cannot utilize ViewBag.
That said, I think you're confused in general about how things work though. Initially your app is responding with an HTML document. Everything else, your action, view, etc. is all about getting to this HTML document that can finally be returned to the client, a web browser in this case.
Once that response has been sent. The server is done. Client-side, the web browser parses the document and creates various object models: the DOM, CSSOM, etc. It then uses these object models to render the page (the paint). During and after this process, your JavaScript is run. All of this is happening client-side, and the server doesn't know or care; it's already done its job.
Then, you're wanting some new information from the server. That requires a new request from the client, which will result in a new response from the server. That request could come in the form of navigation by the user within the browser (in which case the whole browser window/tab view will change) or in the form of an AJAX request, which leaves the current view in the browser window/tab as-is.
XMLHttpRequest, the actual JavaScript client class responsible for making AJAX requests, is what's referred to as a "thin client". It makes requests and receives responses, but it does not do anything with said response, in ontrast to a "thick client" like a web browser, which takes the response and does all the object model creation and rendering. Instead, a callback function is merely invoked. It is your responsibility to do something in that callback with the response you receive (update the DOM, etc.)
All this is to say that doing anything with ViewBag while returning a JSON response to an AJAX request makes absolutely zero sense, because the view is not changing. You seem to be imagining that change ViewBag would somehow automatically update something on the page that was previously added via ViewBag in your initial Razor view. That's not the case, and it's not how things work. If you want to alter something on the page, the new value needs to be returning as part of the JSON response from your AJAX request, and then you must use that new value to alter the DOM accordingly.

Spring Web service SoapMessage.writeTo(OutputStream o)

We are trying to convert SoapMessage to String value, In our code, we are writing the SoapMessage to ByteArrayOutputStream. But with ByteArrayOutputStream, there is lot of issues like memory leakage issue and performance issues
Please find below our code:
MessageContext messageContext;
SoapMessage requestSoapMessage = (SoapMessage)messageContext.getRequest();
SoapMessage responseSoapMessage = (SoapMessage)messageContext.getResponse();
//Getting request
ByteArrayOutputStream baos_req = new ByteArrayOutputStream();
requestSoapMessage.WriteTo(baos_req);
String soapReqMsg = baos_req.toString();
//Getting response
ByteArrayOutputStream baos_resp = new ByteArrayOutputStream();
responseSoapMessage.WriteTo(baos_resp);
String soapRespMsg = baos_resp.toString();
Please any one guide me,
Is there any way of getting Soap request and response in String without using OutputStream.
Thanks in advance!!!!
There is no other way than using a ByteArrayOutputStream. In fact, that is the same technique I use in Spring-WS myself. Though I would recommend using a UTF-8 character encoding:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
message.writeTo(bos);
return bos.toString("UTF-8");
I am not sure what you mean with "memory leakage and performance issues", I am not aware that the BAOS has any.

RestSharp Serializer Setter has no effect

I am struggling to make RestSharp talk with MVC API with EF4 and I'm very close to making it work. In order to overcome the interface limitation of the stock serializer I am using the Json.NET serializer. For the deserialization it worked out of the box but I can't make RestSharp use my custom serializer. The method Serializeis not getting called. Everything compiled great but doesn't work. Here is the code:
var client = CreateClient();
// client.Authenticator = new HttpBasicAuthenticator(username, password);
var request = new RestRequest("api/{type}s", Method.GET);
request.AddUrlSegment("type", typeof(T).Name);
request.RequestFormat = DataFormat.Json;
request.AddParameter("criteria", criteria);
IRestResponse<List<T>> response = client.Execute<List<T>>(request);
HandleResponse(response);
return response.Data;
Any ideas?
Edit
I tried to follow an example and change the post method to accept JObject and it worked!. But trying to call jitem.ToObject<Item>() caused the following exception:
Error converting value "System.Collections.Generic.List`1[DataAbstractionLayer.Poco.ItemCheckpoint]" to type 'System.Collections.Generic.ICollection`1[DataAbstractionLayer.Poco.ItemCheckpoint]'.
But more interesting is the inner exception:
Could not cast or convert from System.String to System.Collections.Generic.ICollection`1[DataAbstractionLayer.Poco.ItemCheckpoint].
Could it be that Json.NET converts collection items automatically to string?

ASP.NET MVC 3 provide large file download to browser while server retrieves file from elsewhere

I need to provide a file-download feature where the web server retrieves the file from another source (via HTTP) and simultaneously streams it to the browser. I am guessing that using MVC's Controller.File ActionResult will not work, but I wrote a prototype like this anyway:
public ActionResult Download()
{
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create("http://somewhere/somefile.pdf");
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
Stream stream = webResponse.GetResponseStream();
var mimeType = "application/pdf";
var fileName = "somefile.pdf";
return File(stream, mimeType, fileName);
}
This works fine, but there is no way to call Close() on the HttpWebResponse and Stream after the return statement. The help on the HttpWebResponse.GetResponseStream method says, "You must call either the Stream.Close or the HttpWebResponse.Close method to close the stream and release the connection for reuse. It is not necessary to call both Stream.Close and HttpWebResponse.Close, but doing so does not cause an error. Failure to close the stream will cause your application to run out of connections."
Should I create an HttpHandler and manually read bytes from the source stream and write them out to the response, along the lines of this or this? Is there another approach I'm not aware of?
While I'm not directly familiar with trying something like this, my first though was to do what you suggested in regards to reading in the stream, closing the connection, then returning the bytes as the response. Being a stream, I don't know how you can get around leaving it open for the sake of returning its contents as you do in your prototype, but then being able to close it when you're done.

Render view to string followed by redirect results in exception

So here's the issue: I'm building e-mails to be sent by my application by rendering full view pages to strings and sending them. This works without any problem so long as I'm not redirecting to another URL on the site afterwards. Whenever I try, I get "System.Web.HttpException: Cannot redirect after HTTP headers have been sent."
I believe the problem comes from the fact I'm reusing the context from the controller action where the call for creating the e-mail comes from. More specifically, the HttpResponse from the context. Unfortunately, I can't create a new HttpResponse that makes use of HttpWriter because the constructor of that class is unreachable, and using any other class derived from TextWriter causes response.Flush() to throw an exception, itself.
Does anyone have a solution for this?
public static string RenderViewToString(
ControllerContext controllerContext,
string viewPath,
string masterPath,
ViewDataDictionary viewData,
TempDataDictionary tempData)
{
Stream filter = null;
ViewPage viewPage = new ViewPage();
//Right, create our view
viewPage.ViewContext = new ViewContext(controllerContext,
new WebFormView(viewPath, masterPath), viewData, tempData);
//Get the response context, flush it and get the response filter.
var response = viewPage.ViewContext.HttpContext.Response;
//var response = new HttpResponseWrapper(new HttpResponse
// (**TextWriter Goes Here**));
response.Flush();
var oldFilter = response.Filter;
try
{
//Put a new filter into the response
filter = new MemoryStream();
response.Filter = filter;
//Now render the view into the memorystream and flush the response
viewPage.ViewContext.View.Render(viewPage.ViewContext,
viewPage.ViewContext.HttpContext.Response.Output);
response.Flush();
//Now read the rendered view.
filter.Position = 0;
var reader = new StreamReader(filter, response.ContentEncoding);
return reader.ReadToEnd();
}
finally
{
//Clean up.
if (filter != null)
filter.Dispose();
//Now replace the response filter
response.Filter = oldFilter;
}
}
You'd have to initiate a new request.
Bit, do you really want to send emails synchronously this way? If the mail server is down, the user could be waiting a good while.
I always put emails in an offline queue and have a service mail them. You might consider using the Spark template engine for this.
One other approach is to not redirect but write out a page with a meta redirect tag
Here is an alternative method for rendering a view to a string that never results in data being output to the response (therefore it should avoid your problem): http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/
To render a regular view instead of a partial view, you'll need to change "ViewEngines.Engines.FindPartialView" to "ViewEngines.Engines.FindView".
Have a look at the MVC Contrib EmailTemplateService which does exactly what you are after.
http://mvccontrib.googlecode.com/svn/trunk/src/MVCContrib/Services/EmailTemplateService.cs
Sorry Chris, not quite sure what I was thinking but I obviously didn't read the question. While I cannot give you a way around this, I can tell you why you are getting the error - HttpResponse.Flush() sends the headers before flushing the content to your filter. This sets a flag inside the response so that when you try to redirect you get the exception.
Using reflector to look at the code inside Flush, I can't see any clean way for you to get around this without a lot of reflection and other nastiness.

Resources