1

I created a method in my Controller that returns a doc file from a html string.

public class MyController : Controller {

    private FileContentResult Html2DocxResult(string html, string fileDownloadName) {
        byte[] docBytes = DocumentExport.Html2Docx(html);

        FileContentResult result;
        using (var ms = new MemoryStream(docBytes)) {
            result = this.File(ms.ToArray(), "application/msword", fileDownloadName + ".docx");
        }
        return result;
    }
}

When I need to send a file to a client, I can use like that:

public class MyController : Controller {

    [HttpPost]
    public FileContentResult GenerateDocument(string fileName) {
        string html = "<h1>Title</h1><p>Content goes here!</p>";
        return Html2DocxResult(html, fileName);
    }

}

The problem is that I'd like to call Html2DocxResult in any Controller I want to. I think make the method public isn't a good practice because, in this way, I would be creating an accessible Action that I don't want to exist. For exemple, an user could call http://mywebsite.com/MyController/Html2DocxResult?hml=<p>test</p>&fileDownloadName=MyTest and I don't want this.

I tried to create a class to have Html2DocxResult like an static method to serve all Controllers. Like that:

namespace BancoQuestoesMVC.Utils {
    public class DocumentExport {
        public static FileContentResult Html2DocxResult(string html, string fileDownloadName) {
            byte[] docBytes = DocumentExport.Html2Docx(html);

            FileContentResult result;
            using (var ms = new MemoryStream(docBytes)) {
                result = Controller.File(ms.ToArray(), "application/msword", fileDownloadName + ".docx");
            }
            return result;
        }
    }
}

But the method Controller.File can't be accessible in this context. I could pass the current Controller like a parameter to Html2DocxResult and call callerController.File and the problem would be solved. But it doesn't seem a good practice for me.

Someone know how to solve this kind of problem? Is there an design pattern appropriate for that? How a non-controller class can have methods that return ActionResult but without call for a Controller. Is that idea a problem of concept?

osmanpontes
  • 546
  • 1
  • 3
  • 13
  • 1
    How about returning a **new** FileContentResult from your method? `Controller.File` is just a convenience method for doing this. – spender Feb 09 '15 at 18:48
  • I don't know if I'm being perfectionist, but I don't like the idea of copying the 5 lines of code from `Html2DocResult` every time I need to send a doc file from an html string. Thanks in advance! – osmanpontes Feb 09 '15 at 18:55
  • 1
    If you are using it on all your controllers, maybe a base class? Have your controller inherit from a base class which in turn inherits from the Controller class. – puddinman13 Feb 09 '15 at 18:56
  • 1
    Like @spencer suggested change the line to `result = new FileContentResult(ms.ToArray(), "application/msword"){ FileDownloadName = fileDownLoadName + ".docx" }; ` – ziddarth Feb 09 '15 at 19:00
  • I don't need that in my all Controllers, but it's a really good advice. But it doesn't breaks your point. I could inherit from MyFileController only when I know MyController needs to use `Html2DocxResult`. This solves the problem for me for a while. But I'd like to wait a little bit more to know If someone knows different approachs. Thank you. – osmanpontes Feb 09 '15 at 19:06
  • I didn't understand @spender at the first time. Thank you spender and ziddarth. It solved my problem! – osmanpontes Feb 09 '15 at 19:15

2 Answers2

1

Try a base class. All of the controllers that need the FileContentResult method can inherit from this base class.

Base Class:

public class MyBaseController : Controller {

    protected FileContentResult Html2DocxResult(string html, string fileDownloadName) {
        byte[] docBytes = DocumentExport.Html2Docx(html);

        FileContentResult result;
        using (var ms = new MemoryStream(docBytes)) {
            result = this.File(ms.ToArray(), "application/msword", fileDownloadName + ".docx");
        }
        return result;
    }
}

Your Controller:

public class MyController : MyBaseController {

    [HttpPost]
    public FileContentResult GenerateDocument(string fileName) {
        string html = "<h1>Title</h1><p>Content goes here!</p>";
        return Html2DocxResult(html, fileName);
    }

}

Also keep in mind the NonAction attribute as well for methods that you want to prevent from being accessible from http: Disable action method from being called from the address bar

Community
  • 1
  • 1
puddinman13
  • 1,408
  • 4
  • 18
  • 35
1

Another solution would be to write an abstract base class inherited from Controller that makes Html2DocxResult accessible as a protected method. All your controllers which want to use this would have to inherit from this class instead.

public abstract class Html2DocxController : Controller // please find a better name.
{
    protected FileContentResult Html2DocxResult(string html, string fileDownloadName) 
    {
        byte[] docBytes = DocumentExport.Html2Docx(html);

        FileContentResult result;
        using (var ms = new MemoryStream(docBytes)) 
        {
            result = this.File(ms.ToArray(), "application/msword", fileDownloadName + ".docx");
        }
        return result;
    }
}
thegentlecat
  • 478
  • 3
  • 13