dotnet / dotnet/aspnetcore

Make link creation typesafe using expressions

Open
#14,665 2 comments 12 reactions 0 assignees View on GitHub
affected-medium area-mvc enhancement feature-routing severity-major
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

Currently there is no refactor-safe way to create links to actions or pages, it has to be done using magic strings that the compiler doesn't understand. For example, I have an `AboutController` with an `Index` action, then I can create a link using `Url.Action("Index", "About")`. But if I rename the action or controller then this will lead to a runtime exception, not a compile time exception. I could use `Url.Action(nameof(AboutController.Index), "About")`, but using `nameof()` only works for the action, not the controller, since it expects me to specify the name of the controller without the Controller suffix. This is further a problem for actions that take query parameters, where they are specified using an anonymous class with no type safety at all. That is, there is no verification that `Url.Action("Index", "Search", new {name, title, query})` will create a link that sends the correct query parameters to the `SearchController.Index` method.

My proposal is therefore a method that takes an expression, so that it can look like this:

```cs
//Given this controller...
public class AboutController
{
public IActionResult Index()
{
//...
}

public IActionResult Search(string name, string title, string query)
{
//...
}

[HttpPost]
public IActionResult Submit(string id, [FromBody] Model input)
{
//...
}
}

//Get url to action in another controller
Url.Action(c => c.Index()); // => /About/Index

//Inside a controller there would be an overload that is not generic, using the current controller as the basis
Url.Action(c => c.Index()); // => /About/Index

//Actions that have query parameters would also work
Url.Action(c => c.Search(name, title, query)); // => /About/Search?name=test&title=blabla&query=*

//For actions that work with Post or Put and take in a body, the body is ignored so we can pass in null values for the Post parameters
Url.Action(c => c.Submit(id, null)); // =>/About/Submit/1234
```

The `UrlHelper.Action` method would be defined as `public static string Action(this IUrlHelper helper, Expression> expression)` or something similar, and based on the expression passed into it enough information should be able to be gathered to create a url.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.