Dreamine.Hybrid.Wpf 1.0.3
`AddDreamineHybridWpf()` 내부에서 `AddWpfBlazorWebView()` 호출 및
로딩중...
검색중...
일치하는것 없음
DreamineBlazorServerHostedService.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.Hybrid.Interfaces;
2using Microsoft.AspNetCore.Builder;
3using Microsoft.AspNetCore.Components;
4using Microsoft.AspNetCore.Hosting;
5using Microsoft.AspNetCore.Hosting.StaticWebAssets;
6using Microsoft.AspNetCore.Http;
7using Microsoft.Extensions.DependencyInjection;
8using Microsoft.Extensions.Hosting;
9using System;
10using System.IO;
11using System.Linq;
12using System.Net;
13using System.Reflection;
14using System.Threading;
15using System.Threading.Tasks;
16
18{
35 public sealed class DreamineBlazorServerHostedService<TRootComponent> : IHostedService
36 where TRootComponent : IComponent
37 {
46 private readonly IServiceProvider _rootServiceProvider;
55 private readonly IHybridMessageBus _messageBus;
73 private IHost? _webHost;
74
116 IServiceProvider rootServiceProvider,
117 IHybridMessageBus messageBus,
119 {
120 _rootServiceProvider = rootServiceProvider ?? throw new ArgumentNullException(nameof(rootServiceProvider));
121 _messageBus = messageBus ?? throw new ArgumentNullException(nameof(messageBus));
122 _options = options ?? throw new ArgumentNullException(nameof(options));
123 }
124
149 public async Task StartAsync(CancellationToken cancellationToken)
150 {
151 Assembly blazorAssembly = typeof(TRootComponent).Assembly;
152
153 WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions
154 {
155 Args = Array.Empty<string>(),
156 ContentRootPath = _options.ContentRootPath,
157 ApplicationName = blazorAssembly.GetName().Name
158 });
159
160 StaticWebAssetsLoader.UseStaticWebAssets(builder.Environment, builder.Configuration);
161
162 builder.Services
163 .AddRazorComponents()
164 .AddInteractiveServerComponents();
165
166 builder.Services.AddSingleton(_messageBus);
167
168 RegisterSharedServices(builder.Services);
169 _options.ConfigureServices?.Invoke(builder.Services);
170
171 if (_options.AutoRegisterViewModels)
172 {
173 RegisterViewModels(builder.Services, blazorAssembly);
174 }
175
176 builder.WebHost.ConfigureKestrel(options =>
177 {
178 if (_options.ListenAnyIp)
179 {
180 options.ListenAnyIP(_options.Port);
181 return;
182 }
183
184 if (string.Equals(_options.Host, "localhost", StringComparison.OrdinalIgnoreCase))
185 {
186 options.ListenLocalhost(_options.Port);
187 return;
188 }
189
190 if (IPAddress.TryParse(_options.Host, out IPAddress? address))
191 {
192 options.Listen(address, _options.Port);
193 return;
194 }
195
196 options.ListenLocalhost(_options.Port);
197 });
198
199 WebApplication app = builder.Build();
200
201 app.UseStaticFiles();
202 app.Use(async (context, next) =>
203 {
204 if (TryGetPublishedIndexPath(app.Environment.ContentRootPath, context.Request, out string? indexPath))
205 {
206 context.Response.ContentType = "text/html; charset=utf-8";
207 await context.Response.SendFileAsync(indexPath!);
208 return;
209 }
210
211 await next();
212 });
213
214 // 추가 정적 파일(업로드 데이터 등)을 UseRouting 이전에 등록해야
215 // Blazor catch-all 라우터보다 먼저 처리됩니다.
216 _options.ConfigurePipeline?.Invoke(app);
217
218 app.UseRouting();
219
220 _options.ConfigurePipelineAfterRouting?.Invoke(app);
221
222 app.UseAntiforgery();
223
224 app.MapGet("/_dreamine/instance", () => _options.InstanceId);
225
226 app.MapRazorComponents<TRootComponent>()
227 .AddInteractiveServerRenderMode();
228
229 _webHost = app;
230
231 await app.StartAsync(cancellationToken);
232 }
233
258 public async Task StopAsync(CancellationToken cancellationToken)
259 {
260 if (_webHost is null)
261 {
262 return;
263 }
264
265 try
266 {
267 await _webHost.StopAsync(cancellationToken);
268 }
269 finally
270 {
271 _webHost.Dispose();
272 _webHost = null;
273 }
274 }
275
300 private void RegisterSharedServices(IServiceCollection services)
301 {
302 foreach (Type serviceType in _options.SharedServiceTypes)
303 {
304 object? instance = _rootServiceProvider.GetService(serviceType);
305
306 if (instance is null)
307 {
308 throw new InvalidOperationException(
309 $"Shared service '{serviceType.FullName}' was not found in the root service provider.");
310 }
311
312 if (!_options.AllowDisposableSharedServices &&
313 (instance is IDisposable || instance is IAsyncDisposable))
314 {
315 throw new InvalidOperationException(
316 $"Shared service '{serviceType.FullName}' implements IDisposable/IAsyncDisposable. " +
317 "Blazor Server would own disposal for services registered into its container. " +
318 "Share a non-disposable facade or explicitly enable AllowDisposableSharedServices when that ownership is intended.");
319 }
320
321 services.AddSingleton(serviceType, instance);
322 }
323 }
324
349 private static void RegisterViewModels(IServiceCollection services, Assembly assembly)
350 {
351 foreach (Type type in assembly.GetTypes()
352 .Where(type =>
353 type.IsClass &&
354 !type.IsAbstract &&
355 type.IsPublic &&
356 type.Name.EndsWith("ViewModel", StringComparison.Ordinal)))
357 {
358 services.AddScoped(type);
359 }
360 }
361
402 private static bool TryGetPublishedIndexPath(string contentRootPath, HttpRequest request, out string? indexPath)
403 {
404 indexPath = null;
405
406 if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
407 {
408 return false;
409 }
410
411 string? requestPath = request.Path.Value;
412 if (string.IsNullOrWhiteSpace(requestPath) ||
413 string.Equals(requestPath, "/", StringComparison.Ordinal) ||
414 !requestPath.EndsWith("/", StringComparison.Ordinal))
415 {
416 return false;
417 }
418
419 string[] segments = requestPath
420 .Trim('/')
421 .Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
422
423 if (segments.Length == 0 ||
424 segments.Any(segment => segment is "." or ".." || Path.GetFileName(segment) != segment))
425 {
426 return false;
427 }
428
429 string webRoot = Path.GetFullPath(Path.Combine(contentRootPath, "wwwroot"));
430 string targetDirectory = webRoot;
431 foreach (string segment in segments)
432 {
433 targetDirectory = Path.Combine(targetDirectory, segment);
434 }
435
436 string candidate = Path.GetFullPath(Path.Combine(targetDirectory, "index.html"));
437 string webRootPrefix = webRoot.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)
438 ? webRoot
439 : webRoot + Path.DirectorySeparatorChar;
440
441 if (!candidate.StartsWith(webRootPrefix, StringComparison.OrdinalIgnoreCase) || !File.Exists(candidate))
442 {
443 return false;
444 }
445
446 indexPath = candidate;
447 return true;
448 }
449 }
450}
static void RegisterViewModels(IServiceCollection services, Assembly assembly)
static bool TryGetPublishedIndexPath(string contentRootPath, HttpRequest request, out string? indexPath)
DreamineBlazorServerHostedService(IServiceProvider rootServiceProvider, IHybridMessageBus messageBus, DreamineBlazorServerHostOptions options)