最近因为有个需要截图OCR的需求,再加上需要交一个C#大作业,就做了一个WinForm截图OCR小项目出来。
(不过后来发现QQ截图好像已经有这功能了?)
运行截图:





谈一谈挺有意思的一些知识点:
Http请求
Delegate委托回调
我封装了一个POST请求的函数:
需要下载一个Newtonsoft.Json
用于处理响应的JSON数据
这种方式是使用了一个委托回调,类似于JavaScript中的回调函数,C#中叫Delegate回调
delegate void AfterRequest(JObject data);
class HttpUtil
{
/// <summary>
/// 发出POST请求
/// </summary>
/// <param name="url">请求的url</param>
/// <param name="requestBody">url参数格式的字符串,如"key1=value1&key2=value2",需要经过urlencode处理</param>
/// <param name="function">回调函数,data是一个json转换为的一个JObject对象</param>
public static void PostAsync(String url, string requestBody, AfterRequest function)
{
try
{
string responseBody = null;
HttpClient httpClient = new HttpClient();
//StringContent content = new StringContent(requestBody,System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
StringContent content = new StringContent(requestBody);
content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = null;
response = httpClient.PostAsync(url, content).Result;
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
responseBody = response.Content.ReadAsStringAsync().Result;
}
JObject data = JObject.Parse(responseBody);
function(data);
}
catch (Exception e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", e.Message);
}
}
}
Async/Await
实际上还有一种能使代码更清晰、简洁的方法,并且这种方法更受C#推荐:Async/Await
class HttpUtil
{
/// <summary>
/// 发出POST请求
/// </summary>
/// <param name="url">请求的url</param>
/// <param name="requestBody">url参数格式的字符串,如"key1=value1&key2=value2",需要经过urlencode处理</param>
public static async Task<JObject> PostAsync(String url, string requestBody)
{
try
{
string responseBody = null;
HttpClient httpClient = new HttpClient();
//StringContent content = new StringContent(requestBody,System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
StringContent content = new StringContent(requestBody);
content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = null;
response = await httpClient.PostAsync(url, content);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
responseBody = await response.Content.ReadAsStringAsync();
}
JObject data = JObject.Parse(responseBody);
return data;
}
catch (Exception e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ", e.Message);
}
return null;
}
}
使用这种方法,能避免回调地狱,让调用时代码不会多层嵌套。
甚至ES7也支持了这种方法,语法和C#类似。
原理:
函数执行时,一旦遇到await就会返回。等到触发的异步操作完成(并且调用栈清空),再接着执行函数体内后面的语句
实际上async/await只是个语法糖,内部执行顺序和回调函数并无区别。
API鉴权
因为做这个应用去调用了百度云和腾讯云提供的OCR api,恰巧发现两家的api鉴权机制正是目前主流验证方式之中具有代表性的两种。
百度云:Token
看看百度云的Token获取方法:
向授权服务地址https://aip.baidubce.com/oauth/2.0/token
发送请求(推荐使用POST),并在URL中带上以下参数:
- grant_type: 必须参数,固定为client_credentials;
- client_id: 必须参数,应用的API Key;
- client_secret: 必须参数,应用的Secret Key;
token验证方式就是给每个用户分配一套账号和密码(这里是client_id和client_secret),服务器能通过这套认证信息获取用户的权限,生成一个token字符串,保存在本地并返回给用户,用户请求api的时候就要带上这个token(可以是放在请求体或请求头中),服务器就能通过token验证用户的身份。
安全隐患: Token被劫持,伪造请求和篡改参数。
腾讯云:签名验证
可以看看腾讯云提供的计算签名的步骤:
- 将<key, value>请求参数对按key进行字典升序排序,得到有序的参数对列表N
- 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8
- 将应用密钥以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=密钥)
- 对字符串S进行MD5运算,将得到的MD5值所有字符转换成大写,得到接口请求签名
可以看出腾讯云的验证机制更为复杂,在计算签名的时候需要用到当前时间戳、一个随机字符串和用户的app_key,这样得到的签名每次请求的时候都是不同的,即使中间者劫持了签名也无法利用签名进行第二次重复请求,如果请求参数被篡改也能识别出。
Token和签名验证并不冲突,签名验证是在身份认证的基础上防止请求重放、参数篡改等攻击。百度云这种加密机制也可以使用某种密钥将带有token的参数字符串生成签名进行验证。
总结
要看到知识的融会贯通之处,学习JS相关的异步思想,后端的验证机制。要善于思考,以行促学。
原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/winform%e6%88%aa%e5%9b%beocr%e5%b0%8f%e9%a1%b9%e7%9b%ae%e7%9a%84%e5%bc%80%e5%8f%91-%e7%9e%a5%e8%a7%81%e7%9f%a5%e8%af%86%e7%9a%84%e8%9e%8d%e4%bc%9a%e8%b4%af%e9%80%9a/