/// <summary> /// Handle custom resource request and forward it to the appropriate frame. /// </summary> /// <param name="url"></param> /// <returns></returns> private void HandleCustomResourceRequested(ResourceHandler resourceHandler) { var url = resourceHandler.Url; if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && uri.Segments.Length > 1 && uri.Host.Equals(CustomResourceBaseUrl, StringComparison.InvariantCultureIgnoreCase)) { var frameName = uri.Segments.ElementAt(1).TrimEnd(ResourceUrl.PathSeparator.ToCharArray()); if (frameName != null && Frames.TryGetValue(frameName, out var frame)) { var customResourceRequestedHandlers = GetCustomResourceHandlers(frame); if (customResourceRequestedHandlers.Any()) { resourceHandler.BeginAsyncResponse(() => { // get resource key from the query params var resourceKeyAndOptions = uri.Query.TrimStart('?').Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries).Select(p => Uri.UnescapeDataString(p)); var resourceKey = resourceKeyAndOptions.FirstOrDefault(); var options = resourceKeyAndOptions.Skip(1).ToArray(); // get response from first handler that returns a stream var response = customResourceRequestedHandlers.Select(h => h(resourceKey, options)).FirstOrDefault(r => r?.Content != null); if (response != null) { var extension = (response.Extension ?? Path.GetExtension(resourceKey)).TrimStart('.'); resourceHandler.RespondWith(response.Content, extension); } else { resourceHandler.RespondWith(MemoryStream.Null); } }); } } } }