From a3ce4392f65f862dec55e654528417117b6e496a Mon Sep 17 00:00:00 2001 From: DESKTOP-FEDOR Date: Thu, 30 Nov 2023 13:13:32 +0500 Subject: [PATCH 1/6] 1.1 --- BadNews/Controllers/ErrorsController.cs | 13 +++++++++++ BadNews/Startup.cs | 11 ++++++++++ BadNews/Views/Errors/StatusCode.cshtml | 29 +++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 BadNews/Controllers/ErrorsController.cs create mode 100644 BadNews/Views/Errors/StatusCode.cshtml diff --git a/BadNews/Controllers/ErrorsController.cs b/BadNews/Controllers/ErrorsController.cs new file mode 100644 index 0000000..1bbb5ac --- /dev/null +++ b/BadNews/Controllers/ErrorsController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; + +namespace BadNews.Controllers +{ + public class ErrorsController : Controller + { + public IActionResult StatusCode(int? code) + { + return View(code); + } + + } +} \ No newline at end of file diff --git a/BadNews/Startup.cs b/BadNews/Startup.cs index cd346c3..f83ed56 100644 --- a/BadNews/Startup.cs +++ b/BadNews/Startup.cs @@ -34,6 +34,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); + services.AddControllersWithViews(); } // В этом методе конфигурируется последовательность обработки HTTP-запроса @@ -42,6 +43,16 @@ public void Configure(IApplicationBuilder app) app.UseDeveloperExceptionPage(); app.UseHttpsRedirection(); app.UseStaticFiles(); + app.UseStatusCodePagesWithReExecute("/StatusCode/{0}"); + app.UseRouting(); + app.UseEndpoints(endpionts => + { + endpionts.MapControllerRoute("status-code", "StatusCode/{code?}", new + { + controller = "Errors", + action = "StatusCode" + }); + }); app.Map("/news", newsApp => { diff --git a/BadNews/Views/Errors/StatusCode.cshtml b/BadNews/Views/Errors/StatusCode.cshtml new file mode 100644 index 0000000..0279213 --- /dev/null +++ b/BadNews/Views/Errors/StatusCode.cshtml @@ -0,0 +1,29 @@ +@model int? + + + + + + + + Bad News + + +
@(Model?.ToString() ?? "Неизвестный код состояния")
+ + + \ No newline at end of file From 3d987dee14ed80c8034c254b7b6e0498f71eec75 Mon Sep 17 00:00:00 2001 From: DESKTOP-FEDOR Date: Thu, 30 Nov 2023 13:31:14 +0500 Subject: [PATCH 2/6] 1.2 --- BadNews/Controllers/ErrorsController.cs | 6 +++- BadNews/Program.cs | 3 +- BadNews/Startup.cs | 24 ++++++------- BadNews/Views/Errors/Exception.cshtml | 46 +++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 BadNews/Views/Errors/Exception.cshtml diff --git a/BadNews/Controllers/ErrorsController.cs b/BadNews/Controllers/ErrorsController.cs index 1bbb5ac..685d572 100644 --- a/BadNews/Controllers/ErrorsController.cs +++ b/BadNews/Controllers/ErrorsController.cs @@ -8,6 +8,10 @@ public IActionResult StatusCode(int? code) { return View(code); } - + + public IActionResult Exception() + { + return View(null, HttpContext.TraceIdentifier); + } } } \ No newline at end of file diff --git a/BadNews/Program.cs b/BadNews/Program.cs index c2036c8..44ffd42 100644 --- a/BadNews/Program.cs +++ b/BadNews/Program.cs @@ -19,6 +19,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); + webBuilder.UseEnvironment(Environments.Development); }); } @@ -36,4 +37,4 @@ private static void InitializeDataBase() repository.InitializeDataBase(articles); } } -} +} \ No newline at end of file diff --git a/BadNews/Startup.cs b/BadNews/Startup.cs index f83ed56..c84ed90 100644 --- a/BadNews/Startup.cs +++ b/BadNews/Startup.cs @@ -40,34 +40,34 @@ public void ConfigureServices(IServiceCollection services) // В этом методе конфигурируется последовательность обработки HTTP-запроса public void Configure(IApplicationBuilder app) { - app.UseDeveloperExceptionPage(); + if (env.IsDevelopment()) + app.UseDeveloperExceptionPage(); + else + app.UseExceptionHandler("/Errors/Exception"); + + app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseStatusCodePagesWithReExecute("/StatusCode/{0}"); app.UseRouting(); - app.UseEndpoints(endpionts => + app.UseEndpoints(endpoints => { - endpionts.MapControllerRoute("status-code", "StatusCode/{code?}", new + endpoints.MapControllerRoute("status-code", "StatusCode/{code?}", new { controller = "Errors", action = "StatusCode" }); + endpoints.MapControllerRoute("default", "{controller}/{action}"); }); app.Map("/news", newsApp => { - newsApp.Map("/fullarticle", fullArticleApp => - { - fullArticleApp.Run(RenderFullArticlePage); - }); + newsApp.Map("/fullarticle", fullArticleApp => { fullArticleApp.Run(RenderFullArticlePage); }); newsApp.Run(RenderIndexPage); }); - app.MapWhen(context => context.Request.Path == "/", rootPathApp => - { - rootPathApp.Run(RenderIndexPage); - }); + app.MapWhen(context => context.Request.Path == "/", rootPathApp => { rootPathApp.Run(RenderIndexPage); }); // Остальные запросы — 404 Not Found } @@ -148,4 +148,4 @@ private static string BuildFullArticlePageHtml(FullArticleModel model) return pageHtml; } } -} +} \ No newline at end of file diff --git a/BadNews/Views/Errors/Exception.cshtml b/BadNews/Views/Errors/Exception.cshtml new file mode 100644 index 0000000..3d3cbbe --- /dev/null +++ b/BadNews/Views/Errors/Exception.cshtml @@ -0,0 +1,46 @@ +@model string + + + + + + + Bad News + + + + + + +
+
+ +
+ + + + +
+ + + +
+

