.NET Microsoft.AspNetCore.Http.Extensions UriHelper.GetDisplayUrl返回无效的URI

时间:2017-12-01 18:47:49

标签: c# .net http .net-core

我是.NET框架的新手,我遇到了Microsoft.AspNetCore.Http.HttpRequest界面的问题。我正在尝试使用GetDisplayUri扩展名,但它返回的URI无效。稍后我将URI传递给System.Uri.CreateThis(),并抛出以下异常:

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

GetDisplayUri方法应该根据HttpRequest内的字段创建一个URL,但是我无法弄清楚URL的哪些部分在哪个字段中,我不能在网上找到这个例子。我特别想知道如何将网址分解为PathPathBaseQueryString变量。

例如,假设我想构建网址"http://example.com/route/endpoint?foo=bar"。我很确定我的QueryString只是"?foo=bar",但我不知道如何将其余的URL分解为其他字段。另请告诉我,除了我提到的与GetDisplayUri相关的三个字段之外还有其他字段。

如果有什么不清楚,请告诉我。

2 个答案:

答案 0 :(得分:2)

我可能无法帮助您确定异常的来源,但我可以举例说明如何根据请求和查询字符串构建/重建URL。

我有一个显示记录列表的屏幕。由于其中有很多,我需要支持过滤和分页。过滤器作为查询字符串放置,即?foo1=bar1&foo2=bar2。分页也会在网址上添加其他页面大小和当前页码,即size=15&page=1

我没有使用GetDisplayUri(),而是使用UrlHelperExtensions获取当前的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);
        }
    }
}

为了进行分页,我需要跟踪当前页面大小,总项目数,当前页面,总页数,起始页面和结束页面。我为它创建了一个类,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;
        }
    }
}

最后,我可以使用url扩展和部分视图上的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>
}

这样,分页上的链接会反映到当前网址,其中包含查询字符串,以及页面大小和当前页面。

答案 1 :(得分:1)

好的,经过大约一个小时的反复试验,我发现哪些字段映射到了网址的哪个部分。一般表单为url = [Scheme]://[Host][PathBase][Path][QueryString],每个表单都有关于如何连接它们的特定规则(例如,Path和PathBase必须以/开头)。

因此,在问题("http://example.com/route/endpoint?foo=bar")的示例中,您的HttpRequest对象需要以下字段。

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")
}

由于隐藏了GetDisplayUri的实施,因此有点令人讨厌。希望如果其他人碰到这个,我已经为你节省了一个小时的猜测和检查。

编辑:正如大卫在他的回答评论中指出的那样,.NET Core是开源的,我本可以找到答案here