4

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.

4
  • why you want like that? can you explain why it is needed, it in unclear Commented Oct 1, 2014 at 10:25
  • added a clarification, hope it helps Commented Oct 1, 2014 at 10:39
  • As I understand you want to get sometimes a common html page, and sometimes a json object, that will contain also the html code as one of its properties? Commented Oct 1, 2014 at 11:08
  • Absolutely @ViktorArsanov Commented Oct 1, 2014 at 11:13

1 Answer 1

8

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);
}
Sign up to request clarification or add additional context in comments.

2 Comments

The origin link has changed to this link.
I read this but get stuck on getting the ControllerContext. For debug purposes I would like to convert an ActionResult to the HTML that is displayed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.