CheckKeywords:校验你的接口参数中是否使用了其他语言的关键字/保留字

WHY

不想看文字说明的可以看图片说明

img

前段时间公司在做一个新的项目,还是负责了 API 部分的开发,照常使用restful风格,照常的 需求评审->技术方案评审->接口定义->与web/app端开发评审接口定义->开发->联调->测试 项目流程。结果中间遇到一个有意思的问题。

有个参数的含义的是功能项的意思,查了下 codelf 挑了几个相对合适的与各端开发一起沟通了下,确定使用了 function ,然后 eolinker 也定义好了,对应的类也建好了(公司技术栈:后端 .NET Core,前端 React , APP swift/java),结果前端开发发现 functionjskeywords ,就民主的改为了 func ,改完没两天,IOS 的开发又发现 func 是 Swiftkeywords ,得,又要改一次。(ps 自己也写前端,上学时也用 Swift 写过 APP ,但是评审的时候也没注意到。)

问题虽然不大,却会很烦恼,因为已经评审过了,所以 eolinker 的定义,代码都要改。查了下有没有现成支持做这个校验的,就自己实现了下,刚好没用过 Blazor ,所以做了一个接口版本,也做了一个 Blazor 版本

HOW

项目已开源 Git 源码 ,程序整体上并不复杂,只列下几个注意的点

Blazor

Blazor 是一个用 .NET 生成交互式客户端 Web UI 框架。可以和 js 互操作,使用 .NET 库。

Blazor wasm 是 Blazor 对 WebAssembly的实现。一个 SPA 框架,可以纯静态部署,本项目是选择了wasm版本。

想了解更多关于Blazor的说明,可以看 官方文档

因为想用下Blazor和API共用类库,解决方案是这样的

  • CheckKeywordsBlazor
  • CheckKeywordsGateway
  • Shared(类库)
  • Test(单元测试)

HttpClient

因为支持swagger/openapi json url的解析,所以要用到http请求。在官方示例中,C# 和 HTML 都是写在razor里的,使用时像下边这样就可以。(Program.Main 需要注入 HttpClient )

index.razor

1
2
3
4
5
6
7
8
9
@using System.Net.Http
@inject HttpClient Http

@code {
private TodoItem[] todoItems;

protected override async Task OnInitializedAsync() =>
todoItems = await Http.GetFromJsonAsync<TodoItem[]>("api/TodoItems");
}

之前vs版本,对于Blazor格式化有点问题,所以就将类写成了partial class,想当然的写出来下边的代码,结果报错了。

1
2
3
4
5
6
7
8
9
10
11
public partial class Index
{
private readonly HttpClient _httpClient {get; set;}

public Index(HttpClient httpClient)
{
_httpClient = httpClient;
}

//使用_httpClient
}

看了下官方文档,改成这样就可以了

1
2
3
4
5
6
7
public partial class Index
{
[Inject]
protected HttpClient HttpClient { get; set; }

//使用HttpClient
}

部署

由于这个是单页面应用,会存在和其他单页面应用相同的问题,当访问 baseurl/xxx时,就会404。和James沟通过这个,非动态url也可以实现像umi静态化那样的发布时为每个路由生成html文件

  • IIS

    • 可以安装url-rewrite模块进行配置
  • Nginx

    1
    2
    3
    location / {
    try_files $uri $uri/ /index.html =404;
    }

EmbeddedResource

类库中的实现本来是这样的:把各个语言的关键词写到一个json文件,类库直接读文件放入内存中使用。在Blazor中不能直接读文件(需要改为http请求json),为了保证类库通用,将json文件改为嵌入的资源(EmbeddedResource),通过 GetManifestResourceStream 读取。

1
2
3
4
var assembly = Assembly.GetExecutingAssembly();
var configStream = assembly.GetManifestResourceStream("Shared.keywords.json");
var streamReader = new StreamReader(configStream);
var str = streamReader.ReadToEnd();

压缩

