Синхронный код VirusTotal для Rx включен асинхронно (и, возможно, некоторый анализ JSON)
В прошлый раз, когда я разместил здесь вопрос, все предоставили отличное руководство по решению моей проблемы. Двигайтесь вперед во времени и вот другое. Я пытаюсь переделать небольшой вспомогательный инструмент, который проверяет URL и файлы на предмет VirusTotal, чтобы получить некоторую основную информацию. Код ниже работает довольно хорошо, но блокирует пользовательский интерфейс. Мне сказали, что я должен посмотреть на Rx, и мне нравится читать по нему, но, похоже, я не могу обернуться вокруг него. Итак, теперь возникает вопрос: как лучше спроектировать следующий код, чтобы он использовал Rx, чтобы он был асинхронным и оставлял мой пользовательский интерфейс в покое, пока он работает? VirusTotal также использует многоуровневый JSON для ответов, поэтому, если у кого-то есть хороший способ интегрировать это в это, это было бы даже лучше.
class Virustotal
{
private string APIKey = "REMOVED";
private string FileReportURL = "https://www.virustotal.com/vtapi/v2/file/report";
private string URLReportURL = "http://www.virustotal.com/vtapi/v2/url/report";
private string URLSubmitURL = "https://www.virustotal.com/vtapi/v2/url/scan";
WebRequest theRequest;
HttpWebResponse theResponse;
ArrayList theQueryData;
public string GetFileReport(string checksum) // Gets latest report of file from VT using a hash (MD5 / SHA1 / SHA256)
{
this.WebPostRequest(this.FileReportURL);
this.Add("resource", checksum);
return this.GetResponse();
}
public string GetURLReport(string url) // Gets latest report of URL from VT
{
this.WebPostRequest(this.URLReportURL);
this.Add("resource", url);
this.Add("scan", "1"); //Automatically submits to VT if no result found
return this.GetResponse();
}
public string SubmitURL(string url) // Submits URL to VT for insertion to scanning queue
{
this.WebPostRequest(this.URLSubmitURL);
this.Add("url", url);
return this.GetResponse();
}
public string SubmitFile() // Submits File to VT for insertion to scanning queue
{
// File Upload code needed
return this.GetResponse();
}
private void WebPostRequest(string url)
{
theRequest = WebRequest.Create(url);
theRequest.Method = "POST";
theQueryData = new ArrayList();
this.Add("apikey", APIKey);
}
private void Add(string key, string value)
{
theQueryData.Add(String.Format("{0}={1}", key, Uri.EscapeDataString(value)));
}
private string GetResponse()
{
// Set the encoding type
theRequest.ContentType="application/x-www-form-urlencoded";
// Build a string containing all the parameters
string Parameters = String.Join("&",(String[]) theQueryData.ToArray(typeof(string)));
theRequest.ContentLength = Parameters.Length;
// We write the parameters into the request
StreamWriter sw = new StreamWriter(theRequest.GetRequestStream());
sw.Write(Parameters);
sw.Close();
// Execute the query
theResponse = (HttpWebResponse)theRequest.GetResponse();
StreamReader sr = new StreamReader(theResponse.GetResponseStream());
return sr.ReadToEnd();
}
}
1 ответ
Ваш код написан плохо, что делает его более трудным сделать асинхронным - в первую очередь три переменные уровня класса. При кодировании на Rx вы хотите думать "функциональное программирование", а не "ООП" - поэтому нет переменных уровня класса.
Итак, что я сделал, это - я перекодировал GetResponse
метод для инкапсуляции всего состояния в один вызов - и я сделал его возвращение IObservable<string>
а не просто string
,
Публичные функции теперь можно записать так:
public IObservable<string> GetFileReport(string checksum)
{
return this.GetResponse(this.FileReportURL,
new Dictionary<string, string>() { { "resource", checksum }, });
}
public IObservable<string> GetURLReport(string url)
{
return this.GetResponse(this.URLReportURL,
new Dictionary<string, string>()
{ { "resource", url }, { "scan", "1" }, });
}
public IObservable<string> SubmitURL(string url)
{
return this.GetResponse(this.URLSubmitURL,
new Dictionary<string, string>() { { "url", url }, });
}
public IObservable<string> SubmitFile()
{
return this.GetResponse("UNKNOWNURL", new Dictionary<string, string>());
}
А также GetResponse
выглядит так:
private IObservable<string> GetResponse(
string url,
Dictionary<string, string> theQueryData)
{
return Observable.Start(() =>
{
var theRequest = WebRequest.Create(url);
theRequest.Method = "POST";
theRequest.ContentType="application/x-www-form-urlencoded";
theQueryData.Add("apikey", APIKey);
string Parameters = String.Join("&",
theQueryData.Select(x =>
String.Format("{0}={1}", x.Key, x.Value)));
theRequest.ContentLength = Parameters.Length;
using (var sw = new StreamWriter(theRequest.GetRequestStream()))
{
sw.Write(Parameters);
sw.Close();
}
using (var theResponse = (HttpWebResponse)theRequest.GetResponse())
{
using (var sr = new StreamReader(theResponse.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
});
}
Я на самом деле не проверял это - у меня нет APIKEY для начинающих - но он должен работать хорошо. Дайте мне знать, как вы идете.