nulltron
nulltron

Reputation: 646

ASP.NET Core MVC requests return 404

I created a quick project to prove a point and now well I guess I suck. For some reason I'm pulling 404 errors on an endpoint in this very simple site setup. I feel like I'm missing something minor but I can't seem to put my finger on it.

Any assistance would be great, I really just can't seem to wrap my head around how the routes are being created and / or how the route that I'm attempting to use is invalid?

I've only ever used the .NET framework, and .NET Core seems to be handling things slightly different.

Originally, I had just made it a form targeting the asp-controller / asp-action with method=POST. However that didn't work and I received a 404 page.

Then I tried using jQuery Ajax request, same result.

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
        });
    }
}

Home controller:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
    }

    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Privacy()
    {
        return View();
    }

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        var model = new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier};

        if (Response.StatusCode.Equals(HttpStatusCode.NotFound))
            return View("~/Views/Shared/404.cshtml", model);

        return View(model);
    }

    [HttpPost]
    public async Task<IActionResult> DownloadAsync([FromBody] DownloadModel model)
    {
        try
        {
            var youtube = new YoutubeClient();
            var manifest = await youtube.Videos.Streams.GetManifestAsync(model.VideoId);

            var info = manifest.GetMuxed().WithHighestVideoQuality();

            if (info != null)
            {
                var file = $"{model.VideoId}.{info.Container}";
                await youtube.Videos.Streams.DownloadAsync(info, file);
                var bytes = await System.IO.File.ReadAllBytesAsync(file);
                return File(bytes, "application/force-download", file);
            }
        }
        catch (Exception e)
        {
            // TODO: Document Exception
            this._logger.LogError(e, $"Download exception occurred.");
        }

        return BadRequest();
    }
}

Index.cshtml

@model DownloadModel
@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Yipper (Youtube Ripper)</h1>
    <input type="text" asp-for="Url" class="url-input" id="url-textbox"/>
    <button id="url-submit-btn" class="url-btn" type="submit">RIP</button>
</div>

@section Scripts
{
    <script>
        function download(e) {
            e.preventDefault();
            const request = {
                "Url": $("#url-textbox").val() 
            }
            console.log(request);
            $.ajax('@Url.Action("DownloadAsync", "Home")',
                {
                    method: "POST",
                    data: request
                }).done(function(result) {
                    console.log(result);
                }).fail(function(error) {
                    console.log(error);
                });
        }

        $(function() {
            $("#url-submit-btn").click(download);
        });
    </script>
}

Upvotes: 0

Views: 2178

Answers (1)

Yiyi You
Yiyi You

Reputation: 18179

ASP.NET Core will trim the suffix Async from action names by default after .Net Core3.0,you can refer to the link.So you can change your ajax url to @Url.Action("Download", "Home"),and then you post object to action,so you need to remove [FromBody].Here is a demo worked:

Controller:

[HttpPost]
        public async Task<IActionResult> DownloadAsync(DownloadModel model)
        {
            return Ok();
        }

Index.cshtml:

<div class="text-center">
    <h1 class="display-4">Yipper (Youtube Ripper)</h1>
    <input type="text" asp-for="Url" class="url-input" id="url-textbox" />
    <button id="url-submit-btn" class="url-btn" type="submit">RIP</button>
</div>

@section Scripts
{
    <script>
        function download(e) {
            e.preventDefault();
            const request = {
                "Url": $("#url-textbox").val()
            }
            console.log(request);
            $.ajax('@Url.Action("Download", "Home")',
                {
                    method: "POST",
                    data: request
                }).done(function(result) {
                    console.log(result);
                }).fail(function(error) {
                    console.log(error);
                });
        }

        $(function() {
            $("#url-submit-btn").click(download);
        });
    </script>
}

result: enter image description here

Upvotes: 3

Related Questions