Blazor wasm 首次访问时需要下载引用的dll,可想而知要下载的文件有多大,所以还是要压缩下的。

  • IIS

  • github/静态部署
    index.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <script src="decode.min.js"></script>
    <script src="_framework/Blazor.webassembly.js" autostart="false"></script>
    <script>
    Blazor.start({
    loadBootResource: function (type, name, defaultUri, integrity) {
    if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
    return (async function () {
    const response = await fetch(defaultUri + '.br', {
    cache: 'no-cache'
    });
    if (!response.ok) {
    throw new Error(response.statusText);
    }
    const originalResponseBuffer = await response.arrayBuffer();
    const originalResponseArray = new Int8Array(originalResponseBuffer);
    const decompressedResponseArray = BrotliDecode(originalResponseArray);
    const contentType = type ===
    'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
    return new Response(decompressedResponseArray, {
    headers: {
    'content-type': contentType
    }
    });
    })();
    }
    }
    });
    </script>

    ps. git google/brotli上的decode.min.js有bug,对应issue, 我是自己压缩的,需要可以自取,或者自行压缩

压缩效果

不压缩 9.3M
压缩后 3.5M

Text.Json 的使用

微软官方文档中,关于.NET Core 的最佳实践,总会有建议用 System.Text.Json 替换其他的 Json 库,可能解析的太小,没有明显感觉到变快。附一个两个类库获取 Json 中所有的 key 的方法吧。

感谢 bill 和 shawn,对该部分的贡献。

Newtonsoft.Json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public override List<string> BuildNeedCheckWords(string str)
{
var words = new List<string>();
var jsonObject = JObject.Parse(str);
BuildJsonAllKey(jsonObject, words);

return words.Distinct().ToList();

}

private static void BuildJsonAllKey(JObject jObject, List<string> keys)
{
foreach (var item in jObject.Cast<KeyValuePair<string, JToken>>().ToList())
{
keys.Add(item.Key);

if (item.Value is JObject itemValueJObject)
{
BuildJsonAllKey(itemValueJObject, keys);
continue;
}
}
}

System.Text.Json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public override List<string> BuildNeedCheckWords(string str)
{
var jsonDocument = JsonDocument.Parse(str, new JsonDocumentOptions
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip
});

var words = new List<string>();
BuildJsonAllKey(jsonDocument.RootElement, words);

return words.Distinct().ToList();
}

private static void BuildJsonAllKey(JsonElement jsonElement, List<string> keys)
{
foreach (var jsonProperty in jsonElement.EnumerateObject())
{
keys.Add(jsonProperty.Name);

if (jsonProperty.Value.ValueKind == JsonValueKind.Object)
{
BuildJsonAllKey(jsonProperty.Value, keys);
}
}
}

terminal color

img

个人很喜欢使用 CLI ,所以在 API 有做转换,如果是 shell 请求不返回json,而是带有颜色标记的 text/plain ,该部分有参考 wttr.in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public string[] PLAIN_TEXT_AGENTS = {
"curl",
"httpie",
"lwp-request",
"wget",
"python-requests",
"openbsd ftp",
"powershell"
};
// check
var agent = HttpContext.Request.Headers[HeaderNames.UserAgent][0].ToLower();

if (PLAIN_TEXT_AGENTS.Any(t => agent.Contains(t)))
{
return Content(ConvertCLI(result));
}

//ConvertCLI
private string ConvertCLI(CheckResult checkResult)
{
var result = string.Empty;
if (checkResult.Result == (int)EnumCheckResult.Success)
{
result = "Perfect √\r\n";
}
else if (checkResult.Result == (int)EnumCheckResult.Available)
{
result = "Usable\r\n\r\nSavewords\r\n";
checkResult.Items.ForEach(
t =>
{
result += @$"Language:{t.Language}
SaveWords:{string.Join(',', t.Words)}
REF:{t.Ref}
";
});
}
//...
else
{
result = "System Error\r\n";
}
result += PLAIN_END;

return result;
}

项目地址

https://github.com/JiChao99/check-keywords

当前支持的语言

  • C#, JavaScript, C, Java, Python, C++, PHP, VB, R, Ruby, Go, Swift, Objective-C, Rust, Kotlin, Dart, TypeScript

如果有其他语言需要 欢迎PR,或者联系我添加。

REF

评论