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
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()
});
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.
Related
I am trying to open pdf in the cefsharp browser itself. But nothing is getting displayed when I open the pdf. I have set the logs to error level but there are no errors in it.
This is how I am registering the CefCustomScheme :
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CefSharpSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CefSharpSchemeHandlerFactory(),
IsCSPBypassing = true,
});
This is my CefSharpSchemeHandlerFactory :
public class CefSharpSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "local";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
if (schemeName == SchemeName && request.Url.EndsWith("CefSharp.Core.xml", System.StringComparison.OrdinalIgnoreCase))
{
//Convenient helper method to lookup the mimeType
var mimeType = ResourceHandler.GetMimeType(".xml");
//Load a resource handler for CefSharp.Core.xml
//mimeType is optional and will default to text/html
return ResourceHandler.FromFilePath("CefSharp.Core.xml", mimeType);
}
return new CefSharpSchemeHandler();
}
}
This is my CefSharpSchemeHandler :
internal class CefSharpSchemeHandler : IResourceHandler
{
private static readonly IDictionary<string, string> ResourceDictionary;
private string mimeType;
private MemoryStream stream;
static CefSharpSchemeHandler()
{
}
bool IResourceHandler.ProcessRequest(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))
{
Task.Run(() =>
{
using (callback)
{
var bytes = Encoding.UTF8.GetBytes(resource);
stream = new MemoryStream(bytes);
var fileExtension = Path.GetExtension(fileName);
mimeType = ResourceHandler.GetMimeType(fileExtension);
callback.Continue();
}
});
return true;
}
else
{
callback.Dispose();
}
return false;
}
void IResourceHandler.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 IResourceHandler.ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
{
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 IResourceHandler.CanGetCookie(Cookie cookie)
{
return true;
}
bool IResourceHandler.CanSetCookie(Cookie cookie)
{
return true;
}
void IResourceHandler.Cancel()
{
}
void IDisposable.Dispose()
{
}
}
And this how I am opening My pdf file from some other class :
string pdfPath = #"D:\Magic\10774.pdf";
var mimeType = ResourceHandler.GetMimeType(".pdf");
ResourceHandler.FromFilePath(pdfPath, mimeType);
I am not getting a hit at CefSharpSchemeHandlerFactory create method.
I'm uploading excel file from android to C# the request from android is arriving to c# server(I don't want to use asp.net).
I've built from scratch a simple HTTPServe, I do't how to handle multipart data upload in c# that's my code :
Request.cs:
class Request
{
public String type { get; set; }
public String url { get; set; }
public String host { get; set; }
private Request(String type,String url,String host)
{
this.type = type;
this.url = url;
this.host = host;
}
public static Request GetRequest(String request)
{
if (String.IsNullOrEmpty(request))
return null;
String[] tokens = request.Split(' ');
String type = tokens[0];
String url = tokens[1];
String host = tokens[4];
return new Request(type, url, host);
}
}
Response.cs:
class Response
{
private Byte[] data = null;
private String status;
private String mime;
private Response(String status,String mime,Byte[] data)
{
this.status = status;
this.data = data;
this.mime = mime;
}
public static Response From(Request request)
{
Console.WriteLine(request.type);
if (request == null)
return MakeNullRequest();
if (request.type.Equals("POST"))
{
return UploadCompleteResponse(request);
}
return MakeFromExcel();
}
private static Response UploadCompleteResponse(Request request)
{ //Handling the multipart request here but I don't know how that is a simple example
Console.WriteLine("uploaded!!");
byte[] bytes = Encoding.ASCII.GetBytes("uploaded!!");
return new Response("ok 200","text/plain",bytes);
}
//private static Response MakeFromFile(FileInfo f)
//{
// // to do later
// //return new Response("200 ok", "text/html", d);
//}
//Retruning an excel file response
private static Response MakeFromExcel()
{
String file = Environment.CurrentDirectory + HTTPServer.EXCEL_DIR+"brains.xlsx";
Console.WriteLine(file);
FileInfo info = new FileInfo(file);
if (info.Exists)
{
FileStream fs = info.OpenRead();
BinaryReader read = new BinaryReader(fs);
Byte[] d = new Byte[fs.Length];
read.Read(d, 0, d.Length);
fs.Close();
return new Response("200 ok", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", d);
}
return PageNotFound();
}
private static Response MakeNullRequest()
{
String file = Environment.CurrentDirectory+HTTPServer.MSG_DIR +"400.html";
Console.WriteLine(file);
FileInfo info = new FileInfo(file);
FileStream fs=info.OpenRead();
BinaryReader read=new BinaryReader(fs);
Byte[] d = new Byte[fs.Length];
read.Read(d, 0, d.Length);
fs.Close();
return new Response("400 Bad Request", "text/html", d);
}
private static Response PageNotFound()
{
String file = Environment.CurrentDirectory + HTTPServer.MSG_DIR + "400.html";
FileInfo info = new FileInfo(file);
FileStream fs = info.OpenRead();
BinaryReader read = new BinaryReader(fs);
Byte[] d = new Byte[fs.Length];
read.Read(d, 0, d.Length);
return new Response("404 Not Found", "text/html", d);
}
public void Post(NetworkStream stream)
{
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine(String.Format("{0} {1}\r\nServer: {2}\r\nContent-Type: {3}\r\nAccept-Ranges: 255 bytes\r\nContent-Length: {4}\r\n", HTTPServer.VERSION
, status, HTTPServer.NAME, mime, data.Length));
writer.Flush();
stream.Write(data, 0, data.Length);
stream.Flush();
stream.Close();
}
}
In the above code I used more than one type of response You can access and the request fields by increasing the size of the token array.
HTTServer.cs:
class HTTPServer
{
public const String MSG_DIR = "\\root\\msg\\";
public const String EXCEL_DIR = "\\root\\excel\\";
public const String WEB_DIR = "\\root\\web\\";
public const String VERSION = "HTTP/1.1";
public const String NAME = "Thecode007 HTTP SERVER v 1.0";
private bool running = false;
private TcpListener listener;
public HTTPServer(int port)
{
listener = new TcpListener(IPAddress.Any,port);
}
public void Start() {
Thread serverThread = new Thread(new ThreadStart(Run));
serverThread.Start();
}
private void Run()
{
running = true;
listener.Start();
while (running)
{
Console.WriteLine("Waiting for connection...");
TcpClient client = listener.AcceptTcpClient();
HandleClient(client);
Console.WriteLine("Client connected!");
}
running = false;
listener.Stop();
}
private void HandleClient(TcpClient client)
{
StreamReader reader = new StreamReader(client.GetStream());
String msg = "";
while (reader.Peek() != -1)
{
msg += reader.ReadLine()+"\n";
}
Console.WriteLine(msg);
Request req = Request.GetRequest(msg);
Response resp = Response.From(req);
resp.Post(client.GetStream());
}
}
And here is the main method:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting Serving on port 9000");
HTTPServer server = new HTTPServer(9000);
server.Start();
Console.ReadLine();
}
}
Disclaimer: You should never write a HTTP server directly with TcpClient for any production work. There are far too many specifications to be accounted for and C# has these built in to any Asp.Net web server you use.
That being said, for an exercise this is a matter of parsing the request in a specific way. You should be able to reference these questions to learn more about the details of multipart boundary:
What is http multipart request?
What should a Multipart HTTP request with multiple files look like?
Essentially, you will have to read a header from the http headers (Content-Type), and parse the value of boundary= to determine what is separating the key/value pairs of the multipart form in the request body. Once you've got the boundary you will have to parse the body of the request into the real key/value pairs of the form. From there, the contents of the uploaded file will be the value of whatever key the form submits it as.
Read more on the official W3 spec
Response.cs:
class Response
{
private Byte[] data = null;
private String status;
private String mime;
private Response(String status,String mime,Byte[] data)
{
this.status = status;
this.data = data;
this.mime = mime;
}
public static Response From(Request request)
{
if (request == null)
return MakeNullRequest();
if (request.type.Equals("POST"))
{
return UploadCompleteResponse(request);
}
return MakeFromExcel();
}
private static Response UploadCompleteResponse(Request request)
{ //I added on the rquest array the datapart which contains data
String data=request.data;
byte[] bytes = Encoding.ASCII.GetBytes("uploaded!!");
return new Response("ok 200","text/plain",bytes);
}
//private static Response MakeFromFile(FileInfo f)
//{
// // to do later
// //return new Response("200 ok", "text/html", d);
//}
//Retruning an excel file response
private static Response MakeFromExcel()
{
String file = Environment.CurrentDirectory + HTTPServer.EXCEL_DIR+"brains.xlsx";
Console.WriteLine(file);
FileInfo info = new FileInfo(file);
if (info.Exists)
{
FileStream fs = info.OpenRead();
BinaryReader read = new BinaryReader(fs);
Byte[] d = new Byte[fs.Length];
read.Read(d, 0, d.Length);
fs.Close();
return new Response("200 ok", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", d);
}
return PageNotFound();
}
private static Response MakeNullRequest()
{
String file = Environment.CurrentDirectory+HTTPServer.MSG_DIR +"400.html";
Console.WriteLine(file);
FileInfo info = new FileInfo(file);
FileStream fs=info.OpenRead();
BinaryReader read=new BinaryReader(fs);
Byte[] d = new Byte[fs.Length];
read.Read(d, 0, d.Length);
fs.Close();
return new Response("400 Bad Request", "text/html", d);
}
private static Response PageNotFound()
{
String file = Environment.CurrentDirectory + HTTPServer.MSG_DIR + "400.html";
FileInfo info = new FileInfo(file);
FileStream fs = info.OpenRead();
BinaryReader read = new BinaryReader(fs);
Byte[] d = new Byte[fs.Length];
read.Read(d, 0, d.Length);
return new Response("404 Not Found", "text/html", d);
}
public void Post(NetworkStream stream)
{
try
{
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine(String.Format("{0} {1}\r\nServer: {2}\r\nContent-Type: {3}\r\nAccept-Ranges: 255 bytes\r\nContent-Length: {4}\r\n", HTTPServer.VERSION
, status, HTTPServer.NAME, mime, data.Length));
writer.Flush();
stream.Write(data, 0, data.Length);
stream.Flush();
stream.Close();
}catch(Exception ex){
}
}
}
Request.cs:
class Request
{
public String type { get; set; }
public String url { get; set; }
public String host { get; set; }
public String data { get; set; }
private Request(String type,String url,String host)
{
this.type = type;
this.url = url;
this.host = host;
}
private Request(String type, String url, String host,String data)
{
this.type = type;
this.url = url;
this.host = host;
this.data = data;
}
public static Request GetRequest(String request)
{
if (String.IsNullOrEmpty(request))
return null;
String[] tokens = request.Split(' ');
String type = tokens[0];
String url = tokens[1];
String host = tokens[4];
String data = "N/A";
if (tokens.Length >= 9)
{
data = tokens[8];
}
return new Request(type, url, host, data);
}
}
Secondly my android client was excpecting a response which is responsbody type not a plain text thats why the data was never sent....
Using c# with the .net framework and microsoft azure and i am trying to upload a file in a cli which should be picked up by a webjob. I'm sure the webjob is fine but i am having problems getting upload to work.
// Pick URL location of service up from metadata
AudioSamples client = new AudioSamples(new AnonymousCredential());
var id = Console.ReadLine();
Console.WriteLine();
string path = "api/samples/" + id;
Console.WriteLine("Enter the file name.");
string fileName = Console.ReadLine();
using (var stream = File.OpenRead(fileName))
{
HttpOperationResponse response = await client.PutAsync(path, new StreamContent(stream));
}
From what i understand the PutAsync should work for streaming the file but it gives me an error saying the command doesn't exist
this api should also be used with the upload to a blob but im not sure how it connect it to the client.
namespace AudioSamples.Controllers
{
[ApiExplorerSettings(IgnoreApi = true)]
public class DataController : ApiController
{
private const String partitionName = "AudioSamples_Partition_1";
private CloudStorageAccount storageAccount;
private CloudTableClient tableClient;
private CloudTable table;
private BlobStorageService _blobStorageService = new BlobStorageService();
private CloudQueueService _queueStorageService = new CloudQueueService();
String name;
public DataController()
{
storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["AzureWebJobsStorage"].ToString());
tableClient = storageAccount.CreateCloudTableClient();
table = tableClient.GetTableReference("AudioSamples");
}
private void deleteOldBlobs(AudioSampleEntityModel sample)
{
CloudBlobContainer blobContainer = _blobStorageService.getCloudBlobContainer();
CloudBlockBlob blob;
if (sample.Blob != null)
{
blob = blobContainer.GetBlockBlobReference(sample.Blob);
blob.Delete();
}
if (sample.SampleBlob != null)
{
blob = blobContainer.GetBlockBlobReference(sample.SampleBlob);
blob.Delete();
}
}
// PUT: api/Data/5
public IHttpActionResult Put(String id)
{
// PUT – see also ProductsController.cs from Lab 4
// Create a retrieve operation.
// id is a parameter of method and forms the row key
TableOperation retrieveOperation =
TableOperation.Retrieve<AudioSampleEntityModel>(partitionName, id);
TableResult getOperationResult = table.Execute(retrieveOperation);
if (getOperationResult.Result == null)
return NotFound();
AudioSampleEntityModel sample = (AudioSampleEntityModel)getOperationResult.Result;
deleteOldBlobs(sample);
try
{
CloudBlobContainer blobContainer = _blobStorageService.getCloudBlobContainer();
name = string.Format("{0}{1}", Guid.NewGuid(), ".mp3");
String path = "/mp3s" + name;
var baseUrl = Request.RequestUri.GetLeftPart(UriPartial.Authority);
String sampleURL = baseUrl.ToString() + "/api/data/" + id;
sample.SampleBlobURL = sampleURL;
sample.Blob = path;
sample.CreatedDate = DateTime.Now;
sample.SampleDate = null;
var updateOperation = TableOperation.InsertOrReplace(sample);
table.Execute(updateOperation);
CloudBlockBlob blob = blobContainer.GetBlockBlobReference(sample.Blob);
var request = HttpContext.Current.Request;
blob.Properties.ContentType = "audio/mpeg3";
blob.UploadFromStream(request.InputStream);
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine("DataController(PUT): " + e.Message);
return BadRequest("DataController(PUT): " + e.Message);
}
try
{
CloudQueue sampleQueue = _queueStorageService.getCloudQueue();
var queueMessageSample = new AudioSampleEntityModel(partitionName, id);
sampleQueue.AddMessage(new CloudQueueMessage(JsonConvert.SerializeObject(queueMessageSample)));
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine("DataController(PUT): " + e.Message);
return BadRequest("DataController(PUT): " + e.Message);
}
System.Diagnostics.Trace.WriteLine(String.Format("*** WebRole: Enqueued '{0}'", sample.Blob));
return StatusCode(HttpStatusCode.NoContent);
}
}
}
This is the webjob which i think is working fine.
public class Functions
{
public static void GenerateSample(
[QueueTrigger("audiosamplemaker")] AudioSampleEntityModel sampleInQueue,
[Table("Samples", "{PartitionKey}", "{RowKey}")] AudioSampleEntityModel sampleInTable,
[Blob("audiocollection/audio/{queueTrigger}")] CloudBlockBlob inputBlob,
[Blob("audiocollection/samples/{queueTrigger}")] CloudBlockBlob outputBlob, TextWriter logger,
[Table("Samples")] CloudTable tableBinding, TextWriter kek)
{
//use log.WriteLine() rather than Console.WriteLine() for trace output
logger.WriteLine("GenerateSample() started...");
logger.WriteLine("Input blob is: " + sampleInQueue);
// Open streams to blobs for reading and writing as appropriate.
// Pass references to application specific methods
using (Stream input = inputBlob.OpenRead())
using (Stream output = outputBlob.OpenWrite())
{
createSample(input, output, 20);
outputBlob.Properties.ContentType = "audio/mp3";
outputBlob.Metadata["Title"] = inputBlob.Metadata["Title"];
}
logger.WriteLine("GenerateSample() completed...");
}
private static void createSample(Stream input, Stream output, int duration)
{
using (var reader = new Mp3FileReader(input, wave => new NLayer.NAudioSupport.Mp3FrameDecompressor(wave)))
{
Mp3Frame frame;
frame = reader.ReadNextFrame();
int frameTimeLength = (int)(frame.SampleCount / (double)frame.SampleRate * 1000.0);
int framesRequired = (int)(duration / (double)frameTimeLength * 1000.0);
int frameNumber = 0;
while ((frame = reader.ReadNextFrame()) != null)
{
frameNumber++;
if (frameNumber <= framesRequired)
{
output.Write(frame.RawData, 0, frame.RawData.Length);
}
else break;
}
}
}
}
In your web api controller, the put method only has one parameter named ‘id’. But in your client, you also passed a stream content in put method.
So you couldn’t call web api method successfully. And for this stream content, you could set a string or model type in Put method instead of file stream.
According to your description, I suppose you want to pass data from client to web api, then read data from this file path in web Api and upload data to a blob.
I have created a sample demo to upload a myfile.txt to a Blob. Other data types are similar to this. You could refer to.
Code in Console:
Code in AudioSampleEntityModel class:
public class AudioSampleEntityModel : TableEntity
{
public AudioSampleEntityModel()
{
}
public AudioSampleEntityModel(string partitionName, string id)
{
}
public string id { get; set; }
public string Blob { get; set; }
public string SampleBlob { get; set; }
public string SampleBlobURL { get; set; }
public DateTime CreatedDate { get; set; }
public string SampleDate { get; set; }
}
Code in Function.cs:
public class Functions
{
/// <summary>
/// pass the content from Queue to Blob(The content is from myfile.txt)
/// </summary>
/// <param name="model"></param>
/// <param name="orderBlob"></param>
public static void MultipleOutput(
[QueueTrigger("myqueue2")] AudioSampleEntityModel model,
[Blob("orders/myfile")] out string orderBlob) //create a container named 'orders'
{
orderBlob = model.SampleBlob; //store the content from SampleBlob property to Blob
}
}
Code in Program:
class Program
{
static void Main() {
Console.WriteLine("----------------Update Employee -------------------");
Console.WriteLine("Enter id which you want to update");
string id = Console.ReadLine();
var response=DemoData(id).Result;
Console.WriteLine("the data from webapi: "+response);
var host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
public static async Task<string> DemoData(string id)
{
HttpClient client = new HttpClient();
string path = "http://localhost:53581/api/values/" + id;
Console.WriteLine("Enter the file name.");
string fileName = Console.ReadLine();
string filepath = "E:\\JanleyZhang\\" + fileName;
var filepathJson = JsonConvert.SerializeObject(filepath);// convert other FileStream type to json string
var data = new StringContent(content: filepathJson,
encoding: Encoding.UTF8,
mediaType: "application/json");
var response = await client.PutAsync(path, data);//get response from web api
var content = response.Content;
var result = await content.ReadAsStringAsync();//read content from response
//upload the data to a queue
string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference("myqueue2");
queue.CreateIfNotExists();
AudioSampleEntityModel model = new AudioSampleEntityModel()
{
id=id,
SampleBlob=result //pass the result to this property
};
queue.AddMessage(new CloudQueueMessage(JsonConvert.SerializeObject(model))); //store the file content to queue
return result;
}
}
Code in api controller:
public string Put(string id, [FromBody]string filepath) //pass two parameters
{
string b = "The id and model id are not equal.";
if (id == "1")
{
FileStream fs = File.OpenRead(filepath);
byte[] byt = new byte[fs.Length];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(byt, 0, byt.Length) > 0)
{
b=temp.GetString(byt);//read the content from myfile.txt
// The operation about uploading a blob .........
}
return b;
}
return b;
}
You could see the result like this:
The new Recaptcha 2 looks promising, but i didn't find a way to validate it in ASP.NET's server side,
if(Page.IsValid) in This answer, is valid for the old Recaptcha, but not the new one,
How to validate the new reCAPTCHA in server side?
After reading many resources, I ended up with writing this class to handle the validation of the new ReCaptcha :
As mentioned Here : When a reCAPTCHA is solved by end user, a new field (g-recaptcha-response) will be populated in HTML.
We need to read this value and pass it to the class below to validate it:
In C#:
In the code behind of your page :
string EncodedResponse = Request.Form["g-Recaptcha-Response"];
bool IsCaptchaValid = (ReCaptchaClass.Validate(EncodedResponse) == "true" ? true : false);
if (IsCaptchaValid) {
//Valid Request
}
The Class:
using Newtonsoft.Json;
public class ReCaptchaClass
{
public static string Validate(string EncodedResponse)
{
var client = new System.Net.WebClient();
string PrivateKey = "6LcH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory";
var GoogleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse));
var captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ReCaptchaClass>(GoogleReply);
return captchaResponse.Success.ToLower();
}
[JsonProperty("success")]
public string Success
{
get { return m_Success; }
set { m_Success = value; }
}
private string m_Success;
[JsonProperty("error-codes")]
public List<string> ErrorCodes
{
get { return m_ErrorCodes; }
set { m_ErrorCodes = value; }
}
private List<string> m_ErrorCodes;
}
In VB.NET:
In the code behind of your page :
Dim EncodedResponse As String = Request.Form("g-Recaptcha-Response")
Dim IsCaptchaValid As Boolean = IIf(ReCaptchaClass.Validate(EncodedResponse) = "True", True, False)
If IsCaptchaValid Then
'Valid Request
End If
The Class:
Imports Newtonsoft.Json
Public Class ReCaptchaClass
Public Shared Function Validate(ByVal EncodedResponse As String) As String
Dim client = New System.Net.WebClient()
Dim PrivateKey As String = "6dsfH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory"
Dim GoogleReply = client.DownloadString(String.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse))
Dim captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject(Of ReCaptchaClass)(GoogleReply)
Return captchaResponse.Success
End Function
<JsonProperty("success")> _
Public Property Success() As String
Get
Return m_Success
End Get
Set(value As String)
m_Success = value
End Set
End Property
Private m_Success As String
<JsonProperty("error-codes")> _
Public Property ErrorCodes() As List(Of String)
Get
Return m_ErrorCodes
End Get
Set(value As List(Of String))
m_ErrorCodes = value
End Set
End Property
Private m_ErrorCodes As List(Of String)
End Class
Here's a version that uses the JavaScriptSerializer. Thanks Ala for the basis for this code.
WebConfig App Setting -
I've added the secret key to the Web.Config in my case to allow transforms between environments. It can also be easily encrypted here if required.
<add key="Google.ReCaptcha.Secret" value="123456789012345678901234567890" />
The ReCaptcha Class - A simple class to post the response parameter along with your secret to Google and validate it. The response is deserialized using the .Net JavaScriptSerializer class and from that true or false returned.
using System.Collections.Generic;
using System.Configuration;
public class ReCaptcha
{
public bool Success { get; set; }
public List<string> ErrorCodes { get; set; }
public static bool Validate(string encodedResponse)
{
if (string.IsNullOrEmpty(encodedResponse)) return false;
var client = new System.Net.WebClient();
var secret = ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"];
if (string.IsNullOrEmpty(secret)) return false;
var googleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, encodedResponse));
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var reCaptcha = serializer.Deserialize<ReCaptcha>(googleReply);
return reCaptcha.Success;
}
}
Validate The Response - Check the validity of the g-Recaptcha-Response form parameter in your Controller (or code behind for a web form) and take appropriate action.
var encodedResponse = Request.Form["g-Recaptcha-Response"];
var isCaptchaValid = ReCaptcha.Validate(encodedResponse);
if (!isCaptchaValid)
{
// E.g. Return to view or set an error message to visible
}
Most of these answers seem more complex than needed. They also dont specify the IP which will help prevent a interception attack (https://security.stackexchange.com/questions/81865/is-there-any-reason-to-include-the-remote-ip-when-using-recaptcha). Here's what I settled on
public bool CheckCaptcha(string captchaResponse, string ipAddress)
{
using (var client = new WebClient())
{
var response = client.DownloadString($"https://www.google.com/recaptcha/api/siteverify?secret={ ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"] }&response={ captchaResponse }&remoteIp={ ipAddress }");
return (bool)JObject.Parse(response)["success"];
}
}
You can use "IsValidCaptcha()" method to validate your google recaptcha on server side. Replace your secret key with "YourRecaptchaSecretkey" in the following method.
Public bool IsValidCaptcha()
{
string resp = Request["g-recaptcha-response"];
var req = (HttpWebRequest)WebRequest.Create
(https://www.google.com/recaptcha/api/siteverify?secret=+ YourRecaptchaSecretkey + "&response=" + resp);
using (WebResponse wResponse = req.GetResponse())
{
using (StreamReader readStream = new StreamReader(wResponse.GetResponseStream()))
{
string jsonResponse = readStream.ReadToEnd();
JavaScriptSerializer js = new JavaScriptSerializer();
// Deserialize Json
CaptchaResult data = js.Deserialize<CaptchaResult>(jsonResponse);
if (Convert.ToBoolean(data.success))
{
return true;
}
}
}
return false;
}
Also create following class as well.
public class CaptchaResult
{
public string success { get; set; }
}
According to the doc you just post your secret key and user's answer to API and read returned "success" property
SHORT ANSWER:
var webClient = new WebClient();
string verification = webClient.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, userResponse));
if (JObject.Parse(verification)["success"].Value<bool>())
{
// SUCCESS!!!
FULL EXAMPLE:
Suppose, you implement this page in IamNotARobotLogin.cshtml.
<head>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<form action="Login" method="POST">
<div class="g-recaptcha" data-sitekey="your_site_key"></div><br/>
<input type="submit" value="Log In">
</form>
</body>
And suppose you wish the controller saved, let's say, "I_AM_NOT_ROBOT" flag in the session if the verification succeeded:
public ActionResult IamNotARobotLogin()
{
return View();
}
[HttpPost]
public ActionResult Login()
{
const string secretKey = "6LcH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory";
string userResponse = Request.Form["g-Recaptcha-Response"];
var webClient = new System.Net.WebClient();
string verification = webClient.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, userResponse));
var verificationJson = Newtonsoft.Json.Linq.JObject.Parse(verification);
if (verificationJson["success"].Value<bool>())
{
Session["I_AM_NOT_A_ROBOT"] = "true";
return RedirectToAction("Index", "Demo");
}
// try again:
return RedirectToAction("IamNotARobotLogin");
}
Here's my fork of Ala's solution in order to:
send paramter in POST
to sanitize the form input
include the requester IP address
store the secret in Web.Config:
In the controller:
bool isCaptchaValid = await ReCaptchaClass.Validate(this.Request);
if (!isCaptchaValid)
{
ModelState.AddModelError("", "Invalid captcha");
return View(model);
}
The utility class:
public class ReCaptchaClass
{
private static ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static string SecretKey = System.Configuration.ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"];
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("error-codes")]
public List<string> ErrorCodes { get; set; }
public static async Task<bool> Validate(HttpRequestBase Request)
{
string encodedResponse = Request.Form["g-Recaptcha-Response"];
string remoteIp = Request.UserHostAddress;
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{"secret", SecretKey},
{"remoteIp", remoteIp},
{"response", encodedResponse}
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("https://www.google.com/recaptcha/api/siteverify", content);
var responseString = await response.Content.ReadAsStringAsync();
var captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ReCaptchaClass>(responseString);
if ((captchaResponse.ErrorCodes?.Count ?? 0) != 0)
{
log.Warn("ReCaptcha errors: " + string.Join("\n", captchaResponse.ErrorCodes));
}
return captchaResponse.Success;
}
}
}
This article give clear step by step explication on how to implement a ReCaptcha validation attribute on your model.
First, create the Recaptcha validation attribute.
namespace Sample.Validation
{
public class GoogleReCaptchaValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Lazy<ValidationResult> errorResult = new Lazy<ValidationResult>(() => new ValidationResult("Google reCAPTCHA validation failed", new String[] { validationContext.MemberName }));
if (value == null || String.IsNullOrWhiteSpace( value.ToString()))
{
return errorResult.Value;
}
IConfiguration configuration = (IConfiguration)validationContext.GetService(typeof(IConfiguration));
String reCaptchResponse = value.ToString();
String reCaptchaSecret = configuration.GetValue<String>("GoogleReCaptcha:SecretKey");
HttpClient httpClient = new HttpClient();
var httpResponse = httpClient.GetAsync($"https://www.google.com/recaptcha/api/siteverify?secret={reCaptchaSecret}&response={reCaptchResponse}").Result;
if (httpResponse.StatusCode != HttpStatusCode.OK)
{
return errorResult.Value;
}
String jsonResponse = httpResponse.Content.ReadAsStringAsync().Result;
dynamic jsonData = JObject.Parse(jsonResponse);
if (jsonData.success != true.ToString().ToLower())
{
return errorResult.Value;
}
return ValidationResult.Success;
}
}
}
Then add the validation attribute on your model.
namespace Sample.Models
{
public class XModel
{
// ...
[Required]
[GoogleReCaptchaValidation]
public String GoogleReCaptchaResponse { get; set; }
}
}
Finally, you have just to call the ModelState.IsValid method
namespace Sample.Api.Controllers
{
[ApiController]
public class XController : ControllerBase
{
[HttpPost]
public IActionResult Post(XModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// ...
}
}
}
Et voilà ! :)
Another example is posted here:
RecaptchaV2.NET (Github)
It also implements the secure token option of Recaptcha 2.0 (look at full source code for that bit, I have stripped out relevant pieces of code ONLY for validating a result).
This one doesn't rely on newtonsoft's json parser and instead uses the built in .NET one.
Here is the relevant snippet of code from the RecaptchaV2.NET library (from recaptcha.cs):
namespace RecaptchaV2.NET
{
/// <summary>
/// Helper Methods for the Google Recaptcha V2 Library
/// </summary>
public class Recaptcha
{
public string SiteKey { get; set; }
public string SecretKey { get; set; }
public Guid SessionId { get; set; }
/// <summary>
/// Validates a Recaptcha V2 response.
/// </summary>
/// <param name="recaptchaResponse">g-recaptcha-response form response variable (HttpContext.Current.Request.Form["g-recaptcha-response"])</param>
/// <returns>RecaptchaValidationResult</returns>
public RecaptchaValidationResult Validate(string recaptchaResponse)
{
RecaptchaValidationResult result = new RecaptchaValidationResult();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://www.google.com/recaptcha/api/siteverify?secret=" + SecretKey + "&response="
+ recaptchaResponse + "&remoteip=" + GetClientIp());
//Google recaptcha Response
using (WebResponse wResponse = req.GetResponse())
{
using (StreamReader readStream = new StreamReader(wResponse.GetResponseStream()))
{
string jsonResponse = readStream.ReadToEnd();
JavaScriptSerializer js = new JavaScriptSerializer();
result = js.Deserialize<RecaptchaValidationResult>(jsonResponse.Replace("error-codes", "ErrorMessages").Replace("success", "Succeeded"));// Deserialize Json
}
}
return result;
}
private string GetClientIp()
{
// Look for a proxy address first
String _ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
// If there is no proxy, get the standard remote address
if (string.IsNullOrWhiteSpace(_ip) || _ip.ToLower() == "unknown")
_ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
return _ip;
}
}
public class RecaptchaValidationResult
{
public RecaptchaValidationResult()
{
ErrorMessages = new List<string>();
Succeeded = false;
}
public List<string> ErrorMessages { get; set; }
public bool Succeeded { get; set; }
public string GetErrorMessagesString()
{
return string.Join("<br/>", ErrorMessages.ToArray());
}
}
}
Google's ReCaptcha API no longer accepts the payload as query string parameters in a GET request. Google always returned a "false" success response unless I sent the data via HTTP POST. Here is an update to Ala's (excellent!) class which POSTs the payload to the Google service endpoint:
using Newtonsoft.Json;
using System.Net;
using System.IO;
using System.Text;
public class RecaptchaHandler
{
public static string Validate(string EncodedResponse, string RemoteIP)
{
var client = new WebClient();
string PrivateKey = "PRIVATE KEY";
WebRequest req = WebRequest.Create("https://www.google.com/recaptcha/api/siteverify");
string postData = String.Format("secret={0}&response={1}&remoteip={2}",
PrivateKey,
EncodedResponse,
RemoteIP);
byte[] send = Encoding.Default.GetBytes(postData);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = send.Length;
Stream sout = req.GetRequestStream();
sout.Write(send, 0, send.Length);
sout.Flush();
sout.Close();
WebResponse res = req.GetResponse();
StreamReader sr = new StreamReader(res.GetResponseStream());
string returnvalue = sr.ReadToEnd();
var captchaResponse = JsonConvert.DeserializeObject<RecaptchaHandler>(returnvalue);
return captchaResponse.Success;
}
[JsonProperty("success")]
public string Success
{
get { return m_Success; }
set { m_Success = value; }
}
private string m_Success;
[JsonProperty("error-codes")]
public List<string> ErrorCodes
{
get { return m_ErrorCodes; }
set { m_ErrorCodes = value; }
}
private List<string> m_ErrorCodes;
}
Using dynamic to validate recaptcha at server side
Calling Function
[HttpPost]
public ActionResult ClientOrderDetail(FormCollection collection, string EncodedResponse)
{
Boolean Validation = myFunction.ValidateRecaptcha(EncodedResponse);
return View();
}
Function Declaration
public static Boolean ValidateRecaptcha(string EncodedResponse)
{
string PrivateKey = "YourSiteKey";
var client = new System.Net.WebClient();
var GoogleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse));
var serializer = new JavaScriptSerializer();
dynamic data = serializer.Deserialize(GoogleReply, typeof(object));
Boolean Status = data["success"];
string challenge_ts = data["challenge_ts"];
string hostname = data["hostname"];
return Status;
}
the example I posted in this so post uses Newtonsoft.JSON to deserialize the full returned JSON, posts the data to Google(as opposed to using a querystring) stores the relevant variables in the web.config rather than hard coded.
I need to create an instance of HttpPostedFileBase class object and pass it to a method, but I cannot find any way to instantiate it. I am creating a test case to test my fileupload method.
This is my method which takes an HttpPostedFileBase object. I need to call it from my test case class. I am not using any mock library.
Is there a simple way to do this?
[HttpPost]
public JsonResult AddVariation(HttpPostedFileBase file, string name, string comment, string description, decimal amount, string accountLineTypeID)
{
var accountLineType = _fileService.GetAccountLineType(AccountLineType.Debit);
if (Guid.Parse(accountLineTypeID) == _fileService.GetAccountLineType(AccountLineType.Credit).AccountLineTypeID)
{
amount = 0 - amount;
}
var info = new File()
{
FileID = Guid.NewGuid(),
Name = name,
Description = description,
FileName = file.FileName,
BuildID = Guid.Parse(SelectedBuildID),
MimeType = file.ContentType,
CreatedUserID = CurrentUser.UserID,
UpdatedUserID = CurrentUser.UserID,
Amount = amount,
};
var cmmnt = new Comment()
{
CommentDate = DateTime.Now,
CommentText = comment,
FileID = info.FileID,
UserID = CurrentUser.UserID
};
_variationService.AddVariation(info, file.InputStream);
_variationService.AddComment(cmmnt);
return Json("Variation Added Sucessfully", JsonRequestBehavior.AllowGet);
}
HttpPostedFileBase is an abstract class so therefore it cannot be directly instantiated.
Create a class that derives from HttpPostedFileBase and returns the values you are looking for.
class MyTestPostedFileBase : HttpPostedFileBase
{
Stream stream;
string contentType;
string fileName;
public MyTestPostedFileBase(Stream stream, string contentType, string fileName)
{
this.stream = stream;
this.contentType = contentType;
this.fileName = fileName;
}
public override int ContentLength
{
get { return (int)stream.Length; }
}
public override string ContentType
{
get { return contentType; }
}
public override string FileName
{
get { return fileName; }
}
public override Stream InputStream
{
get { return stream; }
}
public override void SaveAs(string filename)
{
throw new NotImplementedException();
}
}
I think that #BenjaminPaul has the best answer - but wanted to add this in case anyone else is looking to test the content length on the MyTestPostedFileBase object.
I created the class as is outlined above and then passing a stream that is filled with random bytes - this allows the `MyTestPostedFileBase.ContentLength to return a testable value that I needed.
byte[] byteBuffer = new Byte[10];
Random rnd = new Random();
rnd.NextBytes(byteBuffer);
System.IO.MemoryStream testStream = new System.IO.MemoryStream(byteBuffer);
and then instantiate it:
var TestImageFile = new MyTestPostedFileBase(testStream, "test/content", "test-file.png");