Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2020 Robert Bosch GmbH |
| 3 | * Author: Constantin Ziesche (constantin.ziesche@bosch.com) |
| 4 | * |
| 5 | * This program and the accompanying materials are made available under the |
| 6 | * terms of the Eclipse Public License 2.0 which is available at |
| 7 | * http://www.eclipse.org/legal/epl-2.0 |
| 8 | * |
| 9 | * SPDX-License-Identifier: EPL-2.0 |
| 10 | *******************************************************************************/ |
Constantin Ziesche | ce27660 | 2020-10-27 09:36:00 +0100 | [diff] [blame] | 11 | using BaSyx.Components.Common.Abstractions; |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 12 | using BaSyx.Utils.AssemblyHandling; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 13 | using BaSyx.Utils.DependencyInjection; |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 14 | using BaSyx.Utils.JsonHandling; |
| 15 | using BaSyx.Utils.ResultHandling; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 16 | using BaSyx.Utils.Settings.Types; |
| 17 | using Microsoft.AspNetCore.Builder; |
| 18 | using Microsoft.AspNetCore.Hosting; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 19 | using Microsoft.AspNetCore.Http; |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 20 | using Microsoft.AspNetCore.Mvc; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 21 | using Microsoft.AspNetCore.Rewrite; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 22 | using Microsoft.Extensions.DependencyInjection; |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 23 | using Microsoft.Extensions.FileProviders; |
| 24 | using Microsoft.Extensions.Hosting; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 25 | using Microsoft.Extensions.Logging; |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 26 | using Newtonsoft.Json; |
| 27 | using Newtonsoft.Json.Serialization; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 28 | using NLog; |
| 29 | using NLog.Web; |
| 30 | using System; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 31 | using System.Collections.Generic; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 32 | using System.IO; |
| 33 | using System.Linq; |
Constantin Ziesche | 02817f1 | 2020-08-04 21:40:43 +0200 | [diff] [blame] | 34 | using System.Reflection; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 35 | using System.Text.RegularExpressions; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 36 | using System.Threading; |
| 37 | using System.Threading.Tasks; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 38 | using System.Web; |
Constantin Ziesche | 02817f1 | 2020-08-04 21:40:43 +0200 | [diff] [blame] | 39 | using static BaSyx.Utils.Settings.Types.ServerSettings; |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 40 | |
| 41 | namespace BaSyx.Components.Common |
| 42 | { |
Constantin Ziesche | ce27660 | 2020-10-27 09:36:00 +0100 | [diff] [blame] | 43 | public abstract class ServerApplication : IServerApplication |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 44 | { |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 45 | private static readonly Logger logger = NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger(); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 46 | |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 47 | private string _contentRoot; |
| 48 | private string _webRoot; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 49 | private bool _secure = false; |
| 50 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 51 | private readonly List<Action<IApplicationBuilder>> AppBuilderPipeline; |
| 52 | private readonly List<Action<IServiceCollection>> ServiceBuilderPipeline; |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 53 | |
| 54 | public const string DEFAULT_CONTENT_ROOT = "Content"; |
| 55 | public const string DEFAULT_WEB_ROOT = "wwwroot"; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 56 | public const string UI_RELATIVE_PATH = "/ui"; |
Constantin Ziesche | b10014d | 2021-01-24 11:57:29 +0100 | [diff] [blame^] | 57 | public const string FILES_PATH = "/files"; |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 58 | public const string ERROR_PATH = "/error"; |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 59 | public const string CONTROLLER_ASSEMBLY_NAME = "BaSyx.API.Http.Controllers"; |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 60 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 61 | public Assembly ControllerAssembly { get; private set; } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 62 | public ServerSettings Settings { get; protected set; } |
| 63 | public IWebHostBuilder WebHostBuilder { get; protected set; } |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 64 | |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 65 | public string ExecutionPath { get; } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 66 | |
| 67 | public Action ApplicationStarted { get; set; } |
| 68 | |
| 69 | public Action ApplicationStopping { get; set; } |
| 70 | |
| 71 | public Action ApplicationStopped { get; set; } |
| 72 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 73 | protected ServerApplication() : this(null, null) |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 74 | { } |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 75 | protected ServerApplication(ServerSettings settings) : this(settings, null) |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 76 | { } |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 77 | protected ServerApplication(ServerSettings settings, string[] webHostBuilderArgs) |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 78 | { |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 79 | ExecutionPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 80 | ControllerAssembly = Assembly.Load(CONTROLLER_ASSEMBLY_NAME); |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 81 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 82 | if (!EmbeddedResource.CheckOrWriteRessourceToFile(typeof(ServerApplication).Assembly, Path.Combine(ExecutionPath, "NLog.config"))) |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 83 | logger.Error("NLog.config cannot be loaded or written"); |
| 84 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 85 | if (settings == null && !EmbeddedResource.CheckOrWriteRessourceToFile(typeof(ServerApplication).Assembly, Path.Combine(ExecutionPath, "ServerSettings.xml"))) |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 86 | logger.Error("ServerSettings.xml cannot be loaded or written"); |
| 87 | |
| 88 | Settings = settings ?? ServerSettings.LoadSettingsFromFile("ServerSettings.xml") ?? throw new ArgumentNullException(nameof(settings)); |
| 89 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 90 | WebHostBuilder = DefaultWebHostBuilder.CreateWebHostBuilder(webHostBuilderArgs, Settings); |
| 91 | AppBuilderPipeline = new List<Action<IApplicationBuilder>>(); |
| 92 | ServiceBuilderPipeline = new List<Action<IServiceCollection>>(); |
| 93 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 94 | WebHostBuilder.ConfigureServices( (context, services) => |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 95 | { |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 96 | ConfigureServices(context, services); |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 97 | }); |
| 98 | |
| 99 | WebHostBuilder.Configure(app => |
| 100 | { |
| 101 | Configure(app); |
| 102 | }); |
| 103 | |
| 104 | WebHostBuilder.UseNLog(); |
| 105 | ConfigureLogging(logger.GetLogLevel()); |
| 106 | |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 107 | if (string.IsNullOrEmpty(Settings.ServerConfig.Hosting.ContentPath)) |
| 108 | _contentRoot = Path.Join(ExecutionPath, DEFAULT_CONTENT_ROOT); |
| 109 | else if (Path.IsPathRooted(Settings.ServerConfig.Hosting.ContentPath)) |
| 110 | _contentRoot = Settings.ServerConfig.Hosting.ContentPath; |
| 111 | else |
| 112 | _contentRoot = Path.Join(ExecutionPath, Settings.ServerConfig.Hosting.ContentPath); |
| 113 | |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 114 | try |
| 115 | { |
| 116 | if (!Directory.Exists(_contentRoot)) |
| 117 | Directory.CreateDirectory(_contentRoot); |
| 118 | WebHostBuilder.UseContentRoot(_contentRoot); |
| 119 | } |
| 120 | catch (Exception e) |
| 121 | { |
| 122 | logger.Error(e, $"ContentRoot path {_contentRoot} cannot be created "); |
| 123 | } |
| 124 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 125 | _webRoot = Path.Join(ExecutionPath, DEFAULT_WEB_ROOT); |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 126 | |
| 127 | try |
| 128 | { |
| 129 | if (!Directory.Exists(_webRoot)) |
| 130 | Directory.CreateDirectory(_webRoot); |
| 131 | WebHostBuilder.UseWebRoot(_webRoot); |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 132 | logger.Info($"wwwroot-Path: {_webRoot}"); |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 133 | } |
| 134 | catch (Exception e) |
| 135 | { |
| 136 | logger.Error(e, $"WebRoot path {_webRoot} cannot be created "); |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 137 | } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | public virtual void Run() |
| 141 | { |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 142 | logger.Info("Starting Server..."); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 143 | |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 144 | WebHostBuilder.Build().Run(); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 145 | } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 146 | public virtual async Task RunAsync(CancellationToken cancellationToken = default) |
| 147 | { |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 148 | logger.Info("Starting Server asynchronously..."); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 149 | |
| 150 | await WebHostBuilder.Build().RunAsync(cancellationToken); |
| 151 | } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 152 | public virtual void ConfigureLogging(Microsoft.Extensions.Logging.LogLevel logLevel) |
| 153 | { |
| 154 | WebHostBuilder.ConfigureLogging(logging => |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 155 | { |
| 156 | logging.ClearProviders(); |
| 157 | logging.SetMinimumLevel(logLevel); |
| 158 | }); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 159 | } |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 160 | public virtual void Configure(Action<IApplicationBuilder> app) => AppBuilderPipeline.Add(app); |
| 161 | public virtual void ConfigureServices(Action<IServiceCollection> services) => ServiceBuilderPipeline.Add(services); |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 162 | public virtual void UseContentRoot(string contentRoot) |
| 163 | { |
| 164 | _contentRoot = contentRoot; |
| 165 | WebHostBuilder.UseContentRoot(contentRoot); |
| 166 | } |
| 167 | public virtual void UseWebRoot(string webRoot) |
| 168 | { |
| 169 | _webRoot = webRoot; |
| 170 | WebHostBuilder.UseWebRoot(webRoot); |
| 171 | } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 172 | public virtual void UseUrls(params string[] urls) |
| 173 | { |
| 174 | WebHostBuilder.UseUrls(urls); |
| 175 | if (Settings?.ServerConfig?.Hosting != null) |
| 176 | Settings.ServerConfig.Hosting.Urls = urls?.ToList(); |
| 177 | } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 178 | public virtual void ProvideContent(Uri relativeUri, Stream content) |
| 179 | { |
| 180 | try |
| 181 | { |
| 182 | using (Stream stream = content) |
| 183 | { |
| 184 | string fileName = Path.GetFileName(relativeUri.ToString()); |
Constantin Ziesche | e837f99 | 2020-08-19 12:04:32 +0200 | [diff] [blame] | 185 | logger.Info("FileName: " + fileName); |
| 186 | string directory = Path.GetDirectoryName(relativeUri.ToString()).TrimStart('\\'); |
| 187 | logger.Info("Directory: " + directory); |
| 188 | |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 189 | string hostingDirectory = Path.Join(_contentRoot, directory); |
Constantin Ziesche | e837f99 | 2020-08-19 12:04:32 +0200 | [diff] [blame] | 190 | |
| 191 | logger.Info($"Try creating hosting directory if not existing: {hostingDirectory}"); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 192 | Directory.CreateDirectory(hostingDirectory); |
| 193 | |
Constantin Ziesche | e837f99 | 2020-08-19 12:04:32 +0200 | [diff] [blame] | 194 | string filePath = Path.Join(hostingDirectory, fileName); |
| 195 | logger.Info($"Try writing file: {filePath}"); |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 196 | |
| 197 | using (FileStream fileStream = File.OpenWrite(filePath)) |
| 198 | { |
| 199 | stream.CopyTo(fileStream); |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | catch (Exception e) |
| 204 | { |
| 205 | logger.Error(e, $"Error providing content {relativeUri}"); |
| 206 | } |
| 207 | } |
Constantin Ziesche | 02817f1 | 2020-08-04 21:40:43 +0200 | [diff] [blame] | 208 | public virtual void MapControllers(ControllerConfiguration controllerConfig) |
| 209 | { |
| 210 | this.ConfigureServices(services => |
| 211 | { |
| 212 | if (controllerConfig?.Controllers?.Count > 0) |
| 213 | { |
| 214 | var mvcBuilder = services.AddMvc(); |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 215 | foreach (var controllerAssemblyString in controllerConfig.Controllers) |
Constantin Ziesche | 02817f1 | 2020-08-04 21:40:43 +0200 | [diff] [blame] | 216 | { |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 217 | Assembly controllerAssembly = null; |
| 218 | try |
| 219 | { |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 220 | controllerAssembly = Assembly.Load(controllerAssemblyString); |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 221 | } |
| 222 | catch (Exception e) |
| 223 | { |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 224 | logger.Warn(e, $"Assembly {controllerAssemblyString} cannot be loaded - maybe it is not referenced. Try reading from file..."); |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 225 | try |
| 226 | { |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 227 | if (File.Exists(controllerAssemblyString)) |
| 228 | controllerAssembly = Assembly.LoadFile(controllerAssemblyString); |
| 229 | else if (File.Exists(controllerAssemblyString + ".dll")) |
| 230 | controllerAssembly = Assembly.LoadFile(controllerAssemblyString + ".dll"); |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 231 | else |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 232 | controllerAssembly = Assembly.LoadFrom(controllerAssemblyString); |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 233 | } |
| 234 | catch (Exception exp) |
| 235 | { |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 236 | logger.Warn(exp, $"Assembly {controllerAssemblyString} can finally not be loaded"); |
| 237 | } |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 238 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 239 | if (controllerAssembly != null) |
| 240 | { |
Constantin Ziesche | 181770b | 2020-08-20 20:32:39 +0200 | [diff] [blame] | 241 | mvcBuilder.AddApplicationPart(controllerAssembly); |
Constantin Ziesche | 0399d41 | 2020-09-24 14:31:15 +0200 | [diff] [blame] | 242 | string controllerAssemblyName = controllerAssembly.GetName().Name; |
| 243 | string xmlDocFile = $"{controllerAssemblyName}.xml"; |
| 244 | string xmlDocFilePath = Path.Combine(ExecutionPath, xmlDocFile); |
| 245 | |
| 246 | if (File.Exists(xmlDocFilePath)) |
| 247 | continue; |
| 248 | |
| 249 | try |
| 250 | { |
| 251 | ManifestEmbeddedFileProvider embeddedFileProvider = new ManifestEmbeddedFileProvider(controllerAssembly); |
| 252 | IFileInfo fileInfo = embeddedFileProvider.GetFileInfo(xmlDocFile); |
| 253 | if (fileInfo == null) |
| 254 | { |
| 255 | logger.Warn($"{xmlDocFile} of Assembly {controllerAssemblyName} not found"); |
| 256 | continue; |
| 257 | } |
| 258 | using (Stream stream = fileInfo.CreateReadStream()) |
| 259 | { |
| 260 | using (FileStream fileStream = File.OpenWrite(xmlDocFilePath)) |
| 261 | { |
| 262 | stream.CopyTo(fileStream); |
| 263 | } |
| 264 | } |
| 265 | logger.Info($"{xmlDocFile} of Assembly {controllerAssemblyName} has been created successfully"); |
| 266 | } |
| 267 | catch (Exception e) |
| 268 | { |
| 269 | logger.Warn(e, $"{xmlDocFile} of Assembly {controllerAssemblyName} cannot be read"); |
| 270 | } |
Constantin Ziesche | 0821550 | 2020-09-21 19:08:32 +0200 | [diff] [blame] | 271 | } |
Constantin Ziesche | 02817f1 | 2020-08-04 21:40:43 +0200 | [diff] [blame] | 272 | } |
| 273 | mvcBuilder.AddControllersAsServices(); |
| 274 | } |
| 275 | }); |
| 276 | } |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 277 | protected virtual void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 278 | { |
| 279 | services.AddSingleton(typeof(ServerSettings), Settings); |
| 280 | services.AddSingleton<IServerApplicationLifetime>(this); |
| 281 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 282 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 283 | var urls = Settings.ServerConfig.Hosting.Urls; |
| 284 | var secureUrl = urls.Find(s => s.StartsWith("https")); |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 285 | if (!string.IsNullOrEmpty(secureUrl) && !context.HostingEnvironment.IsDevelopment()) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 286 | { |
| 287 | secureUrl = secureUrl.Replace("+", "0.0.0.0"); |
| 288 | Uri secureUri = new Uri(secureUrl); |
| 289 | _secure = true; |
| 290 | services.AddHttpsRedirection(opts => |
| 291 | { |
| 292 | opts.HttpsPort = secureUri.Port; |
| 293 | }); |
| 294 | } |
| 295 | |
| 296 | services.AddStandardImplementation(); |
| 297 | |
| 298 | services.AddCors(); |
| 299 | services.AddMvc() |
| 300 | .AddApplicationPart(ControllerAssembly) |
| 301 | .AddControllersAsServices() |
| 302 | .AddNewtonsoftJson(options => options.GetDefaultMvcJsonOptions(services)); |
| 303 | |
| 304 | services.AddRazorPages(opts => |
| 305 | { |
| 306 | logger.Info("Pages-RootDirectory: " + opts.RootDirectory); |
| 307 | }); |
| 308 | |
| 309 | services.AddDirectoryBrowser(); |
| 310 | |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 311 | services.PostConfigure<ApiBehaviorOptions>(options => |
| 312 | { |
| 313 | options.InvalidModelStateResponseFactory = actionContext => |
| 314 | { |
| 315 | ValidationProblemDetails error = actionContext.ModelState |
| 316 | .Where(e => e.Value.Errors.Count > 0) |
| 317 | .Select(e => new ValidationProblemDetails(actionContext.ModelState)).FirstOrDefault(); |
| 318 | |
| 319 | Result result = new Result(false, |
| 320 | new Message( |
| 321 | MessageType.Error, |
| 322 | $"Path '{actionContext.HttpContext.Request.Path.Value}' received invalid or malformed request: {error.Errors.Values}", |
| 323 | actionContext.HttpContext.Response.StatusCode.ToString())); |
| 324 | |
| 325 | return new BadRequestObjectResult(result); |
| 326 | }; |
| 327 | }); |
| 328 | |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 329 | foreach (var serviceBuider in ServiceBuilderPipeline) |
| 330 | { |
| 331 | serviceBuider.Invoke(services); |
| 332 | } |
| 333 | } |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 334 | protected virtual void Configure(IApplicationBuilder app) |
| 335 | { |
| 336 | var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>(); |
| 337 | var applicationLifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>(); |
| 338 | |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 339 | if (env.IsProduction()) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 340 | app.UseHsts(); |
Constantin Ziesche | 220223d | 2020-12-03 11:27:54 +0100 | [diff] [blame] | 341 | |
| 342 | app.UseExceptionHandler(ERROR_PATH); |
| 343 | |
| 344 | app.UseStatusCodePages(async context => |
| 345 | { |
| 346 | context.HttpContext.Response.ContentType = "application/json"; |
| 347 | |
| 348 | JsonSerializerSettings settings; |
| 349 | var options = context.HttpContext.RequestServices.GetService<MvcNewtonsoftJsonOptions>(); |
| 350 | if (options == null) |
| 351 | settings = new DefaultJsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; |
| 352 | else |
| 353 | settings = options.SerializerSettings; |
| 354 | |
| 355 | Result result = new Result(false, |
| 356 | new Message(MessageType.Error, "Path: " + context.HttpContext.Request.Path.Value, |
| 357 | context.HttpContext.Response.StatusCode.ToString())); |
| 358 | |
| 359 | string resultMessage = JsonConvert.SerializeObject(result, settings); |
| 360 | |
| 361 | await context.HttpContext.Response.WriteAsync(resultMessage); |
| 362 | }); |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 363 | |
Constantin Ziesche | 09dcb6b | 2020-10-07 13:47:39 +0200 | [diff] [blame] | 364 | if(_secure && !env.IsDevelopment()) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 365 | app.UseHttpsRedirection(); |
| 366 | |
| 367 | app.UseStaticFiles(); |
| 368 | |
| 369 | string path = env.ContentRootPath; |
| 370 | if (Directory.Exists(path)) |
| 371 | { |
| 372 | app.UseStaticFiles(new StaticFileOptions() |
| 373 | { |
| 374 | FileProvider = new PhysicalFileProvider(@path), |
Constantin Ziesche | b10014d | 2021-01-24 11:57:29 +0100 | [diff] [blame^] | 375 | RequestPath = new PathString(FILES_PATH), |
| 376 | ServeUnknownFileTypes = true |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 377 | }); |
| 378 | |
| 379 | app.UseDirectoryBrowser(new DirectoryBrowserOptions |
| 380 | { |
| 381 | FileProvider = new PhysicalFileProvider(@path), |
Constantin Ziesche | b10014d | 2021-01-24 11:57:29 +0100 | [diff] [blame^] | 382 | RequestPath = new PathString(FILES_PATH) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 383 | }); |
| 384 | } |
| 385 | |
| 386 | app.Use((context, next) => |
| 387 | { |
Constantin Ziesche | 7797750 | 2020-12-04 14:05:52 +0100 | [diff] [blame] | 388 | var url = context.GetServerVariable("UNENCODED_URL"); |
| 389 | if (!string.IsNullOrEmpty(url)) |
| 390 | context.Request.Path = new PathString(url); |
| 391 | |
| 392 | return next(); |
| 393 | }); |
| 394 | |
| 395 | app.Use((context, next) => |
| 396 | { |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 397 | string requestPath = context.Request.Path.ToUriComponent(); |
| 398 | |
| 399 | if (requestPath.Contains("submodelElements/")) |
| 400 | { |
| 401 | Match valueMatch = Regex.Match(requestPath, "(?<=submodelElements/)(.*)(?=/value|/invoke|/invocationList)"); |
Constantin Ziesche | eb74d64 | 2020-11-04 17:57:12 +0100 | [diff] [blame] | 402 | if(valueMatch.Success && !string.IsNullOrEmpty(valueMatch.Value)) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 403 | { |
| 404 | string elementPath = HttpUtility.UrlEncode(valueMatch.Value); |
| 405 | requestPath = requestPath.Replace(valueMatch.Value, elementPath); |
| 406 | context.Request.Path = new PathString(requestPath); |
| 407 | } |
| 408 | else |
| 409 | { |
| 410 | Match baseMatch = Regex.Match(requestPath, "(?<=submodelElements/)(.*)"); |
Constantin Ziesche | eb74d64 | 2020-11-04 17:57:12 +0100 | [diff] [blame] | 411 | if(baseMatch.Success && !string.IsNullOrEmpty(baseMatch.Value)) |
Constantin Ziesche | 687f888 | 2020-10-02 16:17:44 +0200 | [diff] [blame] | 412 | { |
| 413 | string elementPath = HttpUtility.UrlEncode(baseMatch.Value); |
| 414 | requestPath = requestPath.Replace(baseMatch.Value, elementPath); |
| 415 | context.Request.Path = new PathString(requestPath); |
| 416 | } |
| 417 | } |
| 418 | } |
| 419 | return next(); |
| 420 | }); |
| 421 | |
| 422 | app.UseRouting(); |
| 423 | |
| 424 | app.UseCors( |
| 425 | options => options |
| 426 | .AllowAnyHeader() |
| 427 | .AllowAnyMethod() |
| 428 | .AllowAnyOrigin() |
| 429 | ); |
| 430 | app.UseAuthorization(); |
| 431 | |
| 432 | app.UseEndpoints(endpoints => |
| 433 | { |
| 434 | endpoints.MapRazorPages(); |
| 435 | endpoints.MapControllers(); |
| 436 | }); |
| 437 | |
| 438 | var options = new RewriteOptions().AddRedirect("^$", UI_RELATIVE_PATH); |
| 439 | app.UseRewriter(options); |
| 440 | |
| 441 | if (ApplicationStarted != null) |
| 442 | applicationLifetime.ApplicationStarted.Register(ApplicationStarted); |
| 443 | if (ApplicationStopping != null) |
| 444 | applicationLifetime.ApplicationStopping.Register(ApplicationStopping); |
| 445 | if (ApplicationStopped != null) |
| 446 | applicationLifetime.ApplicationStopped.Register(ApplicationStopped); |
| 447 | |
| 448 | foreach (var appBuilder in AppBuilderPipeline) |
| 449 | { |
| 450 | appBuilder.Invoke(app); |
| 451 | } |
| 452 | } |
Constantin Ziesche | fa61208 | 2020-04-03 09:54:56 +0200 | [diff] [blame] | 453 | } |
| 454 | } |