2

I have this method in my controller.

public IActionResult Download()
{
  return Json(_context.Users);
}

I noticed that it produces the correct JSON structure but it's being rendered in the browser as common text. I want it to be downloaded to the client's computer. How do I do that?

I'm not sure if is should make my object to stream somehow like this or maybe create a file on my hard drive and serve it like this.

I can't find anything that strikes me as straight-forward and simple like we're used to in C#. So I fear that I'm missing a concept here.

DonkeyBanana
  • 3,266
  • 4
  • 26
  • 65
  • You can't do much server side - it's browser's decision how to show given mime type... You can always try to reply with some binary type "application/…" to convince browser not treat response as text... But why do you care? Normally you'd call service with JavaScript and not via link anyway... – Alexei Levenkov Feb 05 '19 at 22:14
  • @AlexeiLevenkov The application is rendered on server-side and the users' are extremely non-technical. I need to mimic the behavior they recognize from other downloads. I'm at full liberty to horse around with the format - it just happens to be in JSON now because I'm serializing some data. But they won't open the file, really - I can shoot it in any way I want. How would you suggest to force the binary type in the controller, please? – DonkeyBanana Feb 05 '19 at 22:18
  • @AlexeiLevenkov normally you wouldn't call a service with JS in MVC as this provides a security hole and is also bad programming practice usin a seperate language/framework to do something that is inbuilt. n the one already in use. You can use a fileresult Action (below) to tell the browser that this is file rather than just text. The browser in use will handle it from there. – Slipoch Feb 05 '19 at 23:09
  • @Slipoch care to clarify "you wouldn't call a service with JS in MVC as this provides a security hole and is also bad programming practice using a separate language/framework to do something that is inbuilt"? - what "security hole" you see there, why it is bad practice to use JavaScript on web page and what exactly is "inbuilt" to access web services from web page and why it is particular problem if server side code uses ASP.Net MVC (vs. NodeJS or Java for example)? – Alexei Levenkov Feb 06 '19 at 00:40
  • @AlexeiLevenkov, Std JS operates on the client side, also for the server-side flavours such as Angular, they tend to have numerous exploits that can be hit from client-side (although it is harder than with vanilla JS.). Introducing a new framework for one feature that you have already got support for is normally deemed bad programming practice as you are introducing complexity and extra code where none is needed. The OP specifically stated this is from the C# controller on server-side, which may not be using JS. C# MVC will do what the OP wanted (download file from hitting a controller). – Slipoch Feb 06 '19 at 05:56

2 Answers2

7

You can just write json object to a stream or array and use one of File method overloads. Add convenient Serialize method

private byte[] Serialize(object value, JsonSerializerSettings jsonSerializerSettings)
{
    var result = JsonConvert.SerializeObject(value, jsonSerializerSettings);

    return Encoding.UTF8.GetBytes(result);
}

And use it as following

public IActionResult Download()
{
    var download = Serialize(_context.Users, new JsonSerializerSettings());

    return File(download , "application/json", "file.json");
}

If you set special json serializer settings in Startup using .AddJsonOptions() you would like to use them as ASP.NET framework uses them in Json method. Inject MvcJsonOptions in controller

IOptions<MvcJsonOptions> _options;

public YourController(IOptions<MvcJsonOptions> options)
{
    _options = options;
}

And pass settings to method

public IActionResult Download()
{
    var download = Serialize(_context.Users, _options.Value.SerializerSettings);

    return File(download , "application/json", "file.json");
}
Alexander
  • 9,104
  • 1
  • 17
  • 41
2

Convert the data into bytes then those bytes into a FileResult. You return the FileResult and the browser will do whatever it does normally when presented with a 'file', usually either prompt the user or download.

Example below:

public ActionResult TESTSAVE()
    {
        var data = "YourDataHere";
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
        var output = new FileContentResult(bytes, "application/octet-stream");
        output.FileDownloadName = "download.txt";

        return output;
    }

In your case you would simply take your JSON data as a string.

Slipoch
  • 750
  • 10
  • 23