Asp.Net MVC convert View to Word Document

by Mark Wiseman on March 13th, 2009 | Posted in Revium Sandbox | Read the comments

One request we always seem to come across as developers is “Can we have a version of the page avaliable as a Word Document?”. With traditional ASP.Net this is easy. Simply place something similar to the following code block in your Page_Load.

Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/ms-word";
StringWriter stringWriter = new StringWriter();
HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter);
this.RenderControl(htmlTextWriter);
Response.Write(stringWriter.ToString());
Response.End();

And that was it. Now using Asp.Net MVC it isn’t that simple. We somehow need to return this data in the form of an ActionResult. I did this by creating: 2 Controllers 1 View and 1 new ActionResult Class.

First the 2 Views: DetailedReport and DetailedReportWord.

/// <summary>
/// This view is used to display the web version of the report as a web page in the users browser
/// This Controller has a View named DetailedReport
/// <summary>
public ActionResult DetailedReport()
{
    //[...]
    return View();
}

/// <summary>
/// This view is used convert the DetailedReport view into a Word Document and send it to the user
/// This Controller does not have an associated View
/// <summary>
public ActionResult DetailedReportWord()
{
    RouteValueDictionary rvd = new RouteValueDictionary();

    string url = Url.Action("DetailedReport", "Reports", rvd, "http");
    return new WordActionResult(url, "detailed-report.doc");
}

Now for the new ActionResult Class: WordActionResult. This class will take a URL and execute a HttpRequest. The resulting content is then streamed to the users as a Word Document

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;

namespace Revium.Utilities.MVC
{
    public class WordActionResult : ActionResult
    {
        private string _url;
        public string url
        {
            get { return _url; }
        }

        private string _filename;
        public string fileName
        {
            get { return _filename; }
        }

        public WordActionResult(string pUrl, string pFileName)
        {
            _url = pUrl;
            _filename = pFileName;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            HttpContext curContext = HttpContext.Current;
            curContext.Response.Clear();
            curContext.Response.AddHeader("content-disposition", "attachment;filename=" + _filename);
            curContext.Response.Charset = "";
            curContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            curContext.Response.ContentType = "application/ms-word";

            HttpWebRequest wreq = (HttpWebRequest)HttpWebRequest.Create(_url);

            // If we are using forms authentication we need to get the .ASPXAUTH cookie and add it to our request
            var httpCookie = context.HttpContext.Request.Cookies[".ASPXAUTH"];
            wreq.CookieContainer = new CookieContainer();
            wreq.CookieContainer.Add(new Cookie(httpCookie.Name, httpCookie.Value, httpCookie.Path, context.HttpContext.Request.Url.Host));

            HttpWebResponse wres = (HttpWebResponse)wreq.GetResponse();

            using (Stream s = wres.GetResponseStream())
            {
                using (StreamReader sr = new StreamReader(s, Encoding.ASCII))
                {
                    curContext.Response.Write(sr.ReadToEnd());
                    curContext.Response.End();
                }
            }
        }
    }
}

Now you may notice that ExecuteResult is a little different to the example i gave right at the start. This is because we are not converting the current view to a Word document. We are using the DetailedResultWord Controller to convert DetaileResult to a Word document.

This is just a very simple implementation but it has certainly given us the stepping stones required to convert MVC Views to Word and Excel documents.

Related posts:

  1. Creating a generic settings repository in C#
  2. FluentSecurity + MvcSiteMapProvider = Better .Net Security Management
  3. CSV parsing – the easy way!
  4. Limiting LINQ String Field Lengths
  5. ASP.NET MVC3 – Application_Error not firing log4net

Tags: Excel, MVC, Word

« jQuery image captions

CSV parsing – the easy way! »

