6

I have the following ActionLink:

@Html.ActionLink("Upload", "Upload", "File")

yet when I hover over the link, the url displayed in the browser is http://localhost:44334/Upload. Where is the controller in this url? Strangely, clicking the url takes me to my File/Upload view, and yet stranger, the url displayed in the browser's address bar is https://localhost:44334/Upload.

The ActionLink is on page Home/Explorer, so AFAIK a controller name is normally necessary.

Why is the ActionLink helper leaving out the controller name, and why is the link still working? I have tried refreshing the page with cache clearing, with no difference. I don't know what else do besides leave it alone because it works, but I'm concerned this is some quirk and it will no longer work when I deploy my site.

My routing is still standard, out-of-box, in the Startup class's Configure method:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Strangely enough, I have now added a Download link each row of an HTML table of files, looking like this:

@Html.ActionLink("Download", "Download", "File", new { filePath = file.Path })

and this link also renders without the controller name, e.g:

https://localhost/Download?filePath=....
ProfK
  • 49,207
  • 121
  • 399
  • 775
  • Have you defined a specific route definition for it? –  Apr 06 '18 at 07:30
  • do you have an entry in routeconfig that has "upload" in url parameter, and "file" and "upload" in controller and action respectively in other parameter? – Nirman Apr 06 '18 at 07:32
  • Without route definition in `RouteConfig` it's difficult to tell if the route definition or route order is wrong. It is possible that a route defined without controller name in URL but has default values to controller and action name parameter. – Tetsuya Yamamoto Apr 06 '18 at 07:41
  • 2
    You obviously have a route defined with `url: "Upload"` with `defaults: new { controller = "File" ...}` (or the equivalent `RouteAttribute`) –  Apr 06 '18 at 07:59
  • I have changed no routing at all. It is still exactly as output my the MVC Application project template. I have now showed the routing in my question. – ProfK Apr 07 '18 at 02:41
  • @StephenMuecke I don't **obviously** have any `Upload` route defined, as explained in the edited question. – ProfK Apr 07 '18 at 02:44
  • Can you include the declaration of your action used for upload and any attributes defined on it? – Kirk Larkin Apr 11 '18 at 10:57

3 Answers3

5

Http{Verb}("{routeTemplae}") is usually used when building a REST API. In order to be able to generate URLs for the default MVC controllers when using attribute routing you will need to use Route attribute.

The original controller probably did not have a route prefix which meant that

//POST /Upload //<-- when there is no prefix on the controller
[HttpPost("Upload")]

on the controller would route to /Upload with no controller prefix.

By including a prefix for the route on the controller it will include the controller name when generating URLs when using attribute routing.

[Route("[controller]"]
public class FileController : Controller {
    //POST File/Upload
    [HttpPost]
    [Route("Upload")]
    public async Task<ActionResult> Upload(UploadFilesViewModel model, IEnumerable<IFormFile> files) {
        //...
    }

    //GET File/Download
    [HttpGet]
    [Route("Download")]
    public async Task<IActionResult> Download(string filePath) {
        //...
    }
}

Reference Routing in ASP.NET Core

Reference Routing to Controller Actions

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • You are dead right, thanks, my friend. I don't need the `Route` attribute, but you are correct, as I said in my own answer below, that `[HttpPost("Upload")]` routes straight to `/Upload`. You are a winner. – ProfK Apr 11 '18 at 11:36
  • 1
    Just wanted to show that it could still work with attribute routing seeing as you mentioned that you were using the default convention-based routing. – Nkosi Apr 11 '18 at 11:44
3

This is most strange, but in my FileController, I had leftover attribute routing from when I was using the the controller for a Web API instead of in the MVC web application. The methods essentially looked like this:

[HttpPost("Upload")]
public async Task<ActionResult> Upload(UploadFilesViewModel model, IEnumerable<IFormFile> files)

[HttpGet("Download")]
public async Task<IActionResult> Download(string filePath)

Now that I have removed the route names from the attributes, and have just plain [HttpPost] and [HttpGet], the actionlinks work correctly and render URLs that now do contain the controller name.

ProfK
  • 49,207
  • 121
  • 399
  • 775
0

[HttpPost("Upload")]

Here Using POST method so no need of prefix like '/' on MVC controller

[HttpGet("Download")] public async Task Download(string filePath)

Venki WAR
  • 1,997
  • 4
  • 25
  • 38