и путь

ема:

Мне нужно сделать страницу Razor частичной для строки.

Почему я хочу это:

Я хочу создать действие контроллера, которое отвечает JSON, содержащим частичное представление и другие необязательные параметры.

Попытки:

Я знаком со следующим примером, который отображает представление в строку:https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs

Тем не менее, он не совместим со Pages, так как он ищет только в каталоге Views, поэтому даже если я указываю ему абсолютный путь к частичному, он пытается найти мой _Layout.cshtml (чего он даже не должен делать!) И завершается ошибкой. найти его.

Я попытался изменить его, чтобы он отображал страницы, но в итоге я получаю исключение NullReferenceException для ViewData при попытке его визуализации. Я подозреваю, что это имеет отношение к NullView, но я понятия не имею, что вместо этого поместить (конструктор для RazorView требует много объектов, которые я не знаю, как получить правильно).

Код:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0
// Modified by OronDF343: Uses pages instead of views.

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Routing;

namespace TestAspNetCore.Services
{
    public class RazorPageToStringRenderer
    {
        private readonly IRazorViewEngine _viewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;

        public RazorPageToStringRenderer(
            IRazorViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _viewEngine = viewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }

        public async Task<string> RenderPageToStringAsync<TModel>(string viewName, TModel model)
        {
            var actionContext = GetActionContext();
            var page = FindPage(actionContext, viewName);

            using (var output = new StringWriter())
            {
                var viewContext = new ViewContext(actionContext,
                                                  new NullView(),
                                                  new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(),
                                                                                 new ModelStateDictionary())
                                                  {
                                                      Model = model
                                                  },
                                                  new TempDataDictionary(actionContext.HttpContext,
                                                                         _tempDataProvider),
                                                  output,
                                                  new HtmlHelperOptions());

                page.ViewContext = viewContext;
                await page.ExecuteAsync();

                return output.ToString();
            }
        }

        private IRazorPage FindPage(ActionContext actionContext, string pageName)
        {
            var getPageResult = _viewEngine.GetPage(executingFilePath: null, pagePath: pageName);
            if (getPageResult.Page != null)
            {
                return getPageResult.Page;
            }

            var findPageResult = _viewEngine.FindPage(actionContext, pageName);
            if (findPageResult.Page != null)
            {
                return findPageResult.Page;
            }

            var searchedLocations = getPageResult.SearchedLocations.Concat(findPageResult.SearchedLocations);
            var errorMessage = string.Join(
                Environment.NewLine,
                new[] { $"Unable to find page '{pageName}'. The following locations were searched:" }.Concat(searchedLocations));

            throw new InvalidOperationException(errorMessage);
        }

        private ActionContext GetActionContext()
        {
            var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
            return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
        }
    }
}

Ответы на вопрос(3)

Как всегда зарегистрируй Сервис в Startup.cs

services.AddScoped<IViewRenderService, ViewRenderService>();

Услуга определяется следующим образом:

public interface IViewRenderService
{
    Task<string> RenderToStringAsync<T>(string viewName, T model) where T : PageModel;
}

public class ViewRenderService : IViewRenderService
{
    private readonly IRazorViewEngine _razorViewEngine;
    private readonly ITempDataProvider _tempDataProvider;
    private readonly IServiceProvider _serviceProvider;
    private readonly IHttpContextAccessor _httpContext;
    private readonly IActionContextAccessor _actionContext;
    private readonly IRazorPageActivator _activator;


    public ViewRenderService(IRazorViewEngine razorViewEngine,
        ITempDataProvider tempDataProvider,
        IServiceProvider serviceProvider,
        IHttpContextAccessor httpContext,
        IRazorPageActivator activator,
        IActionContextAccessor actionContext)
    {
        _razorViewEngine = razorViewEngine;
        _tempDataProvider = tempDataProvider;
        _serviceProvider = serviceProvider;

        _httpContext = httpContext;
        _actionContext = actionContext;
        _activator = activator;

    }


