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
+
+
+ @using System.Globalization
+ @{
+ var culture = CultureInfo.CreateSpecificCulture("ru-ru");
+ var date = article.Date.ToString("d MMM yyyy", culture);
+ }
+ @date
+
+
+ @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
+
+ @Model.Article.Date.ToString("d MMM yyyy", ViewBag.Culture)
+
+ @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.Date.ToString("d MMM yyyy", ViewBag.Culture)
+
+
+ @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 @@
@Model.Article.Date.ToString("d MMM yyyy", ViewBag.Culture)
+ @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"/>
+
+
@@ -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