CefSharp 自定义 SchemeHandler
CefSharp custom SchemeHandler
我正在使用 CefSharp 的 SchemeHandler,以便使用自定义 url 从我的 C# 项目中获取资源,例如 .css
、.js
或 .png
文件 custom://cefsharp/assets/css/style.css
我有 2 个自定义 classes 来存档它。
第一个 class,MyCustomSchemeHandlerFactory
将是处理自定义方案的那个,它看起来像这样,其中 "custom" 将是自定义方案:
internal class MyCustomSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new MyCustomSchemeHandler();
}
}
我实现的下一个 class 是 MyCustomSchemeHandler
,它将接收调用并输出响应,它看起来像这样:
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()
{
}
}
在这个 class 中,我定义了一个自定义资源字典,它将指示将使用资源中的哪个文件,因此正如我在第一个示例中所述,custom://cefsharp/assets/css/style.css
应该加载资源Properties.Resources.style
,问题是一旦我进入特定的 url,就没有加载任何东西,我尝试输出 mimeType 并且它有效,但不知何故文件本身无法正确输出。我的实现有问题吗?
此外,我尝试以以下形式输出原始文件:
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
MessageBox.Show(resource);
}
它输出正确的文件没有任何问题。
为了加载自定义方案,我在初始化 CefSharp 之前使用了以下代码:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = MyCustomSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new MyCustomSchemeHandlerFactory()
});
以上 classes 基于以下链接:
我的自定义方案处理程序工厂:FlashResourceHandlerFactory.cs
我的自定义方案处理程序:CefSharpSchemeHandler.cs and ResourceHandler.cs
如果你只是需要return一个字符串,那么你可以使用ResourceHandler.FromString(html, mimeType)
。为此,您只需要实施 ISchemeHandlerFactory
.
https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp/ResourceHandler.cs#L98
从文件中读取的示例 https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs#L17 可以非常简单地转换为从字符串中读取。
由于 Cefsharp 在过去几个月里发生了一些变化,这里是一种更新且更简单的处理 'file' 协议的方法。我就这件事写了blog post。
您要添加的是您的方案处理程序及其工厂:
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();
}
}
}
然后注册再调用Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});
我正在使用 CefSharp 的 SchemeHandler,以便使用自定义 url 从我的 C# 项目中获取资源,例如 .css
、.js
或 .png
文件 custom://cefsharp/assets/css/style.css
我有 2 个自定义 classes 来存档它。
第一个 class,MyCustomSchemeHandlerFactory
将是处理自定义方案的那个,它看起来像这样,其中 "custom" 将是自定义方案:
internal class MyCustomSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new MyCustomSchemeHandler();
}
}
我实现的下一个 class 是 MyCustomSchemeHandler
,它将接收调用并输出响应,它看起来像这样:
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()
{
}
}
在这个 class 中,我定义了一个自定义资源字典,它将指示将使用资源中的哪个文件,因此正如我在第一个示例中所述,custom://cefsharp/assets/css/style.css
应该加载资源Properties.Resources.style
,问题是一旦我进入特定的 url,就没有加载任何东西,我尝试输出 mimeType 并且它有效,但不知何故文件本身无法正确输出。我的实现有问题吗?
此外,我尝试以以下形式输出原始文件:
if (ResourceDictionary.TryGetValue(fileName, out resource) && !string.IsNullOrEmpty(resource))
{
MessageBox.Show(resource);
}
它输出正确的文件没有任何问题。
为了加载自定义方案,我在初始化 CefSharp 之前使用了以下代码:
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = MyCustomSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new MyCustomSchemeHandlerFactory()
});
以上 classes 基于以下链接: 我的自定义方案处理程序工厂:FlashResourceHandlerFactory.cs 我的自定义方案处理程序:CefSharpSchemeHandler.cs and ResourceHandler.cs
如果你只是需要return一个字符串,那么你可以使用ResourceHandler.FromString(html, mimeType)
。为此,您只需要实施 ISchemeHandlerFactory
.
https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp/ResourceHandler.cs#L98
从文件中读取的示例 https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs#L17 可以非常简单地转换为从字符串中读取。
由于 Cefsharp 在过去几个月里发生了一些变化,这里是一种更新且更简单的处理 'file' 协议的方法。我就这件事写了blog post。
您要添加的是您的方案处理程序及其工厂:
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();
}
}
}
然后注册再调用Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});