    public async Task<string> RenderToStringAsync<T>(string pageName, T model) where T : PageModel
    {


        var actionContext =
            new ActionContext(
                _httpContext.HttpContext,
                _httpContext.HttpContext.GetRouteData(),
                _actionContext.ActionContext.ActionDescriptor
            );

        using (var sw = new StringWriter())
        {
            var result = _razorViewEngine.FindPage(actionContext, pageName);

            if (result.Page == null)
            {
                throw new ArgumentNullException($"The page {pageName} cannot be found.");
            }

            var view = new RazorView(_razorViewEngine,
                _activator,
                new List<IRazorPage>(),
                result.Page,
                HtmlEncoder.Default,
                new DiagnosticListener("ViewRenderService"));


            var viewContext = new ViewContext(
                actionContext,
                view,
                new ViewDataDictionary<T>(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                {
                    Model = model
                },
                new TempDataDictionary(
                    _httpContext.HttpContext,
                    _tempDataProvider
                ),
                sw,
                new HtmlHelperOptions()
            );


            var page = ((Page)result.Page);

            page.PageContext = new Microsoft.AspNetCore.Mvc.RazorPages.PageContext
            {
                ViewData = viewContext.ViewData

            };

            page.ViewContext = viewContext;


            _activator.Activate(page, viewContext);

            await page.ExecuteAsync();


            return sw.ToString();
        }
    }



}

Я называю это так

  emailView.Body = await this._viewRenderService.RenderToStringAsync("Email/ConfirmAccount", new Email.ConfirmAccountModel
                {
                    EmailView = emailView,
                });

«Email / ConfirmAccount» - это путь к моей странице Razor (Под страницами). «ConfirmAccountModel» - это модель моей страницы для этой страницы.

ViewData имеет значение null, потому что ViewData для страницы устанавливается, когда задан PageContext, поэтому, если он не установлен, ViewData имеет значение NULL.

Я также обнаружил, что мне нужно было позвонить

_activator.Activate(page, viewContext);

Чтобы все это работало. Это еще не полностью протестировано, поэтому может не работать для всех сценариев, но должно помочь вам начать работу.

 Sven15 июн. 2018 г., 17:09
Хорошая работа, спасибо. Рендеринг HTML содержит только частичное представление. Кажется, что _ViewStart отсутствует, поэтому в результате нет макета. Я изо всех сил пытаюсь заставить _ViewStart работать. (См. Свойство PageContext.ViewStartFactories)

Я посмотрел исходный код RazorViewEngine и обнаружил, что поиск страницы осуществляется с использованием данных маршрута "page":

var routeData = new RouteData();
routeData.Values.Add("page", "/Folder/MyPage");

Это работает для меня с полным путем "/ Folder / MyPage" в routeData и именем страницы "MyPage" в вызове GetPage.

Если, как я, вы не получитеGetRouteData() из_httpContext.HttpContext а также_actionContext имеет значение null, вы можете создать расширение:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace Utils
{
    public static class PageExtensions
    {
        public static async Task<string> RenderViewAsync(this PageModel pageModel, string pageName)
        {
            var actionContext = new ActionContext(
                pageModel.HttpContext,
                pageModel.RouteData,
                pageModel.PageContext.ActionDescriptor
            );

            using (var sw = new StringWriter())
            {
                IRazorViewEngine _razorViewEngine = pageModel.HttpContext.RequestServices.GetService(typeof(IRazorViewEngine)) as IRazorViewEngine;
                IRazorPageActivator _activator = pageModel.HttpContext.RequestServices.GetService(typeof(IRazorPageActivator)) as IRazorPageActivator;

                var result = _razorViewEngine.FindPage(actionContext, pageName);

                if (result.Page == null)
                {
                    throw new ArgumentNullException($"The page {pageName} cannot be found.");
                }

                var page = result.Page;

                var view = new RazorView(_razorViewEngine,
                    _activator,
                    new List<IRazorPage>(),
                    page,
                    HtmlEncoder.Default,
                    new DiagnosticListener("ViewRenderService"));


                var viewContext = new ViewContext(
                    actionContext,
                    view,
                    pageModel.ViewData,
                    pageModel.TempData,
                    sw,
                    new HtmlHelperOptions()
                );


                var pageNormal = ((Page)result.Page);

                pageNormal.PageContext = pageModel.PageContext;

                pageNormal.ViewContext = viewContext;


                _activator.Activate(pageNormal, viewContext);

                await page.ExecuteAsync();

                return sw.ToString();
            }
        }
    }
}

Примечание: этот код только отображает вызываемую страницу и пропускает макет.

Вы просто должны позвонить с вашегоPageModel нравится:

var s = this.RenderViewAsync("sendEmail").Result;

"sendEmail" это имя твоегоPageModel view и путь/Pages/sendEmail.cshtml

Ваш ответ на вопрос