UrlActionResult in asp.net MVC - asp.net-mvc

I want to know is there any open source action result code available for asp.net mvc in which i can pass a URL and it will response out the content.
Just like there are some inbuilt Action results
FileResult
FileStreamResult
I want to build a custom Action Result in which i can pass a URL (basically an mp3 url) which will get downloaded in memory and the content will be streamed to the current executing response.
It should also support resume download if supported by server and client. Thats a must for me.
public UrlActionResult DownloadUrl(string url)
{
return new UrlActionResult("http://www.example.com/audiofile.mp3");
}

For a basic scenario you could use something like this:
public class DownloadResult : ActionResult
{
private readonly FileStreamResult _fileStreamResult;
public DownloadResult(string url, string contentType)
{
using (var myWebClient = new WebClient())
{
var myStream = myWebClient.OpenRead(url);
_fileStreamResult = new FileStreamResult(myStream, contentType);
}
}
public override void ExecuteResult(ControllerContext context)
{
_fileStreamResult.ExecuteResult(context);
}
}
However if you want to do the resume download it becomes a lot more complicated. Here is a great article with example code.

If you don't need to hide the url just use a redirect. Using a redirect with spare you server bandwidth:
public ActionResult DownloadUrl(string url)
{
return new Redirect("http://www.example.com/audiofile.mp3");
}

Related

Dynamic internal redirect in ASP.NET MVC

I've searched a lot but didn't find any solution. So here is my case:
I have database model UrlRedirect
public class UrlRedirect : AuditInfo
{
[Key]
public int Id { get; set; }
public string OldUrl { get; set; }
public string NewUrl { get; set; }
}
As you may assume I am trying to save URL mapping between OldUrl and NewUrl.
I want to internally change the OldUrl path of the request to the NewUrl and then run all defined routes as they will run if the user is opening NewUrl directly.
The redirect should be server side URL rewrite and user should see the old URL in their browser
In Global.asax you have some events that are executed in the web application context. (for sample: Start_Application, End_Application, etc).
In your case, you could use the BeginRequest event, where every request made to your web application is executed. In this event you could check the URL and try to redirect it using the default of htpp protocols, such as 301 Moved Permanently status code. For sample, in the Global.asax file, add the code bellow:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
// get the current url
string currentUrl = HttpContext.Current.Request.Url.ToString().ToLower();
// here, you could create a method to get the UrlRedirect object by the OldUrl.
var urlRedirect = GetUrlRedirect(currentUrl);
// check if the urlRedirect object was found in dataBase or any where you save it
if (urlRedirect != null)
{
// redirect to new URL.
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", urlRedirect.NewUrl);
Response.End();
}
}
In the sample, GetUrlRedirect(string) method should check it in a database, xml file, cache, or anywhere you save the UrlRedirect objects.
To understand more about how asp.net core applications life cycles works, read this article.
If you really want to redirect within the server you may use one of the following:
HttpServerUtility.TransferRequest
HttpServerUtility.Transfer
HttpServerUtility.Execute
HttpContext.RewritePath
You can read more about these options, where a similar problem is posed here.
Consider the impact of your choice on the request serving performance. IMHO you should try your best to utilize MVC infrastructure of Routing, or just fall back to simple redirections (even permanent for speed) as [user:316799] wrote when you compute new urls in business layer or map from db.
This is my final solution that works like a charm. Thanks to #WeTTTT for the idea.
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
// ...
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
this.ApplyCustomUrlRedirects(new UowData(), HttpContext.Current);
}
private void ApplyCustomUrlRedirects(IUowData data, HttpContext context)
{
var currentUrl = context.Request.Path;
var url = data.UrlRedirects.All().FirstOrDefault(x => x.OldUrl == currentUrl);
if (url != null)
{
context.RewritePath(url.NewUrl);
}
}
}

Writing to a HttpResponse with BufferOutput=false blocks when an ActionResult comes from a ASP.NET MVC 4 asynchronous action and Glimpse is enabled

I have a custom ActionResult that sets HttpResponse.BufferOutput = false and then writes data to the response stream. I noticed that when the action result comes from a task-based asynchronous action method in ASP.NET MVC, writing to the response stream blocks. This happens only when Glimpse plugin is enabled in web.config. Glimpse is very useful to me, I really want to have it enabled, at least during development and testing. BufferOutput property should remain false, because the content length can be quite large and I don't want to buffer it in memory.
This is the shortest code that could reproduce this exact behaviour:
public sealed class CustomResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
var resp = context.HttpContext.Response;
resp.BufferOutput = false;
resp.ContentType = "text/plain";
resp.Output.Write(DateTime.UtcNow.ToString());
resp.Flush();
}
}
public sealed class DownloadController : Controller
{
// the client nevers gets the response from this action
public async Task<ActionResult> Async()
{
await Task.Yield();
return new CustomResult();
}
// this works
public ActionResult Sync()
{
return new CustomResult();
}
}
I tested this with the latest Glimpse.Mvc4 package (version 1.3.2).
Am I doing something wrong, is there a workaround to this issue or is this a Glimpse bug and I should report it?