20 Responses to “Asp.Net MVC convert View to Word Document”

  1. Evgeny Petrov says:
    March 18, 2009 at 10:34 am

    Very nice idea. Thanks for sharing. This approach might be used for other types of dynamically generated files e.g. pdf files.

  2. Haydee Velaquez says:
    July 30, 2010 at 12:14 pm

    good articles, Microsoft does not recommend using Office application in server-side scenarios, I recommand use Spire.Doc without ole automation.
    http://www.e-iceblue.com/Introduce/word-for-net-introduce.html

  3. Mark Wiseman says:
    August 19, 2010 at 4:56 pm

    Thanks for the tip Haydee. This isn’t using Office server side though. It is just telling the browser to use Word to open the HTML document

  4. Michael Franklin says:
    September 4, 2010 at 4:31 am

    Cannot get this to work.
    how id it that you are calling “WordActionResult.Word” when your class does not contain it? This would be extremely useful if I could get it to work Any help would be great. Thanks

  5. Mark Wiseman says:
    September 22, 2010 at 10:20 am

    @Michael You may be having an issue resolving the namespace. You will either need to add a Using statement to the top of your page

    Using Revium.Utilities.MVC;

    Or call it using the fully qualified name

    Revium.Utilities.MVC.WordActionResult.Word
  6. TWal says:
    May 13, 2011 at 6:08 am

    I am with Michael Franklin
    I don’t see how this will work, there is nothing in the WordActionResult call “Word”,
    Even when you include the namespace, there is nothing in that class called word.
    How can you reference something that is not there? intellisense doesn’t recognize word either???

  7. Mark Wiseman says:
    May 13, 2011 at 9:34 am

    @TWal Yeah i can see the problem now. I had posted my original controller with my newer WordActionResult. I have updated the code above. For quick reference we need to change:
    return WordActionResult.Word(url, “detailed-report.doc”);
    to
    return new WordActionResult(url, “detailed-report.doc”);
    Hope it helps

  8. Jim says:
    May 20, 2011 at 6:58 pm

    Nice technique.

    Any idea how to pass the authorised credentials over in the webrequest? the response I get back is my login page :-(

  9. Mark Wiseman says:
    May 23, 2011 at 9:36 am

    @Jim I’m not sure why this would happen for you. Make sure you use the same Authorize attribute settings on the DetailedReportWord action result or location elements in your web.config files

  10. Chitrang says:
    August 10, 2011 at 9:22 am

    Hi Mark,

    Thanks for the post, this is similar to what I am looking to do in my project. I was able to implement this as you have directed. Now what I am trying to achieve is I need to do this in an AJAX call. On my view there is a button, clicking on which I need to build the model and give it to the user to save as a Word Document. The flow works fine but I guess I will need to change the resulting view which I am not sure how to do. I am new to MVC, could you please point me to the right direction?

    Thanks,
    Chitrang

  11. Mark Wiseman says:
    August 10, 2011 at 10:31 am

    @Chitrang I’ve never had to do this via AJAX before and at a glance I’m not sure if it is possible. This method relies on changing the Response Header and Content type. Your best option might be: use an AJAX request to update some data at the server and then open a new browser window calling DetailedReportWord with some sort of identifier

  12. Chitrang says:
    August 11, 2011 at 10:28 am

    Hi Mark,

    Thanks for the prompt response. I have scrapped the idea of using AJAX call for this, I have created the same model in my application as you have suggested.

    Now the problem that I have is when the WebRequest is initiated it gets thrown to the Login page and what I get in the response is my Login page HTML. This is due to the rule we have setup for our application that without logging in, no one can access any page with just the URL. Is there a way to get around this issue?

    There is something called FileStreamResult or FileContentResult, can one of these be used instead?

    Thanks,
    Chitrang

  13. Mark Wiseman says:
    August 11, 2011 at 11:17 am

    @Chitrang. I think you are going to have to allow access to at least 1 URL for this to work. Good luck

  14. Chitrang says:
    August 12, 2011 at 7:35 am

    Thanks Mark. I am looking for other solutions and if I cant get them to work we may end up having one exception for this in our application rules.

  15. Rampersahd says:
    November 15, 2011 at 8:51 pm

    Hi Mark,

    Nice neat article. I want to know how can i get the images from my HTML to pull through onto the word doc?

  16. Mark Wiseman says:
    November 28, 2011 at 9:19 am

    @Rampersahd The only way to get images to be included using this technique is to make sure the src is the fully qualified URL. i.e. src=”http://yourdomain.com/Content/img.jpg” not src=”../../Content/img.jpg”
    The downside to this is that each time someone opens the document they will actually re-dowload the image from your site. If this is not acceptable you might need to look at server side options like @Haydee Velaquez suggested

  17. Kimi says:
    June 15, 2012 at 2:20 pm

    Hi Mark,

    I’m trying to use your approach to return a view as ms-word. The problem is, the view is required parameters to populate the view with correct data.

    Is there any idea to do it? Really2 need help/idea to accomplish this. I’ve tried to put the url with parameter, buts it seem not populating it.

    And also, when i try to run this at local, it can return as a ms-word if the url is not localhost. If localhost, it will return remote server (500) error.

    Thanks in advance.

  18. Mark Wiseman says:
    June 25, 2012 at 10:53 am

    @Kimi.
    This was just a basic implementation. You can change your URL and add parameters to your views fairly easily. To help put you on the right track I have updated the example to use a more dynamic url
    string url = Url.Action(“DetailedReport”, “Reports”, rvd, “http”);
    If you add the same route values to both action results you can pass them from the DetailedReportWord action by updating the RouteValueDictionary before calling DetailedReport

  19. Pryscila says:
    May 22, 2013 at 5:34 am

    Hi Mark!

    I have one problem with code.

    This part: ‘ string url = Url.Action(“DetailedReport”, “Reports”, rvd, “http”); ‘

    Displays an error message ‘Action is not member of the String’. How can I do?

    Thank you.

  20. Mark Wiseman says:
    May 22, 2013 at 8:24 am

    @Pryscila: Are you using a stnadard MVC project? Does Url.Action auto complete via IntelliSense when you type? Url is a UrlHelper and it is a property of Controller and should work.

Leave a Reply

Click here to cancel reply.

Recent Articles

  • ISAF Sailing World Cup
  • Revium Supports the Prostate Cancer Foundation of Australia
  • Kentico FAQ Module
  • Advanced Visitor Tracking in Analytics
  • Kentico, Smart Search and filtering attachments
  • Enhancing JIRA’s Issue Navigator
  • Mobile Browsing
  • Revium help win gold for Australia

Twitter

  • All things Gold! http://t.co/9DkkjmAr 2012-09-13
  • Mat Belcher - our favourite London Gold Medalist dropped into the office to say thank you. http://t.co/TxHbe2y6 2012-09-13
  • You beauty - http://t.co/1kbcBZwg #london2012 @belcherpage2012 2012-08-10
  • More updates...

Revium Logo

  • Home
  • About
  • Expertise
  • Showcase
  • Contact

  • news
  • blog
  • sandbox
  • twitter
  • rss
  • visit our facebook page

We are Revium, hear us roar!

The news.

26 Feb

Revium Supports the Prostate Cancer Foundation of Australia

We here at Revium are proud to say that we are supporting the Prostate Cancer Foundation of Australia, this month we have been able…

Continue reading
View archive

The blog.

11 Apr

ISAF Sailing World Cup

Mat Belcher and his team have been successful in taking out round 3 of the ISAF Sailing World Cup in Palma de Mallorca a…

Continue reading
View archive

The sandbox.

20 Dec

Kentico, Smart Search and filtering attachments

We had a scenario recently where we had 2 indexes set up in Kentico to search different folders within our site. Everything worked as…

Continue reading
View archive

eNewsletter.

 

© Copyright 2013. All Rights Reserved.

Revium Pty Ltd

info@revium.com.au Work +61 3 9429 2000

10 Harvey Street
Richmond, Victoria, 3121 Australia
View map

Disclaimer and privacy Revium Pty Ltd

Find us: web development, seo

 
Partner logos