CefSharp custom SchemeHandler

天涯浪子 提交于 2019-11-29 10:47:41

问题


Iam using CefSharp's SchemeHandler in order to grab resources from my C# project like .css, .js or .png files using a custom url for example custom://cefsharp/assets/css/style.css

I've 2 custom classes in order to archive this.

First class, MyCustomSchemeHandlerFactory will be the one that handles the custom Scheme and it looks like this, where "custom" will be the custom scheme:

internal class MyCustomSchemeHandlerFactory : ISchemeHandlerFactory
{
    public const string SchemeName = "custom";

    public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
    {
        return new MyCustomSchemeHandler();
    }
}

The next class I've implemented is MyCustomSchemeHandler which will receive the call and output a response and it looks like this:

internal class MyCustomSchemeHandler : IResourceHandler
{

    private static readonly IDictionary<string, string> ResourceDictionary;

    private string mimeType;
    private MemoryStream stream;

    static MyCustomSchemeHandler()
    {
        ResourceDictionary = new Dictionary<string, string>
        {
            { "/home.html", Properties.Resources.index},
            { "/assets/css/style.css", Properties.Resources.style}
        };
    }

    public Stream Stream { get; set; }
    public int StatusCode { get; set; }
    public string StatusText { get; set; }
    public string MimeType { get; set; }
    public NameValueCollection Headers { get; private set; }

    public Stream GetResponse(IResponse response, out long responseLength, out string redirectUrl)
    {
        redirectUrl = null;
        responseLength = -1;

        response.MimeType = MimeType;
        response.StatusCode = StatusCode;
        response.StatusText = StatusText;
        response.ResponseHeaders = Headers;

        var memoryStream = Stream as MemoryStream;
        if (memoryStream != null)
        {
            responseLength = memoryStream.Length;
        }

        return Stream;
    }

    public bool ProcessRequestAsync(IRequest request, ICallback callback)
    {
        // The 'host' portion is entirely ignored by this scheme handler.
        var uri = new Uri(request.Url);
        var fileName = uri.AbsolutePath;
        string resource;

        if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
        {
            var resourceHandler = ResourceHandler.FromString(resource);
            stream = (MemoryStream)resourceHandler.Stream;

            var fileExtension = Path.GetExtension(fileName);
            mimeType = ResourceHandler.GetMimeType(fileExtension);

            callback.Continue();
            return true;
        }
        else
        {
            callback.Dispose();
        }

        return false;
    }

    void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
    {
        responseLength = stream == null ? 0 : stream.Length;
        redirectUrl = null;

        response.StatusCode = (int)HttpStatusCode.OK;
        response.StatusText = "OK";
        response.MimeType = mimeType;
    }

    bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
    {
        //Dispose the callback as it's an unmanaged resource, we don't need it in this case
        callback.Dispose();

        if (stream == null)
        {
            bytesRead = 0;
            return false;
        }

        //Data out represents an underlying buffer (typically 32kb in size).
        var buffer = new byte[dataOut.Length];
        bytesRead = stream.Read(buffer, 0, buffer.Length);

        dataOut.Write(buffer, 0, buffer.Length);

        return bytesRead > 0;
    }

    bool CanGetCookie(Cookie cookie)
    {
        return true;
    }

    bool CanSetCookie(Cookie cookie)
    {
        return true;
    }

    void Cancel()
    {

    }

}

Inside this class I've defined a custom resource dictionary which will dictate what file from the resources will be used, so as I stated in the first example, custom://cefsharp/assets/css/style.css should load the resource Properties.Resources.style, the problem is that nothing gets loaded once I enter to the specific url, I've tried to output the mimeType and It works but somehow the file itself won't output correctly. Is there something wrong with my implementation?

Additionaly I've tried to output the raw file in the form of:

if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
    MessageBox.Show(resource);
}

And it outputs the correct file without any problems.

To load the custom Scheme I use the following code before initializing CefSharp:

var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
    SchemeName = MyCustomSchemeHandlerFactory.SchemeName,
    SchemeHandlerFactory = new MyCustomSchemeHandlerFactory()
});

The above classes were based on the following links: MyCustomSchemeHandlerFactory: FlashResourceHandlerFactory.cs MyCustomSchemeHandler: CefSharpSchemeHandler.cs and ResourceHandler.cs


回答1:


If you simply need to return a string, then you can use ResourceHandler.FromString(html, mimeType). For this you just need to implement the ISchemeHandlerFactory.

https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp/ResourceHandler.cs#L98

Example reading from a file https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs#L17 which can be translated to reading from a string quite simply.




回答2:


Since Cefsharp changed a bit in last few months here is an updated and easier way of handling 'file' protocol. I wrote blog post on this matter.

What you want to add is your scheme handler and its factory:

using System;
using System.IO;
using CefSharp;

namespace MyProject.CustomProtocol
{
    public class CustomProtocolSchemeHandler : ResourceHandler
    {
        // Specifies where you bundled app resides.
        // Basically path to your index.html
        private string frontendFolderPath;

        public CustomProtocolSchemeHandler()
        {
            frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
        }

        // Process request and craft response.
        public override bool ProcessRequestAsync(IRequest request, ICallback callback)
        {
            var uri = new Uri(request.Url);
            var fileName = uri.AbsolutePath;

            var requestedFilePath = frontendFolderPath + fileName;

            if (File.Exists(requestedFilePath))
            {
                byte[] bytes = File.ReadAllBytes(requestedFilePath);
                Stream = new MemoryStream(bytes);

                var fileExtension = Path.GetExtension(fileName);
                MimeType = GetMimeType(fileExtension);

                callback.Continue();
                return true;
            }

            callback.Dispose();
            return false;
        }
    }

    public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
    {
        public const string SchemeName = "customFileProtocol";

        public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
        {
            return new CustomProtocolSchemeHandler();
        }
    }
}

And then register it before calling Cef.Initialize:

var settings = new CefSettings
{
  BrowserSubprocessPath = GetCefExecutablePath()
};

settings.RegisterScheme(new CefCustomScheme
{
  SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
  SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});


来源:https://stackoverflow.com/questions/35965912/cefsharp-custom-schemehandler

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!