How to get a response "stream" from an action in MVC3/Razor?

I am using MVC3, .NET4, C#.
I need to create some XHTML using a Razor View. I do this via an Action.
public ActionResult RenderDoc(int ReportId)
{
//A new document is created.
return View();
}
I then need to take the output from this and convert it to a Word Doc. I am using a 3rd party component to do this and it expects a "stream" or a "file" for the XHTML source that is read in for conversion to a DOC, like the following:
document.Open(MyXhtmlStream,FormatType.Html,XHTMLValidationType.Transitional);
My Question:
What would be a good way to call the "RenderDoc" Action and obtain the result as a stream to feed into "MyXhtmlStream".
Many thanks.
EDIT: I have had another idea !!!
1) Render the View within the action to create a String(XHTMLString). I have seen a method to do this on SO.
2) Create a MemoryStream and put this string into it.
Stream MyStream = New MemoryStream("XHTMLString and encoding method");
EDIT2: Based on Darin's answer
I need to clasyify a little further, and I hope to do this via tweaking Darin's code for my purpose.
public class XmlDocumentResult : ActionResult
{
private readonly string strXhtmlDocument;
public XmlDocumentResult(string strXhtmlDocument)
{
this.strXhtmlDocument = strXhtmlDocument;
}
public override void ExecuteResult(ControllerContext context)
{
WordDocument myWordDocument = new WordDocument();
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
myWordDocument.Open(response.OutputStream, FormatType.Html, XHTMLValidationType.Transitional);
}
}
The above is closer to what I need. Note the 3rd Party WordDocument type. So there is still the issue of how I get the "strXhtmlDocument" into the "Response.OutputStream?
I would just write a custom ActionResult to handle that:
public class XmlDocumentResult : ActionResult
{
private readonly Document document;
public XmlDocumentResult(Document document)
{
this.document = document;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
document.Open(response.OutputStream, FormatType.Html, XHTMLValidationType.Transitional);
}
}
You could of course adjust the response Content-Type if necessary and also append a Content-Disposition header if you want.
And then simply have my controller action return this custom action result:
public ActionResult RenderDoc(int reportId)
{
Document document = repository.GetDocument(reportId);
return new XmlDocumentResult(document);
}
Now the controller action doesn't need to handle plumbing code anymore. The controller action does what a typical controller action is supposed to do:
Query the Model
Pass this model to an ActionResult
In your case the model is this Document class or whatever it is called.

Stream file using ASP.NET MVC FileContentResult in a browser with a name?

