How to add "allow-downloads" to the sandbox attributes list - google-chrome

I am trying to get files from my MVC project (asp.net core 3.1)
I created a link
<a asp-action="#nameof(HomeController.Download)" asp-controller="#HomeController.Name" asp-route-fileName="fileName.doc" download>FileName</a>
I created a controller
public async Task<ActionResult> Download(string fileName) {
var path = Path.Combine(_hostingEnvironment.WebRootPath, fileName);
if (!System.IO.File.Exists(path)) {
return NotFound();
}
var fileBytes = await System.IO.File.ReadAllBytesAsync(path);
var response = new FileContentResult(fileBytes, "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
FileDownloadName = fileName
};
return response;
}
In Chrome i get the warning
Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag ‘allow-downloads’ is not set. See https://www.chromestatus.com/feature/5706745674465280 for more details.
Following the link, how can i add:
add "allow-downloads" to the sandbox attributes list to opt in
The file is downloaded if i click the button from Microsoft Edge

This is what I had to do for my project, hopefully it will point you to the right direction.
In your Startup.cs
services.AddMvc(options =>{
options.Filters.Add(new MyActionFilterAttribute());
}
Then in MyActionFilterAttribute.cs
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Headers.Add("Content-Security-Policy", "sandbox allow-downloads; " )
}
}

Related

Failed to fetch while error reading CSV file ASP.NET

I'm trying to read .CSV file and instead of the read lines, Swagger outputs
Failed to fetch.
Possible Reasons:
CORS
Network Failure
URL scheme must be "http" or "https" for CORS request.
In my opinion, even Visual Studio crashed, the application immediately stops working
//Controller
[ApiController]
[Route("[controller]")]
public class HomeController: ControllerBase
{
private readonly ICSVService _csvService;
public HomeController(ICSVService csvService)
{
_csvService = csvService;
}
[HttpPost("read-csv")]
public async Task<IActionResult> GetCSV([FromForm] IFormFileCollection file)
{
var dataFromFile = _csvService.ReadCSV<Values>(file[0].OpenReadStream());
return Ok(dataFromFile);
}
}
// Reader
public class CSVServise : ICSVService
{
public IEnumerable<T> ReadCSV<T>(Stream file)
{
var reader = new StreamReader(file);
var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords<T>();
return records;
}
}
// Program.cs
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms /aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<ICSVService, CSVServise>();
builder.Services.AddCors();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors(builder => builder.AllowAnyOrigin());
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
My launchSettings.json
I thought enabling CORS would help, but it doesn't seem to have worked. And now I have no idea - what could be the problem?

post_logout_redirect_uri ASP NET Core 2.2 AzureAD Razor Class Library RCL

