8
public class DefaultController : Controller
{
    // GET: Default
    public ActionResult Index()
    {
        return Download();
    }

    public FileResult Download()
    {
        string xmlString = "my test xml data";
        string fileName = "test" + ".xml";
        return File(Encoding.UTF8.GetBytes(xmlString), "application/xml", fileName);
    }
}

I have the above code in asp.net mvc application to download a file. It worked fine as my controller is inherited to Controller. But when I move this code to Webapi controller it throws error at return File. After analysis I found that my controller in webapi is inheriting to ApiController(system.web.http.api controller). I found that there is no File class in ApiController. Is there any option to implement downloading file functionality in webapi controller?

I tried the below alternative code in webapi controller but couldnt see a downloading file once I call this.

public HttpResponseMessage DownloadConstructedXmlFile()
        {
            var result = new HttpResponseMessage(HttpStatusCode.OK);
            string xmlContent = "My test xml data";
            //var serializer = new XmlSerializer(typeof(xmlContent));

            var builder = new StringBuilder();
            using (var writer = new StringWriter(builder))
            {
                // serializer.Serialize(writer, xmlContent);
                result.Content = new StringContent(xmlContent, Encoding.UTF8, "application/xml");
                result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                    FileName = string.Format("test.xml")
                };
               // return result;
            }

            return new HttpResponseMessage();
        }

PS: I am trying to use angularjs code to call this api through angular service.This is invoked on download button click. Any sample angular code or help in api controller code or suggestions would be helpful.

Kurkula
  • 6,673
  • 23
  • 99
  • 170

3 Answers3

11

Here is a much simpler example:

   using System.Net;
   using System.Net.Http;
   using System.Net.Http.Headers;
   using System.Text;
   using System.Web.Http;

   namespace WebApplication1.Controllers
   {
    public class ValuesController : ApiController
    {
        // GET: api/Values
        public HttpResponseMessage Get()
        {
            var xmlString = "<xml><name>Some XML</name></xml>";
            var result = Request.CreateResponse(HttpStatusCode.OK);
            result.Content = new StringContent(xmlString, Encoding.UTF8, "application/xml");
            result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = "test.xml"
            };

            return result;
        }
    }
   }
Jonathan
  • 1,524
  • 3
  • 13
  • 40
  • Actually I am not having text.xml. I am getting data from db and generating xml file on fly. I am trying to download that xml file. – Kurkula Aug 10 '15 at 02:22
  • Thanks for the edit, I have the complete xml as a string and I am trying to download that xml file. I dont have a xml file. – Kurkula Aug 10 '15 at 02:28
  • Sorry I was trying to swap to another computer to edit this, they need a draft feature – Jonathan Aug 10 '15 at 02:29
  • 1
    I made another update, you should be able to just get the xml in a string variable and return it now. – Jonathan Aug 10 '15 at 02:45
  • CreateResponse is throwing null reference. Is it new httpresponsemessage? I am using WebApi 2. An exception of type 'System.ArgumentNullException' occurred in System.Net.Http.Formatting.dll but was not handled in user code Additional information: Value cannot be null. – Kurkula Aug 10 '15 at 02:49
  • 1
    Are you using Web API 1 or 2? I tested this from a New project and it worked correctly with Web API 2 – Jonathan Aug 10 '15 at 02:50
  • I am trying to run this code from TestController. I added the below code in testController to unblock the error. "controller.Request = new HttpRequestMessage();". But My test executes successfully but file is not downloaded. What may be the issue? – Kurkula Aug 10 '15 at 02:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86569/discussion-between-jonathan-and-chandana). – Jonathan Aug 10 '15 at 03:02
3

It think something like this would do the trick!

public HttpResponseMessage Get(string fileName)
{
    FileStream fileStream = FileProvider.Open(fileName);
    var response = new HttpResponseMessage();
    response.Content = new StreamContent(fileStream);
    response.Content.Headers.ContentDisposition 
                      = new ContentDispositionHeaderValue("attachment");
    response.Content.Headers.ContentDisposition.FileName = fileName;
    response.Content.Headers.ContentType
                     = new MediaTypeHeaderValue("application/octet-stream");
    response.Content.Headers.ContentLength 
                     = FileProvider.GetLength(fileName);
    return response;
}
Joel Almeida
  • 7,399
  • 3
  • 22
  • 50
Márcio Duarte
  • 434
  • 3
  • 5
  • 13
2

Considering you're are returning XML you could have the controller return an XDocument.

public XDocument returnXMLFile()
{
   var xDoc = XDocument.Load("test.xml");
   return xDoc;
}

You will then either need to modify GlobalConfiguration.Configuration.Formatters to return XML, or configure your client to set the accept header to application/xml

One way to configure the server to return XML over another type like JSON, is the following code:

var xml = new System.Net.Http.Formatting.XmlMediaTypeFormatter();
xml.MediaTypeMappings.Add(new QueryStringMapping("format", "xml", "application/xml"));
GlobalConfiguration.Configuration.Formatters.Add(xml);

You could then download the file as xml via a hyperlink

<a href="[pathToController]/returnxmlfile?format=xml" >Download XML</a>

To customise have a look at these questions about downloading a file with Angular js.

Community
  • 1
  • 1
mikek3332002
  • 3,460
  • 4
  • 34
  • 44
  • I am trying to get data from db, build xml and then download the xml as file to local system on clicking a button through angular js. – Kurkula Aug 09 '15 at 23:50
  • 1
    @Chandana The return type of the controller method can be anything, The framework will build the xml for you. – mikek3332002 Aug 10 '15 at 02:16
  • I really appreciate your help and time mike. Jonahan answer worked with my requirement, but your answer is also useful for many who get struck with similar situation as me. – Kurkula Aug 10 '15 at 03:22