Skip to content

Using routing data to generate links? #953

Closed
@alastairtree

Description

@alastairtree

DESCRIPTION

Following on from the useful discussion in PR #949 I have implemented a custom ILinkBuilder that uses the MVC routing data to help generate links rather than just string building from static config.

Use cases where this is required:

  • Your app uses a global path base such as in startup.Configure app.UsePathBase(new PathString("/api"))
    If the path base is a fixed string then json api users must also configure the JsonApiOptions.Namespace option to prefix all links, but with the route aware link builder, this option is no longer required and all links will include path base by default
  • Your app is using some custom routing convention, such as the multi-tenant setup described in task: make [Attr] and RequestDeserializer.SetAttributes open to extension #949 like GET /api/{tenantId}/books. The current LinkBuilder cannot see/understand the path before the resourceName, so all links start /books rather than the complete link. I have also used other weird routing setups that mess up the links such as /{controller}/{routeParam1}/{routeParam2}.

To generate complete links that are "routing aware" you need to use MVC LinkGenerator to generate the url of the Get endpoint of the correct controller, in the current http context.

I will paste the code I used below. Any thoughts on the approach? Is there any interest in a PR to either change the current link builder, or add an option to use this version? And if not no worries, folks can easily steal my class below and apply it using the dependency injection framework with services.AddScoped<ILinkBuilder, RoutingLinkBuilder>();

RoutingLinkBuilder.cs gist

(The main change is adding this method, and calling it everywhere to generate a link to the /{resource}/{id?} endpoint)

private string GetResourceLink(ResourceContext resourceContext, string resource, string resourceId = null)
{
    // if current request has any route params specified we need to fetch them here to reuse them,
    // and append/set the id of the resource we are routing to
    var routeData =
        new RouteValueDictionary(_httpContextAccessor.HttpContext.Request.RouteValues) { ["id"] = resourceId };

    var controllerName = routeData["controller"]?.ToString() ?? resource;

    var link = _linkGenerator.GetPathByAction(
        _httpContextAccessor.HttpContext, action: GetResourceByIdActionName
        , controllerName, routeData);

    return link;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions