├── .gitattributes
├── .gitignore
├── FastWebApi
├── BaseWebApi.cs
├── FormHandler.cs
├── HttpSession.cs
├── IWebApiRouter.cs
├── JwsIntegration.cs
├── TinyFox.FastWebApi.csproj
├── UrlHelper.cs
└── WebApiMiddleware.cs
├── LICENSE
├── README.md
├── TinyFox.FastWebApi.sln
└── WebApiTest
├── App.config
├── MyApp
├── MyApp1.cs
└── MyApp2.cs
├── MyWebApiRouter.cs
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── Startup.cs
├── WebApiTest.csproj
└── packages.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/FastWebApi/BaseWebApi.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using Microsoft.Owin;
4 |
5 | namespace TinyFox.FastWebApi
6 | {
7 |
8 |
9 | ///
10 | /// Fast WebApi 框架的用户处理类基类
11 | ///
12 | public abstract class BaseWebApi
13 | {
14 |
15 | ///
16 | /// 请求(输入)对象
17 | ///
18 | protected IOwinRequest Request;
19 |
20 | ///
21 | /// 应答(输出)对象
22 | ///
23 | protected IOwinResponse Response;
24 |
25 | ///
26 | /// OWIN处理上下文对象
27 | ///
28 | protected IOwinContext Content;
29 |
30 | ///
31 | /// 用户级Session
32 | ///
33 | protected HttpSession Session;
34 |
35 | ///
36 | /// 路由参数
37 | ///
38 | protected readonly IDictionary RouteArgs = new Dictionary();
39 |
40 |
41 | ///
42 | /// 添加路由参数
43 | ///
44 | ///
45 | ///
46 | internal void AddRouteArgs(string key, string value)
47 | {
48 | RouteArgs[key] = value;
49 | }
50 |
51 |
52 | ///
53 | /// 处理请求
54 | ///
55 | ///
56 | ///
57 | public Task ProcessRequest(IOwinContext context)
58 | {
59 | Content = context;
60 | Response = context.Response;
61 | Request = context.Request;
62 | Session = new HttpSession(context);
63 |
64 | return Task.Factory.StartNew(ProcessRequest);
65 | }
66 |
67 | ///
68 | /// 具体处理请求
69 | ///
70 | protected abstract void ProcessRequest();
71 |
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/FastWebApi/FormHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using Microsoft.Owin;
7 |
8 | namespace TinyFox.FastWebApi
9 | {
10 |
11 |
12 | ///
13 | /// POST数据处理类
14 | ///
15 | class FormHandler
16 | {
17 |
18 |
19 | ///
20 | /// 表单元素对象
21 | ///
22 | public class FormItem
23 | {
24 | ///
25 | /// 字段名
26 | ///
27 | public string FieldName;
28 |
29 | ///
30 | /// 文件名
31 | ///
32 | public string FileName;
33 |
34 | ///
35 | /// 数据实体
36 | ///
37 | public byte[] FileBody;
38 |
39 | ///
40 | /// 文本内容
41 | ///
42 | public string TextValue;
43 | }
44 |
45 | ///
46 | /// 保存表单域的字典
47 | ///
48 | private readonly IDictionary _dictionary = new Dictionary();
49 |
50 |
51 | ///
52 | /// 表单字段名列表
53 | ///
54 | public List FormNames
55 | {
56 | get { return _dictionary.Keys.ToList(); }
57 | }
58 |
59 | ///
60 | /// 获得的表单对象列表
61 | ///
62 | public IList FormItems
63 | {
64 | get
65 | {
66 | if (_dictionary.Count < 1) return new List();
67 | return _dictionary.Values.ToList();
68 | }
69 | }
70 |
71 | ///
72 | /// 表单字段数量
73 | ///
74 | public int FieldCount { get { return _dictionary.Count; } }
75 |
76 |
77 | ///
78 | /// 根据字段名获取表单域内容
79 | ///
80 | ///
81 | ///
82 | public FormItem this[string name]
83 | {
84 | get
85 | {
86 | if (!_dictionary.Keys.Contains(name)) return null;
87 | return _dictionary[name];
88 | }
89 | }
90 |
91 |
92 | ///
93 | /// 实例化一个表单处理对象
94 | ///
95 | ///
96 | public FormHandler(IOwinContext context)
97 | {
98 | if (context.Request.Method.ToUpper() != "POST") throw new Exception();
99 | var strLen = context.Request.Headers["Content-Length"];
100 | if (string.IsNullOrEmpty(strLen)) strLen = context.Request.Headers["content-length"];
101 | if (string.IsNullOrEmpty(strLen)) throw new Exception("Not 'Content-Length'");
102 | if (!uint.TryParse(strLen, out uint sumsize)) throw new Exception("Content-Length Error");
103 | if (string.IsNullOrEmpty(context.Request.ContentType)) throw new Exception("Error:ContentType is NULL.");
104 |
105 |
106 | //把数据读到缓冲区以备处理
107 | var buffer = new MemoryStream();
108 | var tmpbuffer = new byte[1024 * 64];
109 | uint nextsize = sumsize; //sumsize:POST正文的总长度
110 |
111 | //循环读取所有正文数据并写入内存流
112 | while (nextsize > 0)
113 | {
114 | var len = context.Request.Body.Read(tmpbuffer, 0, tmpbuffer.Length);
115 | if (len < 1) throw new Exception("Network Is Disconnect?");
116 | nextsize -= (uint)len;
117 |
118 | buffer.Write(tmpbuffer, 0, len);
119 | }
120 |
121 | //正文类型
122 | var contentTypes = context.Request.ContentType.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
123 |
124 |
125 | //处理多实体
126 | /////////////////////////////
127 |
128 | if (contentTypes.Length > 1)
129 | {
130 | var ism = false; //是否是多实体
131 | var boundar = ""; //分隔线
132 | foreach (var s in contentTypes)
133 | {
134 | //Content-Type:multipart/form-data; boundary=abcdef
135 | var ss = s.Trim().ToLower();
136 | if (ss == "multipart/form-data") ism = true;
137 | if (ss.StartsWith("boundary=")) boundar = "--" + s.Substring(s.IndexOf('=') + 1).Trim();
138 | }
139 |
140 | //如果是,处理
141 | if (ism && boundar != "")
142 | {
143 | ParseMultipart(buffer.ToArray(), boundar);
144 | return;
145 | }
146 | }
147 |
148 | //处理其它格式(x-www-form-urlencoded)
149 | /////////////////////////////
150 | ParseForm(buffer.ToArray(), null);
151 |
152 | }
153 |
154 |
155 | ///
156 | /// 处理多实体表单
157 | ///
158 | /// 数据
159 | /// 分隔线
160 | private unsafe void ParseMultipart(byte[] body, string boundar)
161 | {
162 | var byttag = Encoding.ASCII.GetBytes(boundar);
163 |
164 | //每个分隔线后正文起点位置
165 | var poslist = new List();
166 |
167 |
168 | //找出每个元素的起点位置
169 | fixed (byte* bp = body, tp = byttag)
170 | {
171 | var keyNum = *(long*)tp;
172 | for (var i = 0; i < body.Length - byttag.Length; i++)
173 | {
174 | var lngnum = *(long*)(bp + i);
175 | if (lngnum == keyNum)
176 | {
177 | var strTag = Encoding.ASCII.GetString(body, i, byttag.Length);
178 | if (strTag == boundar)
179 | {
180 | poslist.Add(i + byttag.Length + 2); //+2是跳过分隔线的回车
181 | i += byttag.Length;
182 | }
183 |
184 | //结束标记
185 | if (body[i + 1] == '-' && body[i + 2] == '-' && body[i + 3] == 0x0D && body[i + 4] == 0x0A) break;
186 | }
187 | }
188 | }
189 |
190 |
191 | //如果元素为0不再继续处理
192 | if (poslist.Count < 2) return;
193 |
194 | //处理每一个元素
195 | for (var i = 0; i < poslist.Count - 1; i++)
196 | {
197 | var size = poslist[i + 1] - poslist[i] - byttag.Length - 4; //分隔线前后有回车符
198 | if (size <= 0) continue;
199 |
200 | var bytes = new byte[size];
201 | Buffer.BlockCopy(body, poslist[i], bytes, 0, size);
202 |
203 | ParseMulitPartItem(bytes);
204 | }
205 |
206 | }
207 |
208 |
209 | ///
210 | /// 处理一个MulitPart表单域
211 | ///
212 | ///
213 | private void ParseMulitPartItem(byte[] body)
214 | {
215 | if (body.Length < 10) return;
216 |
217 | //每一节都是由一个“描述头+双回车+正文实体”组成
218 |
219 | //找出双回车起始位置
220 | var crlf2 = FindCrlf2(body);
221 | if (crlf2 < 1 || crlf2 >= body.Length - 4) return;
222 |
223 | //声明/说明部分(描述头)
224 | var bytHead = new byte[crlf2];
225 | Buffer.BlockCopy(body, 0, bytHead, 0, crlf2);
226 |
227 | //正文部分
228 | var bytBody = new byte[body.Length - crlf2 - 4];
229 | Buffer.BlockCopy(body, crlf2 + 4, bytBody, 0, bytBody.Length);
230 |
231 |
232 | //处理节点说明部分
233 | var strHandle = Encoding.ASCII.GetString(bytHead); //utf8?
234 | var strHandles = strHandle.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
235 |
236 | var name = "";
237 | var fileName = "";
238 | foreach (var s in strHandles)
239 | {
240 | var ss = s.Trim();
241 | //Content-Transfer-Encoding: binary
242 | //正文编码方式
243 |
244 | //处理正文描述
245 | if (ss.StartsWith("Content-Disposition:"))
246 | {
247 | var disp = ss.Substring(20).Trim();
248 | var dispItems = disp.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
249 | foreach (var item in dispItems)
250 | {
251 | var itemx = item.Trim();
252 | if (itemx.StartsWith("name=")) name = itemx.Substring(5).Trim(new[] { '\x20', '\"' });
253 | if (itemx.StartsWith("filename=")) fileName = itemx.Substring(9).Trim(new[] { '\x20', '\"' });
254 | }
255 | }
256 | }
257 |
258 | if (name == "") return;
259 |
260 | if (!string.IsNullOrEmpty(fileName))
261 | {
262 | //文件名不为空,表示是文件
263 | var v = new FormItem { FieldName = name, FileName = fileName, FileBody = bytBody };
264 | _dictionary.Add(name, v);
265 | }
266 | else
267 | {
268 | //没有文件名,肯定是普通表单
269 | var txt = Encoding.UTF8.GetString(bytBody);
270 | var v = new FormItem { FieldName = name, TextValue = txt };
271 | _dictionary.Add(name, v);
272 | }
273 | //Console.WriteLine("name:{0}, file:{1}", name, fileName);
274 |
275 | }
276 |
277 |
278 | ///
279 | /// 处理普通表单(键值对)
280 | ///
281 | /// 正文数据
282 | /// 目标数据字符集
283 | void ParseForm(byte[] body, Encoding charset)
284 | {
285 | if (charset == null) charset = Encoding.UTF8;
286 |
287 | // ContentType = "application/x-www-form-urlencoded;charset=utf8";
288 | // name=value&name1=value1
289 |
290 | var src_string = Encoding.ASCII.GetString(body);
291 | var kvs = src_string.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
292 |
293 | foreach (var kv in kvs)
294 | {
295 |
296 | var pos = kv.IndexOf('=');
297 | if (pos < 1) continue;
298 |
299 | var key = kv.Substring(0, pos).Trim();
300 | if (string.IsNullOrEmpty(key)) continue;
301 |
302 | var val = kv.Substring(pos + 1).Trim();
303 | if (string.IsNullOrEmpty(val) == false && val.IndexOf("%") != -1) val = UrlHelper.UrlDecode(val, charset);
304 |
305 | var content = new FormItem { FieldName = key, TextValue = val };
306 | _dictionary.Add(key, content);
307 | }
308 | }
309 |
310 |
311 |
312 |
313 | private static readonly byte[] _byt_crlf2 = new byte[] { 0x0d, 0x0a, 0x0d, 0x0a };
314 |
315 | ///
316 | /// 查找第一个双回车
317 | ///
318 | ///
319 | ///
320 | private static unsafe int FindCrlf2(byte[] byts)
321 | {
322 | if (byts.Length < 4) return -1;
323 | fixed (byte* pcr = _byt_crlf2, bp = byts)
324 | {
325 | var v1 = *(int*)pcr;
326 | for (var i = 0; i < byts.Length - 3; i++)
327 | {
328 | var v2 = *(int*)(bp + i);
329 | if (v1 == v2) return i;
330 | }
331 | }
332 | return -1;
333 | }
334 |
335 |
336 |
337 |
338 |
339 | }
340 |
341 |
342 |
343 | }
344 |
--------------------------------------------------------------------------------
/FastWebApi/HttpSession.cs:
--------------------------------------------------------------------------------
1 |
2 | #region
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Collections.Concurrent;
7 | using Microsoft.Owin;
8 | using System.Timers;
9 |
10 | #endregion
11 |
12 |
13 |
14 | namespace TinyFox.FastWebApi
15 | {
16 |
17 | ///
18 | /// 简易Session管理器
19 | ///
20 | public class HttpSession
21 | {
22 |
23 | ///
24 | /// 用户Session节点
25 | ///
26 | private sealed class SessionItem
27 | {
28 |
29 | ///
30 | /// 上次访问的时间截(单位秒)
31 | ///
32 | public long LastReadWriteTime = 0;
33 |
34 | ///
35 | /// 对象池
36 | ///
37 | public Dictionary SessionValues = new Dictionary();
38 | }
39 |
40 |
41 | ///
42 | /// 全站SESSION池
43 | ///
44 | private static readonly ConcurrentDictionary _SessionItemPool = new ConcurrentDictionary();
45 |
46 |
47 | ///
48 | /// session id 关键字名称
49 | ///
50 | private const string SessionId_KeyName = "OWINSESSIONID";
51 |
52 |
53 |
54 |
55 |
56 | ///
57 | /// 会话上下文
58 | ///
59 | private IOwinContext _contenxt;
60 |
61 |
62 | ///
63 | /// 用户session id编号
64 | ///
65 | private string _sessionid = null;
66 |
67 | ///
68 | /// 用户的session节点
69 | ///
70 | private SessionItem _sessionItem;
71 |
72 |
73 |
74 | ///
75 | /// 静态构造函数
76 | ///
77 | static HttpSession()
78 | {
79 | //创建定时器,定时清理池中的超时节点
80 | var timer = new Timer { Interval = 1000 * 10 };
81 | timer.Elapsed += TimerElapsed;
82 | timer.Start();
83 | return;
84 |
85 | void TimerElapsed(object sender, ElapsedEventArgs e)
86 | {
87 |
88 | if (_SessionItemPool.Count < 1) return;
89 | var items = _SessionItemPool.ToArray();
90 | if (items == null) return;
91 |
92 | var now = DateTime.Now.Ticks / 10000 / 1000;
93 | foreach (var item in items)
94 | {
95 |
96 | var key = item.Key;
97 | var tim = item.Value == null ? 0 : item.Value.LastReadWriteTime;
98 |
99 | if (now - tim < 60 * 35) continue;
100 | _SessionItemPool.TryRemove(key, out SessionItem tmp);
101 | }
102 | }
103 |
104 | }
105 |
106 |
107 | ///
108 | /// 实例构造函数
109 | ///
110 | ///
111 | public HttpSession(IOwinContext context)
112 | {
113 |
114 | _contenxt = context;
115 |
116 |
117 | //获取SessionId
118 | var cookies = context.Request.Cookies;
119 | var id = "";
120 | foreach (var cookie in cookies)
121 | {
122 | if (cookie.Key == SessionId_KeyName)
123 | {
124 | id = cookie.Value;
125 | break;
126 | }
127 | }
128 |
129 | //如果有SessionId, 就从池中取出用户Session节点
130 | if (!string.IsNullOrEmpty(id))
131 | {
132 | if (_SessionItemPool.TryGetValue(id, out SessionItem item))
133 | {
134 | var now = DateTime.Now.Ticks / 10000 / 1000;
135 |
136 | if (now - item.LastReadWriteTime > 60 * 30)
137 | {
138 | //30分钟过期
139 | _SessionItemPool.TryRemove(id, out SessionItem tmp);
140 | }
141 | else
142 | {
143 | //如果没有过期
144 | _sessionid = id;
145 | _sessionItem = item;
146 | }
147 | }
148 | else
149 | {
150 | //如果没有从池中找出来
151 | //。。。。。
152 | }
153 | }
154 |
155 | }
156 |
157 |
158 |
159 | ///
160 | /// 获取或设置指定关键字的Session记录
161 | ///
162 | /// 关键字
163 | ///
164 | public object this[string key]
165 | {
166 | get
167 | {
168 | if (string.IsNullOrEmpty(key) || _sessionItem == null) return null;
169 | var u_dict = _sessionItem.SessionValues;
170 | _sessionItem.LastReadWriteTime = DateTime.Now.Ticks / 10000 / 1000;
171 | if (!u_dict.TryGetValue(key, out object ov)) return null;
172 | return ov;
173 | }
174 |
175 | set
176 | {
177 | //关键字不能为空
178 | if (string.IsNullOrEmpty(key)) return;
179 |
180 | //如果还没有session节点
181 | if (string.IsNullOrEmpty(_sessionid) || _sessionItem == null)
182 | {
183 | _sessionid = DateTime.Now.Ticks.ToString("X"); //警告:这种算法生成的Key可能会出现不唯一的情况
184 | _contenxt.Response.Cookies.Append(SessionId_KeyName, _sessionid, new CookieOptions { Path = "/" });
185 | var item = new SessionItem { LastReadWriteTime = DateTime.Now.Ticks / 10000 / 1000 };
186 | _SessionItemPool[_sessionid] = item;
187 | _sessionItem = item;
188 |
189 | }
190 |
191 | _sessionItem.LastReadWriteTime = DateTime.Now.Ticks / 10000 / 1000;
192 | _sessionItem.SessionValues[key] = value;
193 |
194 | }
195 | }
196 |
197 |
198 |
199 | ///
200 | /// 移除该用户的一个Session记录
201 | ///
202 | ///
203 | public void Remove(string key)
204 | {
205 | if (_sessionItem == null) return;
206 | _sessionItem.SessionValues.Remove(key);
207 | }
208 |
209 |
210 | ///
211 | /// 清空用户Session
212 | ///
213 | public void RemoveAll()
214 | {
215 | //session id 不能为空
216 | if (string.IsNullOrEmpty(_sessionid))
217 | {
218 | _sessionItem = null;
219 | return;
220 | }
221 |
222 | //从池中移除该用户的记录
223 | _SessionItemPool.TryRemove(_sessionid, out SessionItem tmp);
224 |
225 | //移动用户节点镜像
226 | _sessionItem = null;
227 |
228 | //移除SessionId
229 | _sessionid = null;
230 | }
231 |
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/FastWebApi/IWebApiRouter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Owin;
2 |
3 | namespace TinyFox.FastWebApi
4 | {
5 | ///
6 | /// 路由配置接口类
7 | ///
8 | public interface IWebApiRouter
9 | {
10 | BaseWebApi RouteTo(IOwinContext c);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/FastWebApi/JwsIntegration.cs:
--------------------------------------------------------------------------------
1 |
2 | #region
3 |
4 | using System.Net;
5 | using System.Threading.Tasks;
6 | using Microsoft.Owin;
7 | using Owin;
8 |
9 | #endregion
10 |
11 |
12 |
13 | namespace TinyFox.FastWebApi
14 | {
15 |
16 | ///
17 | /// 用于获取用户端IP地址等预处理工作的中间件
18 | ///
19 | public class JwsIntegration : OwinMiddleware
20 | {
21 |
22 | ///
23 | /// 下一个“中间件”对象
24 | ///
25 | readonly OwinMiddleware _next;
26 |
27 | ///
28 | /// 构造函数,第一个参数必须为 OwinMiddleware对象
29 | ///
30 | /// 下一个中间件
31 | public JwsIntegration(OwinMiddleware next) : base(next)
32 | {
33 | _next = next;
34 | }
35 |
36 |
37 | ///
38 | /// 处理用户请求的具体方法(该方法是中间件必须的实现的方法)
39 | ///
40 | /// OwinContext对象
41 | ///
42 | public override Task Invoke(IOwinContext owinContext)
43 | {
44 |
45 | var headers = owinContext.Request.Headers;
46 |
47 | try
48 | {
49 | //解析访问者IP地址和端口号
50 | if (headers != null && headers.ContainsKey("X-Original-For"))
51 | {
52 | var ipaddAdndPort = headers["X-Original-For"];
53 | var colon = ipaddAdndPort.LastIndexOf(":");
54 | var ip = ipaddAdndPort;
55 | var port = 0;
56 |
57 | if (colon > 0)
58 | {
59 | ip = ipaddAdndPort.Substring(0, colon).Trim(new[] { '[', ']', '\x20' });
60 | port = int.Parse(ipaddAdndPort.Substring(colon + 1));
61 | }
62 |
63 | owinContext.Request.RemoteIpAddress = IPAddress.Parse(ip).ToString();
64 | if (port != 0) owinContext.Request.RemotePort = port;
65 | }
66 |
67 | //处理HTTP/HTTPS协议标记
68 | if (headers != null && headers.ContainsKey("X-Original-Proto"))
69 | {
70 | owinContext.Request.Scheme = headers["X-Original-Proto"];
71 | }
72 | }
73 | catch { }
74 |
75 | return _next.Invoke(owinContext);
76 | }
77 |
78 |
79 |
80 | } //end call mymiddleware
81 |
82 |
83 |
84 |
85 | ///
86 | /// 这个类是为AppBuilder添加一个名叫UseMyApp的扩展方法,目的是方便用户调用
87 | ///
88 | public static class FastWebApiExtension1
89 | {
90 | ///
91 | /// 启用 JWS OWIN预处理中间件
92 | ///
93 | ///
94 | ///
95 | public static IAppBuilder UseJwsIntegration(this IAppBuilder builder)
96 | {
97 | return builder.Use();
98 | }
99 |
100 | }
101 |
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/FastWebApi/TinyFox.FastWebApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net462; netstandard2.0
5 | TinyFox.FastWebApi
6 | TinyFox.FastWebApi
7 | true
8 | www.linuxdot.net
9 |
10 | LiuBing
11 | FastWebApi For TinyFox
12 | www.linuxdot.net
13 | 1.0.5
14 | https://github.com/yunekit/TinyFox.FastWebApi
15 | MIT
16 | TinyFox, OWIN, WebApi
17 |
18 |
19 |
20 |
21 | true
22 |
23 |
24 |
25 | true
26 |
27 |
28 |
29 |
30 |
31 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/FastWebApi/UrlHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | using System.IO;
6 |
7 |
8 | namespace TinyFox.FastWebApi
9 | {
10 |
11 | ///
12 | /// URL编/解码助手
13 | ///
14 | public class UrlHelper
15 | {
16 |
17 | public static string UrlDecode(string str)
18 | {
19 | return UrlDecode(str, Encoding.UTF8);
20 | }
21 |
22 | static char[] GetChars(MemoryStream b, Encoding e)
23 | {
24 | return e.GetChars(b.GetBuffer(), 0, (int)b.Length);
25 | }
26 |
27 | static void WriteCharBytes(IList buf, char ch, Encoding e)
28 | {
29 | if (ch > 255)
30 | {
31 | foreach (byte b in e.GetBytes(new char[] { ch }))
32 | buf.Add(b);
33 | }
34 | else
35 | buf.Add((byte)ch);
36 | }
37 |
38 |
39 | ///
40 | /// 解码
41 | ///
42 | ///
43 | ///
44 | ///
45 | public static string UrlDecode(string s, Encoding e)
46 | {
47 | if (null == s) return null;
48 |
49 | if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
50 | return s;
51 |
52 | if (e == null) e = Encoding.UTF8;
53 |
54 | long len = s.Length;
55 | var bytes = new List();
56 | int xchar;
57 | char ch;
58 |
59 | for (int i = 0; i < len; i++)
60 | {
61 | ch = s[i];
62 | if (ch == '%' && i + 2 < len && s[i + 1] != '%')
63 | {
64 | if (s[i + 1] == 'u' && i + 5 < len)
65 | {
66 | // unicode hex sequence
67 | xchar = GetChar(s, i + 2, 4);
68 | if (xchar != -1)
69 | {
70 | WriteCharBytes(bytes, (char)xchar, e);
71 | i += 5;
72 | }
73 | else
74 | WriteCharBytes(bytes, '%', e);
75 | }
76 | else if ((xchar = GetChar(s, i + 1, 2)) != -1)
77 | {
78 | WriteCharBytes(bytes, (char)xchar, e);
79 | i += 2;
80 | }
81 | else
82 | {
83 | WriteCharBytes(bytes, '%', e);
84 | }
85 | continue;
86 | }
87 |
88 | if (ch == '+')
89 | WriteCharBytes(bytes, ' ', e);
90 | else
91 | WriteCharBytes(bytes, ch, e);
92 | }
93 |
94 | byte[] buf = bytes.ToArray();
95 | bytes = null;
96 | return e.GetString(buf);
97 |
98 | }
99 |
100 | public static string UrlDecode(byte[] bytes, Encoding e)
101 | {
102 | if (bytes == null) return null;
103 |
104 | return UrlDecode(bytes, 0, bytes.Length, e);
105 | }
106 |
107 |
108 | static int GetInt(byte b)
109 | {
110 | char c = (char)b;
111 | if (c >= '0' && c <= '9')
112 | return c - '0';
113 |
114 | if (c >= 'a' && c <= 'f')
115 | return c - 'a' + 10;
116 |
117 | if (c >= 'A' && c <= 'F')
118 | return c - 'A' + 10;
119 |
120 | return -1;
121 | }
122 |
123 | static int GetChar(byte[] bytes, int offset, int length)
124 | {
125 | int value = 0;
126 | int end = length + offset;
127 | for (int i = offset; i < end; i++)
128 | {
129 | int current = GetInt(bytes[i]);
130 | if (current == -1)
131 | return -1;
132 | value = (value << 4) + current;
133 | }
134 |
135 | return value;
136 | }
137 |
138 | static int GetChar(string str, int offset, int length)
139 | {
140 | int val = 0;
141 | int end = length + offset;
142 | for (int i = offset; i < end; i++)
143 | {
144 | char c = str[i];
145 | if (c > 127)
146 | return -1;
147 |
148 | int current = GetInt((byte)c);
149 | if (current == -1)
150 | return -1;
151 | val = (val << 4) + current;
152 | }
153 |
154 | return val;
155 | }
156 |
157 | ///
158 | /// 解码
159 | ///
160 | ///
161 | ///
162 | ///
163 | ///
164 | ///
165 | public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e)
166 | {
167 | if (bytes == null)
168 | return null;
169 | if (count == 0)
170 | return string.Empty;
171 |
172 | if (bytes == null)
173 | throw new ArgumentNullException("bytes");
174 |
175 | if (offset < 0 || offset > bytes.Length)
176 | throw new ArgumentOutOfRangeException("offset");
177 |
178 | if (count < 0 || offset + count > bytes.Length)
179 | throw new ArgumentOutOfRangeException("count");
180 |
181 | StringBuilder output = new StringBuilder();
182 | MemoryStream acc = new MemoryStream();
183 |
184 | int end = count + offset;
185 | int xchar;
186 | for (int i = offset; i < end; i++)
187 | {
188 | if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%')
189 | {
190 | if (bytes[i + 1] == (byte)'u' && i + 5 < end)
191 | {
192 | if (acc.Length > 0)
193 | {
194 | output.Append(GetChars(acc, e));
195 | acc.SetLength(0);
196 | }
197 | xchar = GetChar(bytes, i + 2, 4);
198 | if (xchar != -1)
199 | {
200 | output.Append((char)xchar);
201 | i += 5;
202 | continue;
203 | }
204 | }
205 | else if ((xchar = GetChar(bytes, i + 1, 2)) != -1)
206 | {
207 | acc.WriteByte((byte)xchar);
208 | i += 2;
209 | continue;
210 | }
211 | }
212 |
213 | if (acc.Length > 0)
214 | {
215 | output.Append(GetChars(acc, e));
216 | acc.SetLength(0);
217 | }
218 |
219 | if (bytes[i] == '+')
220 | {
221 | output.Append(' ');
222 | }
223 | else
224 | {
225 | output.Append((char)bytes[i]);
226 | }
227 | }
228 |
229 | if (acc.Length > 0)
230 | {
231 | output.Append(GetChars(acc, e));
232 | }
233 |
234 | return output.ToString();
235 | }
236 |
237 |
238 |
239 |
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/FastWebApi/WebApiMiddleware.cs:
--------------------------------------------------------------------------------
1 |
2 | /***********************************************************
3 | * 作用:演示开发一个 MS OWIN Middleware 中间件的基本方法
4 | * **********************************************************/
5 |
6 |
7 | #region
8 |
9 | using System.Threading.Tasks;
10 | using Microsoft.Owin;
11 | using Owin;
12 |
13 | #endregion
14 |
15 |
16 |
17 | namespace TinyFox.FastWebApi
18 | {
19 |
20 | ///
21 | /// Fast WebApi 中间件
22 | ///
23 | public class WebApiMiddleware : OwinMiddleware
24 | {
25 |
26 | ///
27 | /// 下一个“中间件”对象
28 | ///
29 | readonly OwinMiddleware _next;
30 |
31 |
32 | ///
33 | /// 路由类
34 | ///
35 | readonly IWebApiRouter _router;
36 |
37 | ///
38 | /// 构造函数,第一个参数必须为 OwinMiddleware对象
39 | ///
40 | /// 下一个中间件
41 | public WebApiMiddleware(OwinMiddleware next, IWebApiRouter router) : base(next)
42 | {
43 | _router = router;
44 | _next = next;
45 | }
46 |
47 |
48 | ///
49 | /// 处理用户请求的具体方法(该方法是中间件必须的实现的方法)
50 | ///
51 | /// OwinContext对象
52 | ///
53 | public override Task Invoke(IOwinContext c)
54 | {
55 | var app = _router.RouteTo(c);
56 | if (app == null) return _next.Invoke(c);
57 |
58 | return app.ProcessRequest(c);
59 | }
60 |
61 |
62 |
63 | } //end call mymiddleware
64 |
65 |
66 |
67 |
68 | ///
69 | /// 这个类是为AppBuilder添加一个名叫UseMyApp的扩展方法,目的是方便用户调用
70 | ///
71 | public static class FastWebApiExtension
72 | {
73 | ///
74 | /// 启用FastApi处理框架
75 | ///
76 | ///
77 | ///
78 | ///
79 | public static IAppBuilder UseFastWebApi(this IAppBuilder builder, IWebApiRouter router)
80 | {
81 | return builder.Use(router);
82 | }
83 |
84 | }
85 |
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 jws-admin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TinyFox.FastWebApi
2 | .NET WEBAPI FRAMEWORK FOR TINYFOX OR ANY OWIN HOST SERVER.
3 |
4 | 为 TinyFox 准备的轻量级 WebApi 应用框架,该框架也支持其它所有的符合 OWIN 1.0/1.1 标准的 OWIN 宿主服务器。
5 |
--------------------------------------------------------------------------------
/TinyFox.FastWebApi.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2035
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiTest", "WebApiTest\WebApiTest.csproj", "{B94BA8DF-BE00-4C16-ADA0-7BF4EB1AC2FD}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TinyFox.FastWebApi", "FastWebApi\TinyFox.FastWebApi.csproj", "{F9367B19-874E-4049-9CBF-A631DB9B0BBB}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {B94BA8DF-BE00-4C16-ADA0-7BF4EB1AC2FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {B94BA8DF-BE00-4C16-ADA0-7BF4EB1AC2FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {B94BA8DF-BE00-4C16-ADA0-7BF4EB1AC2FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {B94BA8DF-BE00-4C16-ADA0-7BF4EB1AC2FD}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {F9367B19-874E-4049-9CBF-A631DB9B0BBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {F9367B19-874E-4049-9CBF-A631DB9B0BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {F9367B19-874E-4049-9CBF-A631DB9B0BBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {F9367B19-874E-4049-9CBF-A631DB9B0BBB}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3D86C35C-EA7F-42C3-B500-8233C595B0C4}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/WebApiTest/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/WebApiTest/MyApp/MyApp1.cs:
--------------------------------------------------------------------------------
1 | using TinyFox.FastWebApi;
2 |
3 |
4 | namespace WebApiTest.MyApp
5 | {
6 | ///
7 | /// 处理某种请求的示例1
8 | ///
9 | class MyApp1: BaseWebApi
10 | {
11 |
12 |
13 | ///
14 | /// 处理应用逻辑
15 | ///
16 | protected override async void ProcessRequest()
17 | {
18 |
19 | //定义返回的数据类型:文本
20 | Response.ContentType = "text/plain";
21 | //发送文本内容
22 | //Response.Write("this is App1");
23 | await Response.WriteAsync("This is app1");
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/WebApiTest/MyApp/MyApp2.cs:
--------------------------------------------------------------------------------
1 |
2 | using TinyFox.FastWebApi;
3 |
4 | namespace WebApiTest.MyApp
5 | {
6 | ///
7 | /// 处理某个请求
8 | ///
9 | class MyApp2 : BaseWebApi
10 | {
11 |
12 | ///
13 | /// 处理应用逻辑
14 | ///
15 | protected override void ProcessRequest()
16 | {
17 | //除 GET 方法外,禁止访问
18 | if (Request.Method != "GET") {
19 | Response.StatusCode = 403;
20 | Response.ContentType = "text/html";
21 |
22 | var htm = "NONONONONONONONONONONO....,
";
23 | Response.Write(htm);
24 | }
25 |
26 |
27 | //内容属性(mime类型)
28 | Response.ContentType = "application/json";
29 |
30 | //组织内容
31 | var jsion_txt = "{ \"firstName\":\"John\"}";
32 |
33 | // 注:
34 | // 在实际应用中,一般都是 Newtonsoeft.Json 等工具类将某个对象序列化为JSON文本,然后发送回客户端
35 |
36 |
37 | //输出内容
38 | Response.Write(jsion_txt);
39 |
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/WebApiTest/MyWebApiRouter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Owin;
2 | using TinyFox.FastWebApi;
3 |
4 | namespace WebApiTest
5 | {
6 |
7 | ///
8 | /// 路由器:自定义URL路由规则
9 | ///
10 | public class MyWebApiRouter : IWebApiRouter
11 | {
12 | ///
13 | /// 返回一个处理对象,如果当前请求不在处理之列则返回空
14 | ///
15 | ///
16 | ///
17 | public BaseWebApi RouteTo(IOwinContext c)
18 | {
19 |
20 | //用户请求的URL路径
21 | var path = c.Request.Path.Value;
22 |
23 | //将不同的请求路径交给不同的处理模块处理
24 | if (path == "/app1" || path.StartsWith("/app1/")) return new MyApp.MyApp1();
25 | if (path == "/app2" || path.StartsWith("/app2/")) return new MyApp.MyApp2();
26 |
27 |
28 |
29 |
30 |
31 | //根据其它特征处理
32 | // if(c.Request.Method=="POST") return ......
33 | // if (c.Request.Host.Value=="www.google.com") return .....
34 |
35 | //返回空表示该请求不属于本webapi处理范围
36 | //将以404(找不到网页)的http状态码应答对方
37 | return null;
38 |
39 | }
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/WebApiTest/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using TinyFox;
4 |
5 | namespace WebApiTest
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 |
12 | //设置端口(默认端口为8088)
13 | TinyFoxService.Port = 8080;
14 | //设置网站文件夹路路径(默认本exe所在程序下的 wwwroot 目录)
15 | //TinyFoxService.WebRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
16 |
17 | //启动(非阻塞)
18 | TinyFoxService.Start((new Startup()).OwinMain);
19 |
20 |
21 | //提示并等候终止
22 | Console.WriteLine("按 CTRL+C 退出程序");
23 | (new AutoResetEvent(false)).WaitOne();
24 |
25 | //停止OWIN服务
26 | TinyFoxService.Stop();
27 |
28 |
29 | }
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/WebApiTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 有关程序集的一般信息由以下
6 | // 控制。更改这些特性值可修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("WebApiTest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WebApiTest")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 将 ComVisible 设置为 false 会使此程序集中的类型
18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
19 | //请将此类型的 ComVisible 特性设置为 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
23 | [assembly: Guid("b94ba8df-be00-4c16-ada0-7bf4eb1ac2fd")]
24 |
25 | // 程序集的版本信息由下列四个值组成:
26 | //
27 | // 主版本
28 | // 次版本
29 | // 生成号
30 | // 修订号
31 | //
32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
33 | // 方法是按如下所示使用“*”: :
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/WebApiTest/Startup.cs:
--------------------------------------------------------------------------------
1 | /************************************************************
2 | * 这是 Microsoft.Owin.dll 框架的启动类,必须的。
3 | * **********************************************************/
4 |
5 |
6 |
7 | #region
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Threading.Tasks;
12 | using Owin;
13 | using Microsoft.Owin.Builder;
14 | using TinyFox.FastWebApi;
15 | using System.Text;
16 |
17 | #endregion
18 |
19 |
20 |
21 | namespace WebApiTest
22 | {
23 |
24 |
25 | ///
26 | /// OWIN起始类
27 | ///
28 | class Startup
29 | {
30 | ///
31 | /// Microsoft.Owin处理管线的入口函数
32 | ///
33 | static Func, Task> _owinAppFunc;
34 |
35 |
36 | ///
37 | /// Startup类的构造函数
38 | ///
39 | public Startup() {
40 | var builder = new AppBuilder();
41 | Configuration(builder);
42 | _owinAppFunc = builder.Build();
43 | }
44 |
45 |
46 | ///
47 | /// *** JWS或TinyFox所需要的关键函数 ***
48 | /// 每个请求到来,JWS/TinyFox都把请求打包成字典,通过这个函数提供给本应用
49 | ///
50 | /// 新请求的环境字典
51 | /// 返回一个正在运行或已经完成的任务
52 | public Task OwinMain(IDictionary env)
53 | {
54 | return _owinAppFunc != null ? _owinAppFunc(env) : null;
55 | }
56 |
57 |
58 |
59 | ///
60 | /// 启动类的配置方法,格式是 Microsoft.Owin 所要求的
61 | ///
62 | /// App生成器
63 | void Configuration(IAppBuilder builder)
64 | {
65 |
66 | // 添加预处理中间件,放在第一位置
67 | ////////////////////////////////////////
68 | builder.UseJwsIntegration();
69 |
70 |
71 | // 添加FastWebApi中间件,具体实现,在WebApiMiddleware.cs文件中
72 | ///////////////////////////////////////////////////////////////////////////
73 | builder.UseFastWebApi(new MyWebApiRouter());
74 |
75 |
76 |
77 |
78 | // 放在处理链中最后执行的方法(相当于前一个中间件的next对象的Invoke方法)
79 | // 如果前边的中间件已经成功处理所有的请求,那么处理过程就不会流转到这个位置
80 | ////////////////////////////////////////////////////////////////////////////
81 | builder.Run(c =>
82 | {
83 |
84 | //前边没有处理,意味着找不到网页,所有返回404
85 |
86 | var text = "" +
87 | "" +
88 | string.Format("Can't Found Path: {0}
", c.Request.Path) +
89 | "" +
90 | "";
91 |
92 | c.Response.StatusCode = 404;
93 | c.Response.Write(Encoding.ASCII.GetBytes(text));
94 |
95 | return Task.FromResult(0);
96 | });
97 |
98 | }
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | }
110 |
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/WebApiTest/WebApiTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {B94BA8DF-BE00-4C16-ADA0-7BF4EB1AC2FD}
8 | Exe
9 | WebApiTest
10 | WebApiTest
11 | v4.8
12 | 512
13 | false
14 | true
15 | false
16 | publish\
17 | true
18 | Disk
19 | false
20 | Foreground
21 | 7
22 | Days
23 | false
24 | false
25 | true
26 | Owin.FastWebApi
27 | liubing
28 | 1
29 | 1.0.0.%2a
30 | false
31 | true
32 |
33 |
34 |
35 | AnyCPU
36 | true
37 | full
38 | false
39 | bin\Debug\
40 | DEBUG;TRACE
41 | prompt
42 | 4
43 |
44 |
45 | AnyCPU
46 | pdbonly
47 | true
48 | bin\Release\
49 | TRACE
50 | prompt
51 | 4
52 |
53 |
54 | 9494A9738E9C6A1BBED582EEA1D9C56C0A9F08A4
55 |
56 |
57 | true
58 |
59 |
60 | true
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | ..\packages\TinyFox.4.0.1.29\lib\net451\TinyFox.dll
73 |
74 |
75 | ..\packages\TinyFox.Microsoft.Owin.4.0.1\lib\net451\TinyFox.Microsoft.Owin.dll
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | False
93 | Microsoft .NET Framework 4.6.1 %28x86 和 x64%29
94 | true
95 |
96 |
97 | False
98 | .NET Framework 3.5 SP1
99 | false
100 |
101 |
102 |
103 |
104 | {f9367b19-874e-4049-9cbf-a631db9b0bbb}
105 | TinyFox.FastWebApi
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/WebApiTest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------