We have tried using the sample
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/
Walked through the sample and all works.
We can't get it to redirect after logout process. Also, it seems the account controller is not there but it is called in _layout.chtml this must be something new.
Yes, it does redirect to the application - what I'd like it to do is redirect to a different page.
You can redirect user to another page after sign-out by setting the OnSignedOutCallbackRedirect event :
In Startup.cs add using System.Threading.Tasks;
Config your new redirect url in OnSignedOutCallbackRedirect event :
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
options.Events.OnSignedOutCallbackRedirect = (context) =>
{
context.Response.Redirect("/Home/About");
context.HandleResponse();
return Task.CompletedTask;
};
});
The account controller code is built into the framework now. You can see it in Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureAD.Controllers.Internal (see https://github.com/aspnet/AADIntegration/blob/0efa96de73e3235fbfc55cfe51d9547a693010cc/src/Microsoft.AspNetCore.Authentication.AzureAD.UI/Areas/AzureAD/Controllers/AccountController.cs):
namespace Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureAD.Controllers.Internal
{
[AllowAnonymous]
[Area("AzureAD")]
[NonController]
[Route("[area]/[controller]/[action]")]
internal class AccountController : Controller
{
public IOptionsMonitor<AzureADOptions> Options
{
get;
}
public AccountController(IOptionsMonitor<AzureADOptions> options)
{
this.Options = options;
}
[HttpGet("{scheme?}")]
public IActionResult SignIn([FromRoute] string scheme)
{
scheme = scheme ?? AzureADDefaults.AuthenticationScheme;
string str = base.Url.Content("~/");
return this.Challenge(new AuthenticationProperties()
{
RedirectUri = str
}, new String[] { scheme });
}
[HttpGet("{scheme?}")]
public IActionResult SignOut([FromRoute] string scheme)
{
scheme = scheme ?? AzureADDefaults.AuthenticationScheme;
AzureADOptions azureADOption = this.Options.Get(scheme);
string str = base.Url.Page("/Account/SignedOut", null, null, base.Request.Scheme);
return this.SignOut(new AuthenticationProperties()
{
RedirectUri = str
}, new String[] { azureADOption.CookieSchemeName, azureADOption.OpenIdConnectSchemeName });
}
}
}
Unfortunately, I have not be able to force a redirect after logout. Instead, I see a page that says "You have successfully signed out." I'd like to know how to redirect the user back to the Index page.
I had to override the signedOut page manually by adding this to a controller:
[AllowAnonymous]
[HttpGet]
[Route("/MicrosoftIdentity/Account/SignedOut")]
public IActionResult SignedOut()
{
return Redirect(<MyRealSignedOutRedirectUri>);
}

vNext: Console app that uses razor views without hosting

I am creating console application that does some file conversions. These conversions are easily done creating a model from the input file and then executing razor models for the output.
To have this working in the IDE I used Visual Studio 2015 preview and created a vnext console application that uses MVC. (You get razor support out of the box then). To get this all working you need to host the MVC app though, and the cheapest way to do that is hosting is through a WebListener. So I host the MVC app and then call it through "http://localhost:5003/etc/etc" to get the rendered views that construct the output.
But the console app is not supposed to listen to/use a port. It is just a command line tool for file conversions. If multiple instances would run at the same time they would fight to host the pages on the same port. (This could of coarse be prevented by choosing a port dynamically, but this is not what I am looking for)
So my question is how would you get this working without using a port, but using as much of the vnext frameworks as possible.
In short: how can I use cshtml files that I pass models in a console app that does not use a port using the vnext razor engine.
Here is some code I currently use:
Program.cs
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConsoleTest
{
public class Program
{
private readonly IServiceProvider _hostServiceProvider;
public Program(IServiceProvider hostServiceProvider)
{
_hostServiceProvider = hostServiceProvider;
}
public async Task<string> GetWebpageAsync()
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://localhost:5003/home/svg?idx=1");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
return await httpClient.GetStringAsync("");
}
}
public Task<int> Main(string[] args)
{
var config = new Configuration();
config.AddCommandLine(args);
var serviceCollection = new ServiceCollection();
serviceCollection.Add(HostingServices.GetDefaultServices(config));
serviceCollection.AddInstance<IHostingEnvironment>(new HostingEnvironment() { WebRoot = "wwwroot" });
var services = serviceCollection.BuildServiceProvider(_hostServiceProvider);
var context = new HostingContext()
{
Services = services,
Configuration = config,
ServerName = "Microsoft.AspNet.Server.WebListener",
ApplicationName = "ConsoleTest"
};
var engine = services.GetService<IHostingEngine>();
if (engine == null)
{
throw new Exception("TODO: IHostingEngine service not available exception");
}
using (engine.Start(context))
{
var tst = GetWebpageAsync();
tst.Wait();
File.WriteAllText(#"C:\\result.svg", tst.Result.TrimStart());
Console.WriteLine("Started the server..");
Console.WriteLine("Press any key to stop the server");
Console.ReadLine();
}
return Task.FromResult(0);
}
}
}
Startup.cs
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.ConfigurationModel;
namespace ConsoleTest
{
public class Startup
{
public IConfiguration Configuration { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
//Configure WebFx
app.UseMvc(routes =>
{
routes.MapRoute(
null,
"{controller}/{action}",
new { controller = "Home", action = "Index" });
});
}
}
}
I solved it using the following code:
Program.cs
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.TestHost;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.Runtime.Infrastructure;
namespace ConsoleTest
{
public class Program
{
private Action<IApplicationBuilder> _app;
private IServiceProvider _services;
public async Task<string> TestMe()
{
var server = TestServer.Create(_services, _app);
var client = server.CreateClient();
return await client.GetStringAsync("http://localhost/home/svg?idx=1");
}
public void Main(string[] args)
{
_services = CallContextServiceLocator.Locator.ServiceProvider;
_app = new Startup().Configure;
var x = TestMe();
x.Wait();
Console.WriteLine(x.Result);
Console.ReadLine();
}
}
}
Startup.cs
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using Microsoft.AspNet.Routing;
namespace ConsoleTest
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseServices(services =>
{
// Add MVC services to the services container
services.AddMvc();
});
//Configure WebFx
app.UseMvc(routes =>
{
routes.MapRoute(
null,
"{controller}/{action}",
new { controller = "Home", action = "Index" });
});
}
}
}

