I know there are a lot of questions similar, but I didn't get the answer I wanted in any of them. I want to convert, as it is, an ActionResult to String, by doing something like that:
public ActionResult ProductDetail(int id)
{
// ... lots of operations that include usage of ViewBag and a Model ...
return View(product);
}
To show the product page to the client, but also I want to be able to send it by JSON, like:
public JsonResult ProductDetailJSON(int id)
{
// ... lots of operations that include usage of ViewBag and a Model ...
return Json(new {
html = this.ProductDetail(id).ToString(),
name = ""
// ... more properties
}, JsonRequestBehavior.AllowGet);
}
So I will invoke the Controller Action and get its response (a ActionResult
, that is actually a ViewResult
) and turn it into a String
, but having it created using all the business code of the controller method (including, specially, ViewBag
usage).
EDIT Clarification about why I want to to do it that way
I need to be able to present the product in the browser, so I create my Controller Action called ProductDetail
, and works perfectly fine.
After that comes the need to have an API that gives, for a given product a JSON object with some meta-data (like sold units, name, etc.) and the View. One option was to send a link to the view and that's all... but the view MUST be inlined as a client requirement.
To avoid code repetition and to keep it clean, I'd like to invoke the method ProductDetail
, and capture the HTML Raw response to put it inside the JSON response as an String
.
I've seen others rendering the View with methods like this one:
public string RenderRazorViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
But that way does not execute the controller logic.
Here is a working solution.
Create new classes:
public class ResponseCapture : IDisposable
{
private readonly HttpResponseBase response;
private readonly TextWriter originalWriter;
private StringWriter localWriter;
public ResponseCapture(HttpResponseBase response)
{
this.response = response;
originalWriter = response.Output;
localWriter = new StringWriter();
response.Output = localWriter;
}
public override string ToString()
{
localWriter.Flush();
return localWriter.ToString();
}
public void Dispose()
{
if (localWriter != null)
{
localWriter.Dispose();
localWriter = null;
response.Output = originalWriter;
}
}
}
public static class ActionResultExtensions
{
public static string Capture(this ActionResult result, ControllerContext controllerContext)
{
using (var it = new ResponseCapture(controllerContext.RequestContext.HttpContext.Response))
{
result.ExecuteResult(controllerContext);
return it.ToString();
}
}
}
in your json method do this:
public JsonResult ProductDetailJSON(int id)
{
// ... lots of operations that include usage of ViewBag and a Model ...
return Json(new {
// ControllerContext - modify it so it accepts Id
html = View("ProductDetail").Capture(ControllerContext),
name = ""
// ... more properties
}, JsonRequestBehavior.AllowGet);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With