Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Microsoft.AspNetCore.Http.Extensions UriHelper.GetDisplayUrl returns invalid URI

I'm new to the .NET framework and I'm having an issue with the Microsoft.AspNetCore.Http.HttpRequest interface. I'm trying to use the GetDisplayUri extension but it's returning an invalid URI. Later I pass the URI to System.Uri.CreateThis() and the following exception is getting thrown:

System.UriFormatException: Invalid URI: The format of the URI could not be determined.

The GetDisplayUri method is supposed to create a URL based on the fields inside the HttpRequest, but I can't figure out which parts of the URL go in which fields, and I can't find any examples of this online. I'm specifically wondering how I should be breaking down a URL into the Path, PathBase, and QueryString variables.

For example, say I wanted to build the URL "http://example.com/route/endpoint?foo=bar". I'm pretty sure my QueryString is just "?foo=bar" but I don't know how to break down the rest of the URL into the other fields. Also let me know if there are other fields besides the three that I mentioned that are relevant for GetDisplayUri.

Let me know if anything is unclear.

like image 550
Tom Avatar asked Jan 18 '26 05:01

Tom


2 Answers

Alright, after about an hour of trial and error, I've figured out which fields map to which part of the url. The general form is url = [Scheme]://[Host][PathBase][Path][QueryString] and they each have specific rules about how to connect them (for example, Path and PathBase must start with /).

So in the example from the question ("http://example.com/route/endpoint?foo=bar"), your HttpRequest object would need the following fields.

var request = class_implementing_HttpRequest() {
    Scheme = "http",
    Host = new HostString("example.com"),
    PathBase = new PathString("/route"),
    Path = new PathString("/endpoint"),
    QueryString = new QueryString("?foo=bar")
}

This was a bit obnoxious to figure out since the implementation of GetDisplayUri is hidden. Hopefully if someone else runs into this, I've saved you an hour of guessing and checking.

Edit: As David pointed out in the comments on his answer, .NET Core is open source and I could have figured out the answer looking here.

like image 160
Tom Avatar answered Jan 19 '26 18:01

Tom


I might not be able to help you figure where the exception came from but I can give you an example of how to build / reconstruct URLs from the request and query strings.

I have a screen to display list of records. Since there are many of them, I need to support filtering and pagination. Filters are placed as query string, i.e., ?foo1=bar1&foo2=bar2. The pagination places additional page size and current page number on the url as well, i.e., size=15&page=1.

Instead of using GetDisplayUri(), I have a UrlHelperExtensions that takes the current Url, examine the url query strings and add additional query strings (page size & current page) as needed to the url.

namespace DL.SO.Project.Framework.Mvc.Extensions
{
    public static class UrlHelperExtensions
    {
        public static string Current(this IUrlHelper url, object routeValues)
        {
            // Get current route data
            var currentRouteData = url.ActionContext.RouteData.Values;

            // Get current route query string and add them back to the new route
            // so that I can preserve them.
            // For example, if the user applies filters, the url should have
            // query strings '?foo1=bar1&foo2=bar2'. When you construct the
            // pagination links, you don't want to take away those query 
            // strings.

            var currentQuery = url.ActionContext.HttpContext.Request.Query;
            foreach (var param in currentQuery)
            {
                currentRouteData[param.Key] = param.Value;
            }

            // Convert new route values to a dictionary
            var newRouteData = new RouteValueDictionary(routeValues);

            // Merge new route data
            foreach (var item in newRouteData)
            {
                currentRouteData[item.Key] = item.Value;
            }

            return url.RouteUrl(currentRouteData);
        }
    }
}

In order to do pagination, I need to keep track of the current page size, count of total items, current page, total pages, start page and end page. I create a class for that, Pager.cs.

namespace DL.SO.Project.Framework.Mvc.Paginations
{
    public class Pager
    {   
        public int TotalItems { get; private set; }
        public int CurrentPage { get; private set; }
        public int CurrentPageSize { get; private set; }
        public int TotalPages { get; private set; }
        public int StartPage { get; private set; }
        public int EndPage { get; private set; }

        public Pager(int totalItems, int currentPage = 1, int currentPageSize = 15)
        {
            currentPageSize = currentPageSize < 15
                ? 15
                : currentPageSize;

            // Calculate total, start and end pages
            var totalPages = (int)Math.Ceiling(
                (decimal)totalItems / (decimal)currentPageSize
            );

            currentPage = currentPage < 1
                ? 1
                : currentPage;

            // Only display +- 2
            var startPage = currentPage - 2;
            var endPage = currentPage + 2;
            if (startPage <= 0)
            {
                endPage = endPage - startPage + 1;
                startPage = 1;
            }
            if (endPage > totalPages)
            {
                endPage = totalPages;
                if (endPage > 5)
                {
                    startPage = endPage - 4;
                }
            }

            this.TotalItems = totalItems;
            this.CurrentPage = currentPage;
            this.CurrentPageSize = currentPageSize;
            this.TotalPages = totalPages;
            this.StartPage = startPage;
            this.EndPage = endPage;
        }
    }
}

Finally I can use the url extension and the Pager class on the partial view to construct the pager.

@model DL.SO.Project.Framework.Mvc.Paginations.Pager
@{
    var showingRangeFrom = (Model.CurrentPage - 1) * Model.CurrentPageSize + 1;
    var showingRangeTo = Model.CurrentPage * Model.CurrentPageSize;

    if (showingRangeFrom > Model.TotalItems)
    {
        showingRangeFrom = Model.TotalItems;
    }
    if (showingRangeTo > Model.TotalItems)
    {
        showingRangeTo = Model.TotalItems;
    }
}

@if (Model != null && Model.TotalItems > 0)
{
    <div class="list-pager">
        <div class="list-pager-info">
            <span>Showing <strong>@showingRangeFrom-@showingRangeTo</strong>
                of <strong>@Model.TotalItems</strong> entries
            </span>
        </div>
        <div class="list-pagination">
            <ul class="pagination">
                <li class="page-item @(Model.CurrentPage == 1? "disabled" : "")">
                    <!-- use Url extension here -->
                    <a href="@Url.Current(new { page = Model.CurrentPage - 1 })" 
                        class="page-link" tabindex="-1">
                        &lt; Prev
                    </a>                        
                </li>
                @for (int i = Model.StartPage; i <= Model.EndPage; i++)
                {
                    <li class="page-item @(i == Model.CurrentPage? "active" : "")">
                        <!-- use Url extension here -->
                        <a href="@Url.Current(new { page = i })" 
                            class="page-link">@i</a>
                    </li>
                }
                <li class="page-item @(Model.CurrentPage >= Model.EndPage? "disabled" : "")">
                     <!-- use Url extension here -->
                     <a href="@Url.Current(new { page = Model.CurrentPage + 1 })" 
                         class="page-link" tabindex="-1">Next ></a>
                </li>
            </ul>
        </div>
    </div>
}

This way the links on the pagination would reflect to the current url with the query strings with it, as well as the page size and current page.

like image 22
David Liang Avatar answered Jan 19 '26 18:01

David Liang



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!