Во время обработки вашего запроса возникла ошибка

+ + @if (!string.IsNullOrEmpty(Model)) + { +

Request ID: @Model

+ } + +

Вернуться на главную страницу

+
+ + + + + + + + \ No newline at end of file From 5abd5a26df2244d549682735a1786e1c2578c52b Mon Sep 17 00:00:00 2001 From: DESKTOP-FEDOR Date: Thu, 30 Nov 2023 14:07:27 +0500 Subject: [PATCH 3/6] 1.3 --- BadNews/Controllers/ErrorsController.cs | 12 +++++++++- BadNews/Program.cs | 32 +++++++++++++++++++++++-- BadNews/Startup.cs | 2 ++ BadNews/appsettings.Development.json | 10 +------- BadNews/appsettings.json | 29 ++++++++++++++++++---- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/BadNews/Controllers/ErrorsController.cs b/BadNews/Controllers/ErrorsController.cs index 685d572..53489fe 100644 --- a/BadNews/Controllers/ErrorsController.cs +++ b/BadNews/Controllers/ErrorsController.cs @@ -1,11 +1,21 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; namespace BadNews.Controllers { public class ErrorsController : Controller { + private readonly ILogger logger; + + public ErrorsController(ILogger logger) + { + this.logger = logger; + } + public IActionResult StatusCode(int? code) { + logger.LogWarning("status-code {code} at {time}", code, DateTime.Now); return View(code); } diff --git a/BadNews/Program.cs b/BadNews/Program.cs index 44ffd42..c23d954 100644 --- a/BadNews/Program.cs +++ b/BadNews/Program.cs @@ -1,7 +1,9 @@ +using System; using System.Linq; using BadNews.Repositories.News; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; +using Serilog; namespace BadNews { @@ -10,7 +12,31 @@ public class Program public static void Main(string[] args) { InitializeDataBase(); - CreateHostBuilder(args).Build().Run(); + + Log.Logger = new LoggerConfiguration() + .WriteTo.File(".logs/start-host-log-.txt", + rollingInterval: RollingInterval.Day, + rollOnFileSizeLimit: true, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}") + .CreateLogger(); + + try + { + Log.Information("Creating web host builder"); + var hostBuilder = CreateHostBuilder(args); + Log.Information("Building web host"); + var host = hostBuilder.Build(); + Log.Information("Running web host"); + host.Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly"); + } + finally + { + Log.CloseAndFlush(); + } } public static IHostBuilder CreateHostBuilder(string[] args) @@ -20,7 +46,9 @@ public static IHostBuilder CreateHostBuilder(string[] args) { webBuilder.UseStartup(); webBuilder.UseEnvironment(Environments.Development); - }); + }) + .UseSerilog((hostingContext, loggerConfiguration) => + loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration)); } private static void InitializeDataBase() diff --git a/BadNews/Startup.cs b/BadNews/Startup.cs index c84ed90..763b1f9 100644 --- a/BadNews/Startup.cs +++ b/BadNews/Startup.cs @@ -14,6 +14,7 @@ using System.Text; using System.Threading.Tasks; using System.Web; +using Serilog; namespace BadNews { @@ -48,6 +49,7 @@ public void Configure(IApplicationBuilder app) app.UseHttpsRedirection(); app.UseStaticFiles(); + app.UseSerilogRequestLogging(); app.UseStatusCodePagesWithReExecute("/StatusCode/{0}"); app.UseRouting(); app.UseEndpoints(endpoints => diff --git a/BadNews/appsettings.Development.json b/BadNews/appsettings.Development.json index 8983e0f..9e26dfe 100644 --- a/BadNews/appsettings.Development.json +++ b/BadNews/appsettings.Development.json @@ -1,9 +1 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} +{} \ No newline at end of file diff --git a/BadNews/appsettings.json b/BadNews/appsettings.json index d9d9a9b..6f5ed00 100644 --- a/BadNews/appsettings.json +++ b/BadNews/appsettings.json @@ -1,10 +1,29 @@ { - "Logging": { - "LogLevel": { + "Serilog": { + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": ".logs/log-.txt", + "formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact", + "rollingInterval": "Day", + "rollOnFileSizeLimit": true + } + }, + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}" + } + } + ], + "MinimumLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } } }, "AllowedHosts": "*" -} +} \ No newline at end of file From 1dc0e7764b081bd2b18b73228788ff65d6777969 Mon Sep 17 00:00:00 2001 From: DESKTOP-FEDOR Date: Thu, 30 Nov 2023 15:14:23 +0500 Subject: [PATCH 4/6] 2.1 --- BadNews/Controllers/NewsController.cs | 22 +++++++ BadNews/Startup.cs | 9 ++- BadNews/Views/News/Index.cshtml | 92 +++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 BadNews/Controllers/NewsController.cs create mode 100644 BadNews/Views/News/Index.cshtml diff --git a/BadNews/Controllers/NewsController.cs b/BadNews/Controllers/NewsController.cs new file mode 100644 index 0000000..7bc7617 --- /dev/null +++ b/BadNews/Controllers/NewsController.cs @@ -0,0 +1,22 @@ +using BadNews.ModelBuilders.News; +using Microsoft.AspNetCore.Mvc; + +namespace BadNews.Controllers +{ + public class NewsController : Controller + { + private readonly INewsModelBuilder newsModelBuilder; + + public NewsController(INewsModelBuilder newsModelBuilder) + { + this.newsModelBuilder = newsModelBuilder; + } + + public IActionResult Index() + { + var pageIndex = 0; + var model = newsModelBuilder.BuildIndexModel(pageIndex, false, null); + return View(model); + } + } +} \ No newline at end of file diff --git a/BadNews/Startup.cs b/BadNews/Startup.cs index 763b1f9..6ba1fe4 100644 --- a/BadNews/Startup.cs +++ b/BadNews/Startup.cs @@ -35,7 +35,10 @@ public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); - services.AddControllersWithViews(); + + var mvcBuilder = services.AddControllersWithViews(); + if (env.IsDevelopment()) + mvcBuilder.AddRazorRuntimeCompilation(); } // В этом методе конфигурируется последовательность обработки HTTP-запроса @@ -59,7 +62,7 @@ public void Configure(IApplicationBuilder app) controller = "Errors", action = "StatusCode" }); - endpoints.MapControllerRoute("default", "{controller}/{action}"); + endpoints.MapControllerRoute("default", "{controller=News}/{action=Index}"); }); app.Map("/news", newsApp => @@ -69,7 +72,7 @@ public void Configure(IApplicationBuilder app) newsApp.Run(RenderIndexPage); }); - app.MapWhen(context => context.Request.Path == "/", rootPathApp => { rootPathApp.Run(RenderIndexPage); }); + // app.MapWhen(context => context.Request.Path == "/", rootPathApp => { rootPathApp.Run(RenderIndexPage); }); // Остальные запросы — 404 Not Found } diff --git a/BadNews/Views/News/Index.cshtml b/BadNews/Views/News/Index.cshtml new file mode 100644 index 0000000..3634303 --- /dev/null +++ b/BadNews/Views/News/Index.cshtml @@ -0,0 +1,92 @@ +@using System.Web +@model BadNews.Models.News.IndexModel; + + + + + + Bad News + + + + + + +
+
+ +
+ + + + +
+ + + +
+
+
+ + @foreach (var article in Model.PageArticles) + { + var url = $"/news/fullarticle/{HttpUtility.UrlEncode(article.Id.ToString())}"; +
+

+ @article.Header +

+ +

+ @article.Teaser +

+ Читать полностью +
+ } + + +
+ + +
+
+ + + + + + + + + + \ No newline at end of file From 05fc8fd5a8f4caab96f6406045be60575f3db5b7 Mon Sep 17 00:00:00 2001 From: Danil_Titarenko Date: Thu, 30 Nov 2023 23:05:09 +0500 Subject: [PATCH 5/6] up to 5 done --- .../Components/ArchiveLinksViewComponent.cs | 21 +++ BadNews/Components/WeatherViewComponent.cs | 23 ++++ BadNews/Controllers/EditorController.cs | 53 ++++++++ BadNews/Controllers/ErrorsController.cs | 27 ++++ BadNews/Controllers/NewsController.cs | 34 +++++ BadNews/Program.cs | 40 +++++- .../Weather/OpenWeatherForecast.cs | 10 +- .../Repositories/Weather/WeatherForecast.cs | 3 +- .../Weather/WeatherForecastRepository.cs | 17 ++- BadNews/Startup.cs | 120 ++++-------------- BadNews/Views/Editor/Index.cshtml | 43 +++++++ BadNews/Views/Errors/Exception.cshtml | 16 +++ BadNews/Views/Errors/StatusCode.cshtml | 32 +++++ BadNews/Views/News/FullArticle.cshtml | 30 +++++ BadNews/Views/News/Index.cshtml | 84 ++++++++++++ .../Components/ArchiveLinks/Default.cshtml | 13 ++ .../Shared/Components/Weather/Default.cshtml | 11 ++ BadNews/Views/Shared/_Layout.cshtml | 36 ++++++ .../Shared/_ValidationScriptsPartial.cshtml | 3 + BadNews/Views/_ViewImports.cshtml | 2 + BadNews/Views/_ViewStart.cshtml | 5 + BadNews/appsettings.Development.json | 10 +- BadNews/appsettings.json | 29 ++++- 23 files changed, 542 insertions(+), 120 deletions(-) create mode 100644 BadNews/Components/ArchiveLinksViewComponent.cs create mode 100644 BadNews/Components/WeatherViewComponent.cs create mode 100644 BadNews/Controllers/EditorController.cs create mode 100644 BadNews/Controllers/ErrorsController.cs create mode 100644 BadNews/Controllers/NewsController.cs create mode 100644 BadNews/Views/Editor/Index.cshtml create mode 100644 BadNews/Views/Errors/Exception.cshtml create mode 100644 BadNews/Views/Errors/StatusCode.cshtml create mode 100644 BadNews/Views/News/FullArticle.cshtml create mode 100644 BadNews/Views/News/Index.cshtml create mode 100644 BadNews/Views/Shared/Components/ArchiveLinks/Default.cshtml create mode 100644 BadNews/Views/Shared/Components/Weather/Default.cshtml create mode 100644 BadNews/Views/Shared/_Layout.cshtml create mode 100644 BadNews/Views/Shared/_ValidationScriptsPartial.cshtml create mode 100644 BadNews/Views/_ViewImports.cshtml create mode 100644 BadNews/Views/_ViewStart.cshtml diff --git a/BadNews/Components/ArchiveLinksViewComponent.cs b/BadNews/Components/ArchiveLinksViewComponent.cs new file mode 100644 index 0000000..f7189f3 --- /dev/null +++ b/BadNews/Components/ArchiveLinksViewComponent.cs @@ -0,0 +1,21 @@ +using BadNews.Repositories.News; +using Microsoft.AspNetCore.Mvc; + +namespace BadNews.Components +{ + public class ArchiveLinksViewComponent : ViewComponent + { + private readonly INewsRepository newsRepository; + + public ArchiveLinksViewComponent(INewsRepository newsRepository) + { + this.newsRepository = newsRepository; + } + + public IViewComponentResult Invoke() + { + var years = newsRepository.GetYearsWithArticles(); + return View(years); + } + } +} \ No newline at end of file diff --git a/BadNews/Components/WeatherViewComponent.cs b/BadNews/Components/WeatherViewComponent.cs new file mode 100644 index 0000000..f463adb --- /dev/null +++ b/BadNews/Components/WeatherViewComponent.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using BadNews.Repositories.Weather; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace BadNews.Components +{ + public class WeatherViewComponent : ViewComponent + { + private IWeatherForecastRepository weatherForecastRepository; + + public WeatherViewComponent(IWeatherForecastRepository weatherForecastRepository) + { + this.weatherForecastRepository = weatherForecastRepository; + } + + public async Task InvokeAsync() + { + var data = await weatherForecastRepository.GetWeatherForecastAsync(); + return View(data); + } + } +} \ No newline at end of file diff --git a/BadNews/Controllers/EditorController.cs b/BadNews/Controllers/EditorController.cs new file mode 100644 index 0000000..6c3352e --- /dev/null +++ b/BadNews/Controllers/EditorController.cs @@ -0,0 +1,53 @@ +using System; +using BadNews.Models.Editor; +using BadNews.Repositories.News; +using Microsoft.AspNetCore.Mvc; + +namespace BadNews.Controllers +{ + public class EditorController : Controller + { + private readonly INewsRepository newsRepository; + + public EditorController(INewsRepository newsRepository) + { + this.newsRepository = newsRepository; + } + + public IActionResult Index() + { + return View(new IndexViewModel()); + } + + [HttpPost] + public IActionResult CreateArticle([FromForm] IndexViewModel model) + { + if (!ModelState.IsValid) + return View("Index", model); + var id = newsRepository.CreateArticle(new NewsArticle { + Date = DateTime.Now.Date, + Header = model.Header, + Teaser = model.Teaser, + ContentHtml = model.ContentHtml, + }); + + return RedirectToAction("FullArticle", "News", new { + id = id + }); + } + } + + // public class IndexViewModel + // { + // [Required(ErrorMessage = "У новости должен быть заголовок")] + // public string Header { get; set; } + // + // [StopWords("действительно", "реально", "на самом деле", "поверьте", "без обмана", + // ErrorMessage = "Нельзя использовать стоп-слова")] + // public string Teaser { get; set; } + // + // [StopWords("действительно", "реально", "на самом деле", "поверьте", "без обмана", + // ErrorMessage = "Нельзя использовать стоп-слова")] + // public string ContentHtml { get; set; } + // } +} diff --git a/BadNews/Controllers/ErrorsController.cs b/BadNews/Controllers/ErrorsController.cs new file mode 100644 index 0000000..53489fe --- /dev/null +++ b/BadNews/Controllers/ErrorsController.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace BadNews.Controllers +{ + public class ErrorsController : Controller + { + private readonly ILogger logger; + + public ErrorsController(ILogger logger) + { + this.logger = logger; + } + + public IActionResult StatusCode(int? code) + { + logger.LogWarning("status-code {code} at {time}", code, DateTime.Now); + return View(code); + } + + public IActionResult Exception() + { + return View(null, HttpContext.TraceIdentifier); + } + } +} \ No newline at end of file diff --git a/BadNews/Controllers/NewsController.cs b/BadNews/Controllers/NewsController.cs new file mode 100644 index 0000000..0ef5292 --- /dev/null +++ b/BadNews/Controllers/NewsController.cs @@ -0,0 +1,34 @@ +using System; +using BadNews.ModelBuilders.News; +using Microsoft.AspNetCore.Mvc; + +namespace BadNews.Controllers +{ + public class NewsController : Controller + { + private readonly INewsModelBuilder newsModelBuilder; + + public NewsController(INewsModelBuilder newsModelBuilder) + { + this.newsModelBuilder = newsModelBuilder; + } + + public IActionResult Index(int? year) + { + var pageIndex = 0; + var model = newsModelBuilder.BuildIndexModel(pageIndex, true, year); + return View(model); + } + + public IActionResult FullArticle(Guid id) + { + var model = newsModelBuilder.BuildFullArticleModel(id); + if (model == null) + { + return NotFound(); + } + + return View(model); + } + } +} \ No newline at end of file diff --git a/BadNews/Program.cs b/BadNews/Program.cs index c2036c8..543813a 100644 --- a/BadNews/Program.cs +++ b/BadNews/Program.cs @@ -1,7 +1,10 @@ +using System; using System.Linq; using BadNews.Repositories.News; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; +using Serilog; namespace BadNews { @@ -10,7 +13,31 @@ public class Program public static void Main(string[] args) { InitializeDataBase(); - CreateHostBuilder(args).Build().Run(); + + Log.Logger = new LoggerConfiguration() + .WriteTo.File(".logs/start-host-log-.txt", + rollingInterval: RollingInterval.Day, + rollOnFileSizeLimit: true, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}") + .CreateLogger(); + + try + { + Log.Information("Creating web host builder"); + var hostBuilder = CreateHostBuilder(args); + Log.Information("Building web host"); + var host = hostBuilder.Build(); + Log.Information("Running web host"); + host.Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly"); + } + finally + { + Log.CloseAndFlush(); + } } public static IHostBuilder CreateHostBuilder(string[] args) @@ -19,7 +46,14 @@ public static IHostBuilder CreateHostBuilder(string[] args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); - }); + // webBuilder.UseEnvironment(Environments.Development); + }) + .ConfigureHostConfiguration(config => + { + config.AddJsonFile("appsettings.Secret.json", optional: true, reloadOnChange: false); + }) + .UseSerilog((hostingContext, loggerConfiguration) => + loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration)); } private static void InitializeDataBase() @@ -36,4 +70,4 @@ private static void InitializeDataBase() repository.InitializeDataBase(articles); } } -} +} \ No newline at end of file diff --git a/BadNews/Repositories/Weather/OpenWeatherForecast.cs b/BadNews/Repositories/Weather/OpenWeatherForecast.cs index 38c8532..ef73a56 100644 --- a/BadNews/Repositories/Weather/OpenWeatherForecast.cs +++ b/BadNews/Repositories/Weather/OpenWeatherForecast.cs @@ -18,12 +18,12 @@ public class WeatherInfo public class MainInfo { - public int Temp { get; set; } + public double Temp { get; set; } public decimal FeelsLike { get; set; } - public int TempMin { get; set; } - public int TempMax { get; set; } - public int Pressure { get; set; } - public int Humidity { get; set; } + public double TempMin { get; set; } + public double TempMax { get; set; } + public double Pressure { get; set; } + public double Humidity { get; set; } } } } diff --git a/BadNews/Repositories/Weather/WeatherForecast.cs b/BadNews/Repositories/Weather/WeatherForecast.cs index 4e67c55..4ba2473 100644 --- a/BadNews/Repositories/Weather/WeatherForecast.cs +++ b/BadNews/Repositories/Weather/WeatherForecast.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; namespace BadNews.Repositories.Weather @@ -13,7 +14,7 @@ public static WeatherForecast CreateFrom(OpenWeatherForecast forecast) { return new WeatherForecast { - TemperatureInCelsius = forecast.Main.Temp, + TemperatureInCelsius = (int)forecast.Main.Temp, IconUrl = forecast.Weather.FirstOrDefault()?.IconUrl ?? defaultWeatherImageUrl }; } diff --git a/BadNews/Repositories/Weather/WeatherForecastRepository.cs b/BadNews/Repositories/Weather/WeatherForecastRepository.cs index f8d4cd7..dd24b9e 100644 --- a/BadNews/Repositories/Weather/WeatherForecastRepository.cs +++ b/BadNews/Repositories/Weather/WeatherForecastRepository.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Microsoft.Extensions.Options; namespace BadNews.Repositories.Weather { @@ -8,10 +9,24 @@ public class WeatherForecastRepository : IWeatherForecastRepository private const string defaultWeatherImageUrl = "/images/cloudy.png"; private readonly Random random = new Random(); + private readonly OpenWeatherClient weatherClient; + + public WeatherForecastRepository(IOptions weatherOptions) + { + var apiKey = weatherOptions?.Value.ApiKey; + weatherClient = new OpenWeatherClient(apiKey); + } public async Task GetWeatherForecastAsync() { - return BuildRandomForecast(); + try + { + return WeatherForecast.CreateFrom(await weatherClient.GetWeatherFromApiAsync()); + } + catch (Exception) + { + return BuildRandomForecast(); + } } private WeatherForecast BuildRandomForecast() diff --git a/BadNews/Startup.cs b/BadNews/Startup.cs index cd346c3..19821c2 100644 --- a/BadNews/Startup.cs +++ b/BadNews/Startup.cs @@ -1,19 +1,14 @@ using BadNews.ModelBuilders.News; -using BadNews.Models.News; using BadNews.Repositories.News; +using BadNews.Repositories.Weather; +using BadNews.Validation; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.DataAnnotations; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; +using Serilog; namespace BadNews { @@ -34,107 +29,38 @@ public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.Configure(configuration.GetSection("OpenWeather")); + var mvcBuilder = services.AddControllersWithViews(); + if (env.IsDevelopment()) + mvcBuilder.AddRazorRuntimeCompilation(); } // В этом методе конфигурируется последовательность обработки HTTP-запроса public void Configure(IApplicationBuilder app) { - app.UseDeveloperExceptionPage(); + if (env.IsDevelopment()) + app.UseDeveloperExceptionPage(); + else + app.UseExceptionHandler("/Errors/Exception"); app.UseHttpsRedirection(); app.UseStaticFiles(); + app.UseSerilogRequestLogging(); + app.UseStatusCodePagesWithReExecute("/StatusCode/{0}"); - app.Map("/news", newsApp => + app.UseRouting(); + app.UseEndpoints(endpoints => { - newsApp.Map("/fullarticle", fullArticleApp => + endpoints.MapControllerRoute("status-code", "StatusCode/{code?}", new { - fullArticleApp.Run(RenderFullArticlePage); + controller = "Errors", + action = "StatusCode" }); - - newsApp.Run(RenderIndexPage); - }); - - app.MapWhen(context => context.Request.Path == "/", rootPathApp => - { - rootPathApp.Run(RenderIndexPage); + endpoints.MapControllerRoute("default", "{controller=News}/{action=Index}/{id?}"); }); // Остальные запросы — 404 Not Found } - - // Региональные настройки, которые используются при обработке запросов новостей. - private static CultureInfo culture = CultureInfo.CreateSpecificCulture("ru-ru"); - - private async Task RenderIndexPage(HttpContext context) - { - // Model Builder достается из DI-контейнера - var newsModelBuilder = context.RequestServices.GetRequiredService(); - - // Извлекаются входные параметры запроса - int.TryParse(context.Request.Query["pageIndex"], out var pageIndex); - - // Строится модель страницы - var model = newsModelBuilder.BuildIndexModel(pageIndex, false, null); - - // Строится HTML для модели - string pageHtml = BuildIndexPageHtml(model); - - // Результат записывается в ответ - await context.Response.WriteAsync(pageHtml); - } - - private static string BuildIndexPageHtml(IndexModel model) - { - var articlesBuilder = new StringBuilder(); - var articleTemplate = File.ReadAllText("./$Content/Templates/NewsArticle.hbs"); - foreach (var articleModel in model.PageArticles) - { - var articleHtml = articleTemplate - .Replace("{{header}}", articleModel.Header) - .Replace("{{date}}", articleModel.Date.ToString("d MMM yyyy", culture)) - .Replace("{{teaser}}", articleModel.Teaser) - .Replace("{{url}}", $"/news/fullarticle/{HttpUtility.UrlEncode(articleModel.Id.ToString())}"); - articlesBuilder.AppendLine(articleHtml); - } - - var pageTemplate = File.ReadAllText("./$Content/Templates/Index.hbs"); - var pageHtml = pageTemplate - .Replace("{{articles}}", articlesBuilder.ToString()) - .Replace("{{newerUrl}}", !model.IsFirst - ? $"/news?pageIndex={HttpUtility.UrlEncode((model.PageIndex - 1).ToString())}" - : "") - .Replace("{{olderUrl}}", !model.IsLast - ? $"/news?pageIndex={HttpUtility.UrlEncode((model.PageIndex + 1).ToString())}" - : ""); - return pageHtml; - } - - private async Task RenderFullArticlePage(HttpContext context) - { - // Model Builder достается из DI-контейнера - var newsModelBuilder = context.RequestServices.GetRequiredService(); - - // Извлекаются входные параметры запроса - var idString = context.Request.Path.Value.Split('/').ElementAtOrDefault(1); - Guid.TryParse(idString, out var id); - - // Строится модель страницы - var model = newsModelBuilder.BuildFullArticleModel(id); - - // Строится HTML для модели - string pageHtml = BuildFullArticlePageHtml(model); - - // Результат записывается в ответ - await context.Response.WriteAsync(pageHtml); - } - - private static string BuildFullArticlePageHtml(FullArticleModel model) - { - var pageTemplate = File.ReadAllText("./$Content/Templates/FullArticle.hbs"); - var pageHtml = pageTemplate - .Replace("{{header}}", model.Article.Header) - .Replace("{{date}}", model.Article.Date.ToString("d MMM yyyy", culture)) - .Replace("{{content}}", model.Article.ContentHtml); - return pageHtml; - } } -} +} \ No newline at end of file diff --git a/BadNews/Views/Editor/Index.cshtml b/BadNews/Views/Editor/Index.cshtml new file mode 100644 index 0000000..ea50143 --- /dev/null +++ b/BadNews/Views/Editor/Index.cshtml @@ -0,0 +1,43 @@ +@model BadNews.Models.Editor.IndexViewModel +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/BadNews/Views/Errors/Exception.cshtml b/BadNews/Views/Errors/Exception.cshtml new file mode 100644 index 0000000..feaf1e8 --- /dev/null +++ b/BadNews/Views/Errors/Exception.cshtml @@ -0,0 +1,16 @@ +@model string + + + +
+

Во время обработки вашего запроса возникла ошибка

+ + @if (!string.IsNullOrEmpty(Model)) + { +

Request ID: @Model

+ } + +

Вернуться на главную страницу

+
+ + diff --git a/BadNews/Views/Errors/StatusCode.cshtml b/BadNews/Views/Errors/StatusCode.cshtml new file mode 100644 index 0000000..327f148 --- /dev/null +++ b/BadNews/Views/Errors/StatusCode.cshtml @@ -0,0 +1,32 @@ +@model int? +@{ + Layout = null; +} + + + + + + + + Bad News + + +
@(Model?.ToString() ?? "Неизвестный код состояния")
+ + + \ No newline at end of file diff --git a/BadNews/Views/News/FullArticle.cshtml b/BadNews/Views/News/FullArticle.cshtml new file mode 100644 index 0000000..df5e46a --- /dev/null +++ b/BadNews/Views/News/FullArticle.cshtml @@ -0,0 +1,30 @@ +@model BadNews.Models.News.FullArticleModel + + + +
+
+
+
+

@Model.Article.Header

+ + @Html.Raw(@Model.Article.ContentHtml) +
+
+ + +
+
+ + + + diff --git a/BadNews/Views/News/Index.cshtml b/BadNews/Views/News/Index.cshtml new file mode 100644 index 0000000..10c5f38 --- /dev/null +++ b/BadNews/Views/News/Index.cshtml @@ -0,0 +1,84 @@ +@model BadNews.Models.News.IndexModel +@using System.Web + +@section Header +{ +
+ @foreach (var article in Model.FeaturedArticles) + { +
+
+
+

@article.Header

+
@article.Date.ToString("yy MMM dd", ViewBag.Culture)
+

+ @article.Teaser +

+ Читать полностью +
+
+
+ } +
+ +} + + + +
+
+
+ @foreach (var article in Model.PageArticles) + { +
+

@article.Header

+ +

+ @article.Teaser +

+ Читать полностью +
+ } + + + +
+ + +
+
+ + + + \ No newline at end of file diff --git a/BadNews/Views/Shared/Components/ArchiveLinks/Default.cshtml b/BadNews/Views/Shared/Components/ArchiveLinks/Default.cshtml new file mode 100644 index 0000000..0c0d257 --- /dev/null +++ b/BadNews/Views/Shared/Components/ArchiveLinks/Default.cshtml @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/BadNews/Views/Shared/Components/Weather/Default.cshtml b/BadNews/Views/Shared/Components/Weather/Default.cshtml new file mode 100644 index 0000000..e230e8b --- /dev/null +++ b/BadNews/Views/Shared/Components/Weather/Default.cshtml @@ -0,0 +1,11 @@ +@model BadNews.Repositories.Weather.WeatherForecast +@{ + string FormatTemperature(int temperature) => temperature > 0 ? $"+{temperature}" : temperature.ToString(); +} +
+

Погода сейчас

+
+ +
+
@FormatTemperature(Model.TemperatureInCelsius)
+
\ No newline at end of file diff --git a/BadNews/Views/Shared/_Layout.cshtml b/BadNews/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000..c74a469 --- /dev/null +++ b/BadNews/Views/Shared/_Layout.cshtml @@ -0,0 +1,36 @@ + + + + + + Bad News + + + + + + +
+
+ +
+ + + + @RenderSection("Header", false) +
+ + + +@RenderBody() + + + + + + + + + \ No newline at end of file diff --git a/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml b/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml new file mode 100644 index 0000000..711409c --- /dev/null +++ b/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/BadNews/Views/_ViewImports.cshtml b/BadNews/Views/_ViewImports.cshtml new file mode 100644 index 0000000..9a77ebc --- /dev/null +++ b/BadNews/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, BadNews \ No newline at end of file diff --git a/BadNews/Views/_ViewStart.cshtml b/BadNews/Views/_ViewStart.cshtml new file mode 100644 index 0000000..6fedfde --- /dev/null +++ b/BadNews/Views/_ViewStart.cshtml @@ -0,0 +1,5 @@ +@using System.Globalization +@{ + ViewBag.Culture = CultureInfo.CreateSpecificCulture("ru-ru"); + Layout = "_Layout"; +} \ No newline at end of file diff --git a/BadNews/appsettings.Development.json b/BadNews/appsettings.Development.json index 8983e0f..077404a 100644 --- a/BadNews/appsettings.Development.json +++ b/BadNews/appsettings.Development.json @@ -1,9 +1,3 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} + +} \ No newline at end of file diff --git a/BadNews/appsettings.json b/BadNews/appsettings.json index d9d9a9b..6f5ed00 100644 --- a/BadNews/appsettings.json +++ b/BadNews/appsettings.json @@ -1,10 +1,29 @@ { - "Logging": { - "LogLevel": { + "Serilog": { + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": ".logs/log-.txt", + "formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact", + "rollingInterval": "Day", + "rollOnFileSizeLimit": true + } + }, + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}" + } + } + ], + "MinimumLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } } }, "AllowedHosts": "*" -} +} \ No newline at end of file From 68b551247ad65106aec9f5abebe78a877e335be8 Mon Sep 17 00:00:00 2001 From: Danil_Titarenko Date: Thu, 7 Dec 2023 14:42:53 +0500 Subject: [PATCH 6/6] all done --- .../Components/ArchiveLinksViewComponent.cs | 22 ++++++++++++-- BadNews/Controllers/EditorController.cs | 13 +++++++-- BadNews/Controllers/NewsController.cs | 21 ++++++++++++-- BadNews/Elevation/ElevationExtensions.cs | 6 ++-- BadNews/Elevation/ElevationMiddleware.cs | 27 ++++++++++++++--- BadNews/Program.cs | 2 +- BadNews/Startup.cs | 29 +++++++++++++++++-- BadNews/Views/News/FullArticle.cshtml | 14 +++++++-- BadNews/Views/Shared/_Layout.cshtml | 20 ++++++++----- .../Shared/_ValidationScriptsPartial.cshtml | 6 ++-- 10 files changed, 130 insertions(+), 30 deletions(-) diff --git a/BadNews/Components/ArchiveLinksViewComponent.cs b/BadNews/Components/ArchiveLinksViewComponent.cs index f7189f3..731ccae 100644 --- a/BadNews/Components/ArchiveLinksViewComponent.cs +++ b/BadNews/Components/ArchiveLinksViewComponent.cs @@ -1,20 +1,36 @@ -using BadNews.Repositories.News; +using System; +using BadNews.Repositories.News; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; namespace BadNews.Components { public class ArchiveLinksViewComponent : ViewComponent { private readonly INewsRepository newsRepository; + private readonly IMemoryCache memoryCache; - public ArchiveLinksViewComponent(INewsRepository newsRepository) + public ArchiveLinksViewComponent(INewsRepository newsRepository, IMemoryCache memoryCache) { this.newsRepository = newsRepository; + this.memoryCache = memoryCache; } public IViewComponentResult Invoke() { - var years = newsRepository.GetYearsWithArticles(); + var cacheKey = nameof(ArchiveLinksViewComponent); + if (!memoryCache.TryGetValue(cacheKey, out var years)) + { + years = newsRepository.GetYearsWithArticles(); + if (years != null) + { + memoryCache.Set(cacheKey, years, new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30) + }); + } + } + years = newsRepository.GetYearsWithArticles(); return View(years); } } diff --git a/BadNews/Controllers/EditorController.cs b/BadNews/Controllers/EditorController.cs index 6c3352e..980ec96 100644 --- a/BadNews/Controllers/EditorController.cs +++ b/BadNews/Controllers/EditorController.cs @@ -1,14 +1,16 @@ using System; +using BadNews.Elevation; using BadNews.Models.Editor; using BadNews.Repositories.News; using Microsoft.AspNetCore.Mvc; namespace BadNews.Controllers { + [ElevationRequiredFilter] public class EditorController : Controller { private readonly INewsRepository newsRepository; - + public EditorController(INewsRepository newsRepository) { this.newsRepository = newsRepository; @@ -35,8 +37,15 @@ public IActionResult CreateArticle([FromForm] IndexViewModel model) id = id }); } + + [HttpPost] + public IActionResult DeleteArticle(Guid id) + { + newsRepository.DeleteArticleById(id); + return RedirectToAction("Index", "News"); + } } - + // public class IndexViewModel // { // [Required(ErrorMessage = "У новости должен быть заголовок")] diff --git a/BadNews/Controllers/NewsController.cs b/BadNews/Controllers/NewsController.cs index 0ef5292..c686784 100644 --- a/BadNews/Controllers/NewsController.cs +++ b/BadNews/Controllers/NewsController.cs @@ -1,16 +1,20 @@ using System; using BadNews.ModelBuilders.News; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; namespace BadNews.Controllers { + [ResponseCache(Duration = 30, Location = ResponseCacheLocation.Client, VaryByHeader = "Cookie")] public class NewsController : Controller { private readonly INewsModelBuilder newsModelBuilder; + private readonly IMemoryCache memoryCache; - public NewsController(INewsModelBuilder newsModelBuilder) + public NewsController(INewsModelBuilder newsModelBuilder, IMemoryCache memoryCache) { this.newsModelBuilder = newsModelBuilder; + this.memoryCache = memoryCache; } public IActionResult Index(int? year) @@ -20,14 +24,25 @@ public IActionResult Index(int? year) return View(model); } + [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult FullArticle(Guid id) { - var model = newsModelBuilder.BuildFullArticleModel(id); - if (model == null) + if (memoryCache.TryGetValue(id, out var model)) return View(model); + model = newsModelBuilder.BuildFullArticleModel(id); + if (model != null) + { + memoryCache.Set(id, model, new MemoryCacheEntryOptions + { + SlidingExpiration = TimeSpan.FromSeconds(10) + }); + } + + else { return NotFound(); } + return View(model); } } diff --git a/BadNews/Elevation/ElevationExtensions.cs b/BadNews/Elevation/ElevationExtensions.cs index eed04db..e8f0c73 100644 --- a/BadNews/Elevation/ElevationExtensions.cs +++ b/BadNews/Elevation/ElevationExtensions.cs @@ -7,8 +7,8 @@ public static class ElevationExtensions { public static bool IsElevated(this HttpRequest request) { - bool isElevated = request.Cookies.TryGetValue(ElevationConstants.CookieName, out var value) - && value == ElevationConstants.CookieValue; + var isElevated = request.Cookies.TryGetValue(ElevationConstants.CookieName, out var value) + && value == ElevationConstants.CookieValue; return isElevated; } @@ -17,4 +17,4 @@ public static bool IsElevated(this ViewContext viewContext) return viewContext.HttpContext.Request.IsElevated(); } } -} +} \ No newline at end of file diff --git a/BadNews/Elevation/ElevationMiddleware.cs b/BadNews/Elevation/ElevationMiddleware.cs index 52da41e..aa8081a 100644 --- a/BadNews/Elevation/ElevationMiddleware.cs +++ b/BadNews/Elevation/ElevationMiddleware.cs @@ -7,15 +7,34 @@ namespace BadNews.Elevation public class ElevationMiddleware { private readonly RequestDelegate next; - + public ElevationMiddleware(RequestDelegate next) { this.next = next; } - + public async Task InvokeAsync(HttpContext context) { - throw new NotImplementedException(); + if (!context.Request.Path.Equals("/elevation")) + { + await next(context); + return; + } + + if (context.Request.Query.ContainsKey("up")) + { + context.Response.Cookies.Append(ElevationConstants.CookieName, ElevationConstants.CookieValue, + new CookieOptions + { + HttpOnly = true + }); + } + else + { + context.Response.Cookies.Delete(ElevationConstants.CookieName); + } + + context.Response.Redirect("/"); } } -} +} \ No newline at end of file diff --git a/BadNews/Program.cs b/BadNews/Program.cs index 543813a..7c5e6b9 100644 --- a/BadNews/Program.cs +++ b/BadNews/Program.cs @@ -58,7 +58,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) private static void InitializeDataBase() { - const int newsArticleCount = 100; + const int newsArticleCount = 1000; var generator = new NewsGenerator(); var articles = generator.GenerateNewsArticles() diff --git a/BadNews/Startup.cs b/BadNews/Startup.cs index 19821c2..cfa76f5 100644 --- a/BadNews/Startup.cs +++ b/BadNews/Startup.cs @@ -1,9 +1,12 @@ -using BadNews.ModelBuilders.News; +using System; +using BadNews.Elevation; +using BadNews.ModelBuilders.News; using BadNews.Repositories.News; using BadNews.Repositories.Weather; using BadNews.Validation; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.DataAnnotations; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -31,7 +34,12 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddMemoryCache(); services.Configure(configuration.GetSection("OpenWeather")); + services.AddResponseCompression(options => + { + options.EnableForHttps = true; + }); var mvcBuilder = services.AddControllersWithViews(); if (env.IsDevelopment()) mvcBuilder.AddRazorRuntimeCompilation(); @@ -45,9 +53,22 @@ public void Configure(IApplicationBuilder app) else app.UseExceptionHandler("/Errors/Exception"); app.UseHttpsRedirection(); - app.UseStaticFiles(); + app.UseResponseCompression(); + app.UseStaticFiles(new StaticFileOptions() + { + OnPrepareResponse = options => + { + options.Context.Response.GetTypedHeaders().CacheControl = + new Microsoft.Net.Http.Headers.CacheControlHeaderValue() + { + Public = false, + MaxAge = TimeSpan.FromHours(1) + }; + } + }); app.UseSerilogRequestLogging(); app.UseStatusCodePagesWithReExecute("/StatusCode/{0}"); + app.UseMiddleware(); app.UseRouting(); app.UseEndpoints(endpoints => @@ -59,6 +80,10 @@ public void Configure(IApplicationBuilder app) }); endpoints.MapControllerRoute("default", "{controller=News}/{action=Index}/{id?}"); }); + app.MapWhen(context => context.Request.IsElevated(), branchApp => + { + branchApp.UseDirectoryBrowser("/files"); + }); // Остальные запросы — 404 Not Found } diff --git a/BadNews/Views/News/FullArticle.cshtml b/BadNews/Views/News/FullArticle.cshtml index df5e46a..f0e4ae6 100644 --- a/BadNews/Views/News/FullArticle.cshtml +++ b/BadNews/Views/News/FullArticle.cshtml @@ -1,4 +1,7 @@ -@model BadNews.Models.News.FullArticleModel +@using BadNews.Elevation +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using BadNews.Components +@model BadNews.Models.News.FullArticleModel @@ -10,6 +13,13 @@ + @if (ViewContext.IsElevated()) + { +
+ +
+ } @Html.Raw(@Model.Article.ContentHtml) @@ -27,4 +37,4 @@

- + \ No newline at end of file diff --git a/BadNews/Views/Shared/_Layout.cshtml b/BadNews/Views/Shared/_Layout.cshtml index c74a469..8d366ca 100644 --- a/BadNews/Views/Shared/_Layout.cshtml +++ b/BadNews/Views/Shared/_Layout.cshtml @@ -1,4 +1,6 @@ - +@using BadNews.Elevation +@using Microsoft.AspNetCore.Mvc.TagHelpers + @@ -7,14 +9,18 @@ Bad News - - + rel="stylesheet" asp-append-version="true"/> + +
+ @if (ViewContext.IsElevated()) + { + есть привилегии + }
@@ -28,9 +34,9 @@ - - - + + + \ No newline at end of file diff --git a/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml b/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml index 711409c..a5ceffc 100644 --- a/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml +++ b/BadNews/Views/Shared/_ValidationScriptsPartial.cshtml @@ -1,3 +1,3 @@ - - - \ No newline at end of file + + + \ No newline at end of file