Is there a way to stream a file using ASP.NET MVC FileContentResult within the browser with a specific name?
I have noticed that you can either have a FileDialog (Open/Save) or you can stream the file in a browser window, but then it will use the ActionName when you try to save the file.
I have the following scenario:
byte[] contents = DocumentServiceInstance.CreateDocument(orderId, EPrintTypes.Quote);
result = File(contents, "application/pdf", String.Format("Quote{0}.pdf", orderId));
When I use this, I can stream the bytes, but a OPEN/SAVE file dialog is given to the user. I would like to actually stream this file in a browser window.
If I just use the FilePathResult, it shows the file in a browser window, but then when I click on "Save" button to save the file in PDF, it shows me the Action Name as the name of the file.
Has anyone encountered this?
public ActionResult Index()
{
byte[] contents = FetchPdfBytes();
return File(contents, "application/pdf", "test.pdf");
}
and for opening the PDF inside the browser you will need to set the Content-Disposition header:
public ActionResult Index()
{
byte[] contents = FetchPdfBytes();
Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
return File(contents, "application/pdf");
}
Actually, the absolutely easiest way is to do the following...
byte[] content = your_byte[];
FileContentResult result = new FileContentResult(content, "application/octet-stream")
{
FileDownloadName = "your_file_name"
};
return result;
This might be helpful for whoever else faces this problem. I finally figured out a solution. Turns out, even if we use the inline for "content-disposition" and specify a file name, the browsers still do not use the file name. Instead browsers try and interpret the file name based on the Path/URL.
You can read further on this URL:
Securly download file inside browser with correct filename
This gave me an idea, I just created my URL route that would convert the URL and end it with the name of the file I wanted to give the file. So for e.g. my original controller call just consisted of passing the Order Id of the Order being printed. I was expecting the file name to be of the format Order{0}.pdf where {0} is the Order Id. Similarly for quotes, I wanted Quote{0}.pdf.
In my controller, I just went ahead and added an additional parameter to accept the file name. I passed the filename as a parameter in the URL.Action method.
I then created a new route that would map that URL to the format:
http://localhost/ShoppingCart/PrintQuote/1054/Quote1054.pdf
routes.MapRoute("", "{controller}/{action}/{orderId}/{fileName}",
new { controller = "ShoppingCart", action = "PrintQuote" }
, new string[] { "x.x.x.Controllers" }
);
This pretty much solved my issue.
Previous answers are correct: adding the line...
Response.AddHeader("Content-Disposition", "inline; filename=[filename]");
...will causing multiple Content-Disposition headers to be sent down to the browser. This happens b/c FileContentResult internally applies the header if you supply it with a file name. An alternative, and pretty simple, solution is to simply create a subclass of FileContentResult and override its ExecuteResult() method. Here's an example that instantiates an instance of the System.Net.Mime.ContentDisposition class (the same object used in the internal FileContentResult implementation) and passes it into the new class:
public class FileContentResultWithContentDisposition : FileContentResult
{
private const string ContentDispositionHeaderName = "Content-Disposition";
public FileContentResultWithContentDisposition(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
: base(fileContents, contentType)
{
// check for null or invalid ctor arguments
ContentDisposition = contentDisposition;
}
public ContentDisposition ContentDisposition { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
// check for null or invalid method argument
ContentDisposition.FileName = ContentDisposition.FileName ?? FileDownloadName;
var response = context.HttpContext.Response;
response.ContentType = ContentType;
response.AddHeader(ContentDispositionHeaderName, ContentDisposition.ToString());
WriteFile(response);
}
}
In your Controller, or in a base Controller, you can write a simple helper to instantiate a FileContentResultWithContentDisposition and then call it from your action method, like so:
protected virtual FileContentResult File(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
{
var result = new FileContentResultWithContentDisposition(fileContents, contentType, contentDisposition);
return result;
}
public ActionResult Report()
{
// get a reference to your document or file
// in this example the report exposes properties for
// the byte[] data and content-type of the document
var report = ...
return File(report.Data, report.ContentType, new ContentDisposition {
Inline = true,
FileName = report.FileName
});
}
Now the file will be sent to the browser with the file name you choose and with a content-disposition header of "inline; filename=[filename]".
I hope that helps!
The absolute easiest way to stream a file into browser using ASP.NET MVC is this:
public ActionResult DownloadFile() {
return File(#"c:\path\to\somefile.pdf", "application/pdf", "Your Filename.pdf");
}
This is easier than the method suggested by #azarc3 since you don't even need to read the bytes.
Credit goes to: http://prideparrot.com/blog/archive/2012/8/uploading_and_returning_files#how_to_return_a_file_as_response
** Edit **
Apparently my 'answer' is the same as the OP's question. But I am not facing the problem he is having. Probably this was an issue with older version of ASP.NET MVC?
I adapted it in ASP.NET Core with REST API.
public class FileContentWithFileNameResult : FileContentResult
{
public FileContentWithFileNameResult(byte[] fileContents, string contentType, string fileName)
: base(fileContents, contentType)
{
FileName = fileName;
}
public string FileName { get; private set; }
public override Task ExecuteResultAsync(ActionContext context)
{
var response = context.HttpContext.Response;
response.Headers.Append("Content-Disposition", $"inline; filename={FileName}");
response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
response.Headers.Append("X-Content-Type-Options", "nosniff");
return base.ExecuteResultAsync(context);
}
}
public FileContentResult GetImage(int productId) {
Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId);
if (prod != null) {
return File(prod.ImageData, prod.ImageMimeType);
} else {
return null;
}
}

Accessing resources via Uri in Asp.net mvc

I am working on an ASP.NET MVC web application in which I have an object with a Uri property. The Uri contains a restful link to a resource in the following form:
/Repository/Dataset/5
The Dataset action of the Repository controller returns the contents of dataset 5 as Json.
How do I call this method from the Uri and interpret the response as Json from within the object?
Many thanks.
In server side action return JsonResult.
public ActionResult Dataset(int id)
{
// reository code
return Json(model);
}
client side call $.getJSON.
My opinion is that you should not call your controller from anywhere in code.In ASP.NET MVC Controller is there to accept request, take data and choose proper view to be returned back.
Maybe you should add method on repository that is returning already JSONized data, or introduce "Middle man" that can serialize data returned from repository so controller can call middle man to do the job. Then repository (or "Middle man") can be called from anywhere in code.
e.g.(used Json.NET for json serialization):
public class MiddleMan
{
IRepository repository
public MiddleMan(IRepository repository)
{
this.repository = repository;
}
public string GetJsonObjects(int id)
{
return JsonConvert.SerializeObject(repository.GetObject(id));
}
}
then controller (or anywhere in the code) can call this middle class:
public string Dataset(int id)
{
return new MiddleMan(repository).GetJsonObjects(id);
}
For the time being I'm going to implement a uri extension method something along these lines, creating a WebRequest object for the Uri.
public static string GetContent(this Uri uri)
{
var myRequest = (HttpWebRequest) WebRequest.Create(uri);
myRequest.Method = "GET";
WebResponse myResponse = myRequest.GetResponse();
var sr = new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8);
string result = sr.ReadToEnd();
sr.Close();
myResponse.Close();
return result;
}

Resources