Windows Phone link from Tile error

I have a list of theaters and I created a secondary tile from my application to navigate directly to specific theater. I pass the id of the theater in query string :
I load the theaters from a WCF service in the file "MainViewModel.cs"
In my home page, I have a list of theaters and I can navigate to a details page.
But when I want to navigate from the tile, I have an error...
The Tile :
ShellTile.Create(new Uri("/TheaterDetails.xaml?selectedItem=" + theater.idTheater, UriKind.Relative), tile, false);
My TheaterDetails page :
public partial class TheaterDetails : PhoneApplicationPage
{
theater theater = new theater();
public TheaterDetails()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
if (DataContext == null)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
theater = (from t in App.ViewModel.Theaters
where t.idTheater == index
select t).SingleOrDefault();
DataContext = theater;
....
....
....
The error :
https://dl.dropboxusercontent.com/u/9197067/error.png
Like if the data were not loaded...
Do you have an idea where the problem come from ?
The solution could be easy but I am a beginner... Maybe it's because I load the data asynchronously and the application doesn't wait until it's done...
Thanks
EDIT :
My LoadData() method :
public void LoadData()
{
client.GetTheatersCompleted += new EventHandler<ServiceReference1.GetTheatersCompletedEventArgs>(client_GetTheatersCompleted);
client.GetTheatersAsync();
// Other get methods...
this.IsDataLoaded = true;
}
private void client_GetTheatersCompleted(object sender, ServiceReference1.GetTheatersCompletedEventArgs e)
{
Theaters = e.Result;
}
You should check to see which variable is actually null. In this case it looks to be Theaters (otherwise the error would have thrown earlier).
Since Theaters is populated from a web call it is most likely being called asynchronously, in other words when you return from LoadData() the data is not yet there (it's still waiting for the web call to come back), and is waiting for the web service to return its values.
Possible solutions:
Make LoadData() an async function and then use await LoadData(). This might require a bit of rewriting / refactoring to fit into the async pattern (general introduction to async here, and specific to web calls on Windows Phone here)
A neat way of doing this that doesn't involve hacks (like looping until the data is there) is to raise a custom event when the data is actually populated and then do your Tile navigation processing in that event. There's a basic example here.
So the solution that I found, thanks to Servy in this post : Using async/await with void method
I managed to use async/await to load the data.
I replaced my LoadData() method by :
public static Task<ObservableCollection<theater>> WhenGetTheaters(ServiceClient client)
{
var tcs = new TaskCompletionSource<ObservableCollection<theater>>();
EventHandler<ServiceReference1.GetTheatersCompletedEventArgs> handler = null;
handler = (obj, args) =>
{
tcs.SetResult(args.Result);
client.GetTheatersCompleted -= handler;
};
client.GetTheatersCompleted += handler;
client.GetTheatersAsync();
return tcs.Task;
}
public async Task LoadData()
{
var theatersTask = WhenGetTheaters(client);
Theaters = await theatersTask;
IsDataLoaded = true;
}
And in my page :
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
await App.ViewModel.LoadData();
}

How to serve html file from another directory as ActionResult

I have a specialised case where I wish to serve a straight html file from a Controller Action.
I want to serve it from a different folder other than the Views folder. The file is located in
Solution\Html\index.htm
And I want to serve it from a standard controller action. Could i use return File? And
how do I do this?
Check this out :
public ActionResult Index()
{
return new FilePathResult("~/Html/index.htm", "text/html");
}
If you want to render this index.htm file in the browser then you could create controller action like this:
public void GetHtml()
{
var encoding = new System.Text.UTF8Encoding();
var htm = System.IO.File.ReadAllText(Server.MapPath("/Solution/Html/") + "index.htm", encoding);
byte[] data = encoding.GetBytes(htm);
Response.OutputStream.Write(data, 0, data.Length);
Response.OutputStream.Flush();
}
or just by:
public ActionResult GetHtml()
{
return File(Server.MapPath("/Solution/Html/") + "index.htm", "text/html");
}
So lets say this action is in Home controller and some user hits http://yoursite.com/Home/GetHtml then index.htm will be rendered.
EDIT: 2 other methods
If you want to see raw html of index.htm in the browser:
public ActionResult GetHtml()
{
Response.AddHeader("Content-Disposition", new System.Net.Mime.ContentDisposition { Inline = true, FileName = "index.htm"}.ToString());
return File(Server.MapPath("/Solution/Html/") + "index.htm", "text/plain");
}
If you just want to download file:
public FilePathResult GetHtml()
{
return File(Server.MapPath("/Solution/Html/") + "index.htm", "text/html", "index.htm");
}
I extended wahid's answer to create HtmlResult
Create Html Result which extends FilePathResult
public class HtmlResult : FilePathResult
{
public HtmlResult(string path)
: base(path, "text/html")
{
}
}
Created static method on controller
public static HtmlResult Html(this Controller controller, string path)
{
return new HtmlResult(path);
}
used like we return view
public HtmlResult Index()
{
return this.Html("~/Index.html");
}
Hope it helps
I want put my two cents in. I have found this most terse and it is there already :
public ActionResult Index()
{
var encoding = new System.Text.UTF8Encoding();
var html = ""; //get it from file, from blob or whatever
return this.Content(html, "text/html; charset=utf-8");
}
Can you read the html file in a string and return it in action? It is rendered as Html page as shown below:
public string GetHtmlFile(string file)
{
file = Server.MapPath("~/" + file);
StreamReader streamReader = new StreamReader(file);
string text = streamReader.ReadToEnd();
streamReader.Close();
return text;
}
Home/GetHtmlFile?file=Solution\Html\index.htm
If the destination or storage mechanism of HTML files is complicated then you can you Virtual path provider
Virtual path provider MVC sample
Alternative approach if using .net core is to use a FileProvider.
The files could be in a folder or embedded at compile time.
In this example we will use embedded files.
Add a folder in your project let's say assets, in it create a file myfile.html, add some basic html to the file say
<html>
<head>
<title>Test</title>
</head>
<body>
Hello World
</body>
</html>
Right click on the new file (assuming you are in visual studio) select properties, in the properties screen / build action, select embedded resource. It will add the file to the csproj file.
Right click on your project, edit your csproj file.
Check that your property group contains the following:
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
If not please add it. The csproj should also contain the newly created html file as:
<ItemGroup>
<EmbeddedResource Include="assets\myfile.html" />
</ItemGroup>
To read the file in your controller and pass it to the client requires a file provider which is added to the startup.cs
Edit your startup.cs make sure it includes the HostingEnvironment:
private readonly IHostingEnvironment HostingEnvironment;
public Startup(IHostingEnvironment hostingEnvironment)
{
HostingEnvironment = hostingEnvironment;
}
Then create a file provider and make it a service that can be injected at runtime. Create it as follows:
var physicalProvider = HostingEnvironment.ContentRootFileProvider;
var manifestEmbeddedProvider =
new ManifestEmbeddedFileProvider(Assembly.GetEntryAssembly());
var compositeProvider =
new CompositeFileProvider(physicalProvider, manifestEmbeddedProvider);
services.AddSingleton<IFileProvider>(compositeProvider);
To serve the file go to your controller, use dependency injection to get the FileProvider, create a new service and serve the file. To do this, start with dependency injection by adding the provider to your constructor.
IFileProvider _fileProvider;
public MyController(IFileProvider fileProvider)
{
this._fileProvider = fileProvider;
}
Then use the file provider in your service
[HttpGet("/myfile")]
[Produces("text/html")]
public Stream GetMyFile()
{
// Use GetFileInfo to get details on the file passing in the path added to the csproj
// Using the fileInfo returned create a stream and return it.
IFileInfo fileinfo = _fileProvider.GetFileInfo("assets/myfile.html");
return fileinfo.CreateReadStream();
}
For more info see ASP .Net Core file provider sample and the Microsoft documentation here.