.net mvc 站点自带简易SSL加密传输
因项目需要,传输数据需要加密,因此有了一些经验,现简易抽出来分享!
请求:
前端cryptojs用rsa/aes 或 rsa/des加密,后端.net 解密
返回
后端.net用rsa/aes 或 rsa/des加密,前端cryptojs解密
图示:
数据发送加密:
返回数据加密:
开源代码分享:https://github.com/guandy/NetSSL
现只是简易抽出,如果后续需求量大可考虑做成组件
Word报告自动生成(例如 导出数据库结构)
将很早之前写的一个小组件重新整理优化一下,做成一个通用的功能。适用于导出数据库的结构(表、字段等)到Word或将体检数据自动生成Word版的体检报告等。代码:Github
一、主要需要完成功能:
1. 灵活的配置规则及word样式设置(文本、表格、图表、颜色等).
2. 支持表格.
3. 支持图表.
4. 支持章节内容循环生成.
5. 支持目录.
6.支持文档结构图
7.更新指定位置的文字
8.支持pdf导出.
最后结果如下:
图一
图二
图三
二、需求分析与实现方式
功能主要涉及3个比较重要的部分:数据源、Word样式、配置规则。
为了简单,数据源决定采用一个存储过程返回Dataset的方式, 整张报告的数据来源于此Dataset的多个Datatable.
样式与配置:首先想到的是写一个config文件,所有配置都放到一个文件里,然后将数据按照这个规则生成word。但无疑这样的配置项太多了,关键是“样式”问题,比如字体、颜色、表格宽度.....想想就头大。而且没有“所见即所得”的效果,配置完都不知道啥样。
后来决定采取修改的方式, 先以一个word文件作为模板,在模板中定义好上面提到的“样式”,然后在模板中做一个个标记,然后将数据按照规则更新到对应的标记。
图四
而这个标记我们用到了word的一个叫【书签】的功能,打开word按ctrl+shift+F5, 打开书签功能,如下图:
图五
这样将【规则】通过一系列规则的【书签】定义到word模板中。
三、规则配置
思路确定了,那就开始设计如何通过【书签】将规则定义到word模板中去,这里决定将所有规则都通过【书签】实现,而放弃config文件的方式,这个更统一而且直观一些。
A.循环
以图四为例,数据库有多少张表是不固定的,我们在制作模板的时候不可能先画好N(N为表的总数)个表格等待数据填充, 这里就会需要遍历数据源中提供的所有表结构数据,然后逐一形成表格。这里就需要将图四中的表格循环一下,自动复制生成多个这样的表格。当然,这只是一种情况,还有可能会出现循环嵌套循环的情况,那么我将这个循环定义成一个书签的时候按照这样的格式:
loop_级别_表序号_filter_名称
含义如下:
loop:代表这是一个循环。
级别:默认文档级别为0,出现的第一层循环为1,其内部若再次嵌套循环则级别为2,依次类推。
表序号:取Dataset中的第几张表(从1开始)
filter:循环的时候可能会用到对datatable的查找过滤,在此写出,多个字段用XX隔开(因为此处不允许有下划线外其他特殊字符, 就用这个XX吧 )
名称:loop名称,方便与其他 loop区别
B.更新指定位置的文字
如图四中的【服务器名】、【表总数】等,只需要替换对应的文字即可:
label_级别_名称
含义如下:
label:代表这是一个label。
级别:默认文档级别为0,出现的第一层循环为1,其内部若再次嵌套循环则级别为2,依次类推。
名称:label名称
注意这里省略了表序号,当级别为0的时候 ,自动取最后一个datatable中的数据,因为这个label经常会用到其他表汇总的数据,可能会用到之前几张表的数据,所以放在其他表都处理好后。当级别为1的时候,自然取该级别循环的行数据。
C.表格
表格的配置原本也想对表格添加书签,后来发现有个表格属性, 觉得写在这里更好一些。
如上图所示,【标题】格式为:table_级别_取Dataset中的第几张表(从1开始)_filter字段多个用XX隔开(此处不允许有下划线外其他特殊字符, 就用这个XX吧 )_名称
【说明】为可选项,若需要合计行, 则需要标识, summary或缩写s: [合计]行是模板中表格的第几行 summaryfilter或缩写sf:数据集进一步filter到summary行的条件(因为一个表格只取一个Datatable,通过一个标识指定了哪些datarow是用来作为合计的)
D.图表
同样为了方便将配置写在了【标题】,图表生成后会将名称修改过来。
配置格式为:chart_级别_取Dataset中的第几张表(从1开始)_filter字段多个用XX隔开(此处不允许有下划线外其他特殊字符, 就用这个XX吧 )_chart名称_是否将Datatable的columnName作为第一行_从datatable第几列开始(列起始为1)_截止列,
如下图所示配置即可。
E.目录
无需标识, 模板中添加目录, 当内容处理完成之后, 会根据处理后的结果动态更新目录.
四、主要代码
1 using System;
2 using System.Collections.Generic;
3 using System.Data;
4 using System.Diagnostics;
5 using System.IO;
6 using System.Linq;
7 using System.Reflection;
8 using Excel = Microsoft.Office.Interop.Excel;
9 using Word = Microsoft.Office.Interop.Word;
10
11 namespace FlyLolo.WordReport.Demo
12 {
13 public class WordReportHelper
14 {
15 private Word.Application wordApp = null;
16 private Word.Document wordDoc = null;
17 private DataSet dataSource = null;
18 private object line = Word.WdUnits.wdLine;
19 private string errorMsg = "";
20
21 /// <summary>
22 /// 根据模板文件,创建数据报告
23 /// </summary>
24 /// <param name="templateFile">模板文件名(含路径)</param>
25 /// <param name="newFilePath">新文件路径)</param>
26 /// <param name="dataSource">数据源,包含多个datatable</param>
27 /// <param name="saveFormat">新文件格式:</param>
28 public bool CreateReport(string templateFile, DataSet dataSource, out string errorMsg, string newFilePath, ref string newFileName, int saveFormat = 16)
29 {
30 this.dataSource = dataSource;
31 errorMsg = this.errorMsg;
32 bool rtn = OpenTemplate(templateFile)
33 && SetContent(new WordElement(wordDoc.Range(), dataRow: dataSource.Tables[dataSource.Tables.Count - 1].Rows[0]))
34 && UpdateTablesOfContents()
35 && SaveFile(newFilePath, ref newFileName, saveFormat);
36
37 CloseAndClear();
38 return rtn;
39 }
40
41 /// <summary>
42 /// 打开模板文件
43 /// </summary>
44 /// <param name="templateFile"></param>
45 /// <returns></returns>
46 private bool OpenTemplate(string templateFile)
47 {
48 if (!File.Exists(templateFile))
49 {
50 return false;
51 }
52
53 wordApp = new Word.Application();
54 wordApp.Visible = true;//使文档可见,调试用
55 wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
56 object file = templateFile;
57 wordDoc = wordApp.Documents.Open(ref file, ReadOnly: false);
58 return true;
59 }
60
61 /// <summary>
62 /// 为指定区域写入数据
63 /// </summary>
64 /// <param name="element"></param>
65 /// <returns></returns>
66 private bool SetContent(WordElement element)
67 {
68 string currBookMarkName = string.Empty;
69 string startWith = "loop_" + (element.Level + 1).ToString() + "_";
70 foreach (Word.Bookmark item in element.Range.Bookmarks)
71 {
72 currBookMarkName = item.Name;
73
74 if (currBookMarkName.StartsWith(startWith) && (!currBookMarkName.Equals(element.ElementName)))
75 {
76 SetLoop(new WordElement(item.Range, currBookMarkName, element.DataRow, element.GroupBy));
77 }
78
79 }
80
81 SetLabel(element);
82
83 SetTable(element);
84
85 SetChart(element);
86
87 return true;
88 }
89
90 /// <summary>
91 /// 处理循环
92 /// </summary>
93 /// <param name="element"></param>
94 /// <returns></returns>
95 private bool SetLoop(WordElement element)
96 {
97 DataRow[] dataRows = dataSource.Tables[element.TableIndex].Select(element.GroupByString);
98 int count = dataRows.Count();
99 element.Range.Select();
100
101 //第0行作为模板 先从1开始 循环后处理0行;
102 for (int i = 0; i < count; i++)
103 {
104
105 element.Range.Copy(); //模板loop复制
106 wordApp.Selection.InsertParagraphAfter();//换行 不会清除选中的内容,TypeParagraph 等同于回车,若当前有选中内容会被清除. TypeParagraph 会跳到下一行,InsertParagraphAfter不会, 所以movedown一下.
107 wordApp.Selection.MoveDown(ref line, Missing.Value, Missing.Value);
108 wordApp.Selection.Paste(); //换行后粘贴复制内容
109 int offset = wordApp.Selection.Range.End - element.Range.End; //计算偏移量
110
111 //复制书签,书签名 = 模板书签名 + 复制次数
112 foreach (Word.Bookmark subBook in element.Range.Bookmarks)
113 {
114 if (subBook.Name.Equals(element.ElementName))
115 {
116 continue;
117 }
118
119 wordApp.Selection.Bookmarks.Add(subBook.Name + "_" + i.ToString(), wordDoc.Range(subBook.Start + offset, subBook.End + offset));
120 }
121
122 SetContent(new WordElement(wordDoc.Range(wordApp.Selection.Range.End - (element.Range.End - element.Range.Start), wordApp.Selection.Range.End), element.ElementName + "_" + i.ToString(), dataRows[i], element.GroupBy));
123 }
124
125 element.Range.Delete();
126
127 return true;
128 }
129
130 /// <summary>
131 /// 处理简单Label
132 /// </summary>
133 /// <param name="element"></param>
134 /// <returns></returns>
135 private bool SetLabel(WordElement element)
136 {
137 if (element.Range.Bookmarks != null && element.Range.Bookmarks.Count > 0)
138 {
139 string startWith = "label_" + element.Level.ToString() + "_";
140 string bookMarkName = string.Empty;
141 foreach (Word.Bookmark item in element.Range.Bookmarks)
142 {
143 bookMarkName = item.Name;
144
145 if (bookMarkName.StartsWith(startWith))
146 {
147 bookMarkName = WordElement.GetName(bookMarkName);
148
149 item.Range.Text = element.DataRow[bookMarkName].ToString();
150 }
151 }
152 }
153
154 return true;
155 }
156
157 /// <summary>
158 /// 填充Table
159 /// </summary>
160 /// <param name="element"></param>
161 /// <returns></returns>
162 private bool SetTable(WordElement element)
163 {
164 if (element.Range.Tables != null && element.Range.Tables.Count > 0)
165 {
166 string startWith = "table_" + element.Level.ToString() + "_";
167 foreach (Word.Table table in element.Range.Tables)
168 {
169 if (!string.IsNullOrEmpty(table.Title) && table.Title.StartsWith(startWith))
170 {
171 WordElement tableElement = new WordElement(null, table.Title, element.DataRow);
172
173 TableConfig config = new TableConfig(table.Descr);
174
175 object dataRowTemplate = table.Rows[config.DataRow];
176 Word.Row SummaryRow = null;
177 DataRow SummaryDataRow = null;
178 DataTable dt = dataSource.Tables[tableElement.TableIndex];
179 DataRow[] dataRows = dataSource.Tables[tableElement.TableIndex].Select(tableElement.GroupByString); ;
180
181 if (config.SummaryRow > 0)
182 {
183 SummaryRow = table.Rows[config.SummaryRow];
184 SummaryDataRow = dt.Select(string.IsNullOrEmpty(tableElement.GroupByString) ? config.SummaryFilter : tableElement.GroupByString + " and " + config.SummaryFilter).FirstOrDefault();
185 }
186
187 foreach (DataRow row in dataRows)
188 {
189 if (row == SummaryDataRow)
190 {
191 continue;
192 }
193
194 Word.Row newRow = table.Rows.Add(ref dataRowTemplate);
195 for (int j = 0; j < table.Columns.Count; j++)
196 {
197 newRow.Cells[j + 1].Range.Text = row[j].ToString(); ;
198 }
199
200 }
201
202 ((Word.Row)dataRowTemplate).Delete();
203
204 if (config.SummaryRow > 0 && SummaryDataRow != null)
205 {
206 for (int j = 0; j < SummaryRow.Cells.Count; j++)
207 {
208 string temp = SummaryRow.Cells[j + 1].Range.Text.Trim().Replace("\r\a", "");
209
210 if (!string.IsNullOrEmpty(temp) && temp.Length > 2 && dt.Columns.Contains(temp.Substring(1, temp.Length - 2)))
211 {
212 SummaryRow.Cells[j + 1].Range.Text = SummaryDataRow[temp.Substring(1, temp.Length - 2)].ToString();
213 }
214 }
215 }
216
217 table.Title = tableElement.Name;
218 }
219
220
221 }
222 }
223
224 return true;
225 }
226
227 /// <summary>
228 /// 处理图表
229 /// </summary>
230 /// <param name="element"></param>
231 /// <returns></returns>
232 private bool SetChart(WordElement element)
233 {
234 if (element.Range.InlineShapes != null && element.Range.InlineShapes.Count > 0)
235 {
236 List<Word.InlineShape> chartList = element.Range.InlineShapes.Cast<Word.InlineShape>().Where(m => m.Type == Word.WdInlineShapeType.wdInlineShapeChart).ToList();
237 string startWith = "chart_" + element.Level.ToString() + "_";
238 foreach (Word.InlineShape item in chartList)
239 {
240 Word.Chart chart = item.Chart;
241 if (!string.IsNullOrEmpty(chart.ChartTitle.Text) && chart.ChartTitle.Text.StartsWith(startWith))
242 {
243 WordElement chartElement = new WordElement(null, chart.ChartTitle.Text, element.DataRow);
244
245 DataTable dataTable = dataSource.Tables[chartElement.TableIndex];
246 DataRow[] dataRows = dataTable.Select(chartElement.GroupByString);
247
248 int columnCount = dataTable.Columns.Count;
249 List<int> columns = new List<int>();
250
251 foreach (var dr in dataRows)
252 {
253 for (int i = chartElement.ColumnStart == -1 ? 0 : chartElement.ColumnStart - 1; i < (chartElement.ColumnEnd == -1 ? columnCount : chartElement.ColumnEnd); i++)
254 {
255 if (columns.Contains(i) || dr[i] == null || string.IsNullOrEmpty(dr[i].ToString()))
256 {
257
258 }
259 else
260 {
261 columns.Add(i);
262 }
263 }
264 }
265 columns.Sort();
266 columnCount = columns.Count;
267 int rowsCount = dataRows.Length;
268
269 Word.ChartData chartData = chart.ChartData;
270
271 //chartData.Activate();
272 //此处有个比较疑惑的问题, 不执行此条,生成的报告中的图表无法再次右键编辑数据. 执行后可以, 但有两个问题就是第一会弹出Excel框, 处理完后会自动关闭. 第二部分chart的数据range设置总不对
273 //不知道是不是版本的问题, 谁解决了分享一下,谢谢
274
275 Excel.Workbook dataWorkbook = (Excel.Workbook)chartData.Workbook;
276 dataWorkbook.Application.Visible = false;
277
278 Excel.Worksheet dataSheet = (Excel.Worksheet)dataWorkbook.Worksheets[1];
279 //设定范围
280 string a = (chartElement.ColumnNameForHead ? rowsCount + 1 : rowsCount) + "|" + columnCount;
281 Console.WriteLine(a);
282
283 Excel.Range tRange = dataSheet.Range["A1", dataSheet.Cells[(chartElement.ColumnNameForHead ? rowsCount + 1 : rowsCount), columnCount]];
284 Excel.ListObject tbl1 = dataSheet.ListObjects[1];
285 //dataSheet.ListObjects[1].Delete(); //想过重新删除再添加 这样 原有数据清掉了, 但觉得性能应该会有所下降
286 //Excel.ListObject tbl1 = dataSheet.ListObjects.AddEx();
287 tbl1.Resize(tRange);
288 for (int j = 0; j < rowsCount; j++)
289 {
290 DataRow row = dataRows[j];
291 for (int k = 0; k < columnCount; k++)
292 {
293 dataSheet.Cells[j + 2, k + 1].FormulaR1C1 = row[columns[k]];
294 }
295 }
296
297 if (chartElement.ColumnNameForHead)
298 {
299 for (int k = 0; k < columns.Count; k++)
300 {
301 dataSheet.Cells[1, k + 1].FormulaR1C1 = dataTable.Columns[columns[k]].ColumnName;
302 }
303 }
304 chart.ChartTitle.Text = chartElement.Name;
305 //dataSheet.Application.Quit();
306 }
307 }
308 }
309
310 return true;
311 }
312
313 /// <summary>
314 /// 更新目录
315 /// </summary>
316 /// <returns></returns>
317 private bool UpdateTablesOfContents()
318 {
319 foreach (Word.TableOfContents item in wordDoc.TablesOfContents)
320 {
321 item.Update();
322 }
323
324 return true;
325 }
326
327 /// <summary>
328 /// 保存文件
329 /// </summary>
330 /// <param name="newFilePath"></param>
331 /// <param name="newFileName"></param>
332 /// <param name="saveFormat"></param>
333 /// <returns></returns>
334 private bool SaveFile(string newFilePath, ref string newFileName, int saveFormat = 16)
335 {
336 if (string.IsNullOrEmpty(newFileName))
337 {
338 newFileName = DateTime.Now.ToString("yyyyMMddHHmmss");
339
340 switch (saveFormat)
341 {
342 case 0:// Word.WdSaveFormat.wdFormatDocument
343 newFileName += ".doc";
344 break;
345 case 16:// Word.WdSaveFormat.wdFormatDocumentDefault
346 newFileName += ".docx";
347 break;
348 case 17:// Word.WdSaveFormat.wdFormatPDF
349 newFileName += ".pdf";
350 break;
351 default:
352 break;
353 }
354 }
355
356 object newfile = Path.Combine(newFilePath, newFileName);
357 object wdSaveFormat = saveFormat;
358 wordDoc.SaveAs(ref newfile, ref wdSaveFormat);
359 return true;
360 }
361
362 /// <summary>
363 /// 清理
364 /// </summary>
365 private void CloseAndClear()
366 {
367 if (wordApp == null)
368 {
369 return;
370 }
371 wordDoc.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
372 wordApp.Quit(Word.WdSaveOptions.wdDoNotSaveChanges);
373 System.Runtime.InteropServices.Marshal.ReleaseComObject(wordDoc);
374 System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
375 wordDoc = null;
376 wordApp = null;
377 GC.Collect();
378 KillProcess("Excel", "WINWORD");
379 }
380
381 /// <summary>
382 /// 杀进程..
383 /// </summary>
384 /// <param name="processNames"></param>
385 private void KillProcess(params string[] processNames)
386 {
387 //Process myproc = new Process();
388 //得到所有打开的进程
389 try
390 {
391 foreach (string name in processNames)
392 {
393 foreach (Process thisproc in Process.GetProcessesByName(name))
394 {
395 if (!thisproc.CloseMainWindow())
396 {
397 if (thisproc != null)
398 thisproc.Kill();
399 }
400 }
401 }
402 }
403 catch (Exception)
404 {
405 //throw Exc;
406 // msg.Text+= "杀死" + processName + "失败!";
407 }
408 }
409 }
410
411 /// <summary>
412 /// 封装的Word元素
413 /// </summary>
414 public class WordElement
415 {
416 public WordElement(Word.Range range, string elementName = "", DataRow dataRow = null, Dictionary<string, string> groupBy = null, int tableIndex = 0)
417 {
418 this.Range = range;
419 this.ElementName = elementName;
420 this.GroupBy = groupBy;
421 this.DataRow = dataRow;
422 if (string.IsNullOrEmpty(elementName))
423 {
424 this.Level = 0;
425 this.TableIndex = tableIndex;
426 this.Name = string.Empty;
427 this.ColumnNameForHead = false;
428 }
429 else
430 {
431 string[] element = elementName.Split('_');
432 this.Level = int.Parse(element[1]);
433 this.ColumnNameForHead = false;
434 this.ColumnStart = -1;
435 this.ColumnEnd = -1;
436
437 if (element[0].Equals("label"))
438 {
439 this.Name = element[2];
440 this.TableIndex = 0;
441 }
442 else
443 {
444 this.Name = element[4];
445 this.TableIndex = int.Parse(element[2]) - 1;
446
447 if (!string.IsNullOrEmpty(element[3]))
448 {
449 string[] filters = element[3].Split(new string[] { "XX" }, StringSplitOptions.RemoveEmptyEntries);
450 if (this.GroupBy == null)
451 {
452 this.GroupBy = new Dictionary<string, string>();
453 }
454 foreach (string item in filters)
455 {
456 if (!this.GroupBy.Keys.Contains(item))
457 {
458 this.GroupBy.Add(item, dataRow[item].ToString());
459 }
460
461 }
462 }
463
464 if (element[0].Equals("chart") && element.Count() > 5)
465 {
466 this.ColumnNameForHead = element[5].Equals("1");
467 this.ColumnStart = string.IsNullOrEmpty(element[6]) ? -1 : int.Parse(element[6]);
468 this.ColumnEnd = string.IsNullOrEmpty(element[7]) ? -1 : int.Parse(element[7]);
469 }
470 }
471 }
472 }
473
474 public Word.Range Range { get; set; }
475 public int Level { get; set; }
476 public int TableIndex { get; set; }
477 public string ElementName { get; set; }
478
479 public DataRow DataRow { get; set; }
480 public Dictionary<string, string> GroupBy { get; set; }
481
482 public string Name { get; set; }
483
484 public bool ColumnNameForHead { get; set; }
485 public int ColumnStart { get; set; }
486 public int ColumnEnd { get; set; }
487
488 public string GroupByString
489 {
490 get
491 {
492 if (GroupBy == null || GroupBy.Count == 0)
493 {
494 return string.Empty;
495 }
496
497 string rtn = string.Empty;
498 foreach (string key in this.GroupBy.Keys)
499 {
500 rtn += "and " + key + " = '" + GroupBy[key] + "' ";
501 }
502 return rtn.Substring(3);
503 }
504 }
505
506 public static string GetName(string elementName)
507 {
508 string[] element = elementName.Split('_');
509
510
511 if (element[0].Equals("label"))
512 {
513 return element[2];
514 }
515 else
516 {
517 return element[4];
518 }
519 }
520 }
521
522 /// <summary>
523 /// Table配置项
524 /// </summary>
525 public class TableConfig
526 {
527 public TableConfig(string tableDescr = "")
528 {
529 this.DataRow = 2;
530 this.SummaryRow = -1;
531
532 if (!string.IsNullOrEmpty(tableDescr))
533 {
534 string[] element = tableDescr.Split(',');
535 foreach (string item in element)
536 {
537 if (!string.IsNullOrEmpty(item))
538 {
539 string[] configs = item.Split(':');
540 if (configs.Length == 2)
541 {
542 switch (configs[0].ToLower())
543 {
544 case "data":
545 case "d":
546 this.DataRow = int.Parse(configs[1]);
547 break;
548 case "summary":
549 case "s":
550 this.SummaryRow = int.Parse(configs[1]);
551 break;
552 case "summaryfilter":
553 case "sf":
554 this.SummaryFilter = configs[1];
555 break;
556 default:
557 break;
558 }
559 }
560 }
561 }
562 }
563
564 }
565 public int DataRow { get; set; }
566 public int SummaryRow { get; set; }
567 public string SummaryFilter { get; set; }
568 }
569 }
微信小程序:动画(Animation)
简单总结一下微信动画的实现及执行步骤。
一、实现方式
官方文档是这样说的:①创建一个动画实例 animation。②调用实例的方法来描述动画。③最后通过动画实例的 export 方法导出动画数据传递给组件的 animation 属性。
因为小程序是数据驱动的,给这句话加上数字标注分为三步:
前两步是定义一个动画并设置都要干什么,然后把这个设置好的“规则”扔给界面上的某个元素,让它按照这个规则执行。
当然如果有多个元素的animation="{{ani}}",也都会执行这个动画规则。
二、用例子说话
新建一个小程序,把没用的删掉修改一下,做个简单例子,上图
代码如下:
index.wxml,一个helloworld,一个按钮
<view class="container">
<view class="usermotto" animation="{{ani}}">
<text class="user-motto">{{motto}}</text>
</view>
<button bindtap='start'>动画</button>
</view>
index.wxss, 为了看着方便加了个边框
.usermotto {
margin-top: 100px;
border: solid;
}
index.js
Page({
data: {
motto: 'Hello World',
},
start:function(){
var animation = wx.createAnimation({
duration: 4000,
timingFunction: 'ease',
delay: 1000
});
animation.opacity(0.2).translate(100, -100).step()
this.setData({
ani: animation.export()
})
}
})
三、相关参数及方法
简单介绍一下例子中的几个参数和方法(其他的详见官方文档):
duration: 动画持续多少毫秒
timingFunction: “运动”的方式,例子中的 'ease'代表动画以低速开始,然后加快,在结束前变慢
delay: 多久后动画开始运行
opacity(0.2) 慢慢变透明
translate(100, -100) 向X轴移动100的同时向Y轴移动-100
animation.translateX( -50).step() , 作用就是向右上方移动和变透明是同时进行, 这两种变化完成之后才会进行向左运行的一步。
SignalR 设计理念(一)
实现客户端和服务器端的实时通讯.
问题阐述
- 客户端提供的方法不确定!
- 客户端的方法参数不确定!
- 不同的名称和参数要分别调用指定的方法!
- 调用客户端方法时,不忽略大小写!
例如:
public void Send(string message) { Clients.All.Write(message); // Clients.All.write(message); 也可以。 }
针对以上问题,你会如何设计服务器架构?
SignalR 选用
dynamic
动态数据类型作为客户端通讯的基类。问:
举一反三:
- 为什么服务器端调用客户端要忽略大小写?
核心代码:
public class ClientProxy : DynamicObject
{
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Binder is passed in by the DLR")] public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { result = Invoke(binder.Name, args); return true; } /// <summary> /// 这里处理业务逻辑 /// </summary> /// <param name="method">方法名称</param> /// <param name="args">参数</param> /// <returns></returns> public Task Invoke(string method, params object[] args) { return Task.Factory.StartNew(() => { //这里处理业务逻辑。 }); } }
解析:
ASP.NET -- WebForm -- ViewState
ASP.NET -- WebForm -- ViewState
1. ViewState的作用
当 ASP .NET 中的表单被提交时,表单会随所有表单值一同重新出现。这是由于 ASP .NET 维持了您的 ViewState。
ViewState 会在页面被提交到服务器时指示其状态。通过在每张页面中的一个 <form runat="server"> 控件中放置一个隐藏域,我们就可以定义页面的状态了。
新建一个Test.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>my Test Aspx</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" Text="Button" />
</div>
</form>
</body>
</html>
在浏览器--开发者工具可看到ViewState隐藏域的存在:
2. ViewState的开启和关闭
维持 ViewState 是 ASP.NET Web 表单的默认设置。
如果您不希望维持 ViewState,请在 .aspx 页面的顶部包含指令: <%@ Page EnableViewState="false" %>,或为任意控件添加属性:EnableViewState="false"。
整个页面关闭ViewState:
单个控件关闭ViewState:
把EnableViewState="false",在浏览器--开发者工具,还是可以看见隐藏域__ViewState的。
如果想彻底去掉隐藏域__ViewState,必须将runat="server"去掉,换成action (但是这样做,会导致工具箱的控件不能用), 如下图:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>my Test Aspx</title>
</head>
<body>
<form action="Test.aspx" method="post">
<div>
<input type="text" name="txtName"/>
</div>
</form>
</body>
</html>
浏览器开发者工具中可清楚看见,隐藏域__ViewState被去掉了
ASP.NET -- 一般处理程序ashx
ASP.NET -- 一般处理程序ashx
如果在一个html页面向服务器端请求数据,可用ashx作为后台页面处理数据。ashx适合用作数据后台处理,相当于WebForm中的aspx.cs文件或aspx.vb文件。
入门案例:html页面向ashx页面请求数据,ashx作为后台页面返回数据。
前端html页面:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>My Test ashx</title>
<script type="text/javascript" src="./js/jquery-2.0.3.min.js" ></script>
<script type="text/javascript" >
$(function() {
$("#btn_Test").click(function() {
$.ajax({
type: "post",
url: "Test.ashx",
datatype: "text",
data: { "TestAction":"getBaiduUrl"},
success: function(data) {
$("#myDiv1").html(data);
}
});
});
});
</script>
</head>
<body>
<button type="button" id="btn_Test">Test</button>
<div id="myDiv1" style="width:300px;height:30px;padding: 10px;border:2px solid blue;">
</div>
</body>
</html>
后台Test.ashx页面:
<%@ WebHandler Language="C#" Class="Test" %>
using System;
using System.Web;
public class Test : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
if (context.Request["TestAction"] == "getBaiduUrl")
{
context.Response.Write("百度的地址是: https://www.baidu.com");
}
}
public bool IsReusable {
get {
return false;
}
}
}
运行结果:
常用到的一些js方法,记录一下
获取字符串长度
function GetStringLength(str) { return str.replace(/[^\x00-\xff]/g, "00").length;
}
通过js对html转义和反转义
function HTMLEncode(html) { var temp = document.createElement("div");
(temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
var output = temp.innerHTML;
temp = null;
return output;
}
function HTMLDecode(text) {
var temp = document.createElement("div");
temp.innerHTML = text;
var output = temp.innerText || temp.textContent;
temp = null;
return output;
}
一些公用的js
1 var com = com || {}; 2 (function ($, com) {
3 /**
4 * 截取字符串
5 * @param str:要截取的字符串
6 * @param len:保留多少字符
7 * @param symbol:超过之后字符串末端要添加的字符
8 */
9 com.cutStr = function (str, len, symbol) {
10 str = str || "";
11 if (symbol == null || symbol == undefined)
12 symbol = "...";
13
14 var count = 0;
15 var strTemp = "";
16 for (var i = 0; i < str.length; i++) {
17 if (/[^\x00-\xff]/g.test(str.substr(i, 1))) {
18 count += 2;
19 }
20 else {
21 count += 1;
22 }
23 if (count <= len) {
24 strTemp += str.substr(i, 1);
25 }
26 else {
27 return strTemp + symbol;
28 }
29 }
30 return str;
31 },
32 /*
33 * 将日期字符串转化为Date
34 * (如:将"2016-12-24 20:13:14"转化为Date格式)
35 * @param d:待转化字符串(传入的时间不能有毫秒)
36 */
37 com.getDate = function (d) {
38 //部分浏览器(IE)不支持日期格式“yyyy-MM-dd hh:mm:ss”,必须将“-”转化为“/”
39 var date = new Date(Date.parse(d.replace(/-/g, "/")));
40 return date;
41 },
42 /*
43 * 获取html代码的纯文本
44 * @param html
45 */
46 com.removeHTMLTag = function (html) {
47 html = html.replace(/<\/?[^>]*>/g, ''); //去除HTML tag
48 html = html.replace(/[ | ]*\n/g, '\n'); //去除行尾空白
49 //html = html.replace(/\n[\s| | ]*\r/g,'\n'); //去除多余空行
50 html = html.replace(/ /ig, '');//去掉
51 html = html.replace(/\s/g, ''); //将空格去掉
52 return html;
53 },
54 /*
55 * 获取地址栏参数
56 * @param 参数名
57 */
58 com.getQueryString = function (name) {
59 var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
60 var r = window.location.search.substr(1).match(reg);
61 if (r != null) return (r[2]); return null;
62 },
63
64 /**************************************************************
65 *将金额转化为大写
66 * str:需要转化的金额
67 ****************************************************************/
68 com.convertMoney = function (n) {
69 var fraction = ['角', '分', '毫', '厘'];
70 var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
71 var unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']];
72 var cnInteger = "整"; //整数金额时后面跟的字符
73 var cnIntLast = "元"; //整型完以后的单位
74 var maxNum = 9999999999999.9999;
75 var head = n < 0 ? '负' : '';
76 //取绝对值
77 n = Math.abs(n);
78 if (n >= maxNum) {
79 console.error("金额转化超出最大处理数字");
80 return "";
81 }
82
83 var IntegerNum; //金额整数部分
84 var DecimalNum; //金额小数部分
85 var money = n.toString();
86 if (money.indexOf(".") == -1) {
87 IntegerNum = money;
88 DecimalNum = '';
89 } else {
90 var parts = money.split(".");
91 IntegerNum = parts[0];
92 DecimalNum = parts[1].substr(0, 4);
93 }
94
95 var ChineseStr = "";
96
97 //小数部分
98 if (DecimalNum != '') {
99 for (i = 0; i < DecimalNum.length; i++) {
100 var m = DecimalNum.substr(i, 1);
101 if (m != '0') {
102 ChineseStr += digit[Number(m)] + fraction[i];
103 }
104 }
105 }
106
107 IntegerNum = IntegerNum * 1;
108
109 //整数部分
110 for (var i = 0; i < unit[0].length && IntegerNum > 0; i++) {
111 var p = '';
112 for (var j = 0; j < unit[1].length && IntegerNum > 0; j++) {
113 p = digit[IntegerNum % 10] + unit[1][j] + p;
114 IntegerNum = Math.floor(IntegerNum / 10);
115 }
116 ChineseStr = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + ChineseStr;
117 }
118
119 if (ChineseStr == '') {
120 ChineseStr += digit[0] + cnIntLast + cnInteger;
121 } else if (DecimalNum == '') {
122 ChineseStr += cnInteger;
123 }
124 ChineseStr = head + ChineseStr;
125 return ChineseStr;
126 },
127
128 /**
129 * 获取字符串长度(一个汉字占两个字符)
130 * @param str:字符串
131 */
132 com.getStringLength = function (str) {
133 return str.replace(/[^\x00-\xff]/g, "00").length;
134 }
135
136 })(jQuery, com);
CryptoJS与C#AES加解密互转
页面js引用:
<script type="text/javascript" src="/content/plugin/CryptoJSv3.1.2/components/core-min.js"></script> <script type="text/javascript" src="/content/plugin/CryptoJSv3.1.2/rollups/aes.js"></script>
JS端AES加密解密:
1 com.str = { 2 _KEY: "12345678900000001234567890000000",//32位
3 _IV: "1234567890000000",//16位
4 /**************************************************************
5 *字符串加密
6 * str:需要加密的字符串
7 ****************************************************************/
8 Encrypt: function (str) {
9 var key = CryptoJS.enc.Utf8.parse(this._KEY);
10 var iv = CryptoJS.enc.Utf8.parse(this._IV);
11
12 var encrypted = '';
13
14 var srcs = CryptoJS.enc.Utf8.parse(str);
15 encrypted = CryptoJS.AES.encrypt(srcs, key, {
16 iv: iv,
17 mode: CryptoJS.mode.CBC,
18 padding: CryptoJS.pad.Pkcs7
19 });
20
21 return encrypted.ciphertext.toString();
22 },
23
24 /**************************************************************
25 *字符串解密
26 * str:需要解密的字符串
27 ****************************************************************/
28 Decrypt: function (str) {
29 var key = CryptoJS.enc.Utf8.parse(this._KEY);
30 var iv = CryptoJS.enc.Utf8.parse(this._IV);
31 var encryptedHexStr = CryptoJS.enc.Hex.parse(str);
32 var srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
33 var decrypt = CryptoJS.AES.decrypt(srcs, key, {
34 iv: iv,
35 mode: CryptoJS.mode.CBC,
36 padding: CryptoJS.pad.Pkcs7
37 });
38 var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
39 return decryptedStr.toString();
40 }
41 }
C# AES加密解密:
const string AES_IV = "1234567890000000";//16位
/// <summary>
/// AES加密算法
/// </summary>
/// <param name="input">明文字符串</param>
/// <param name="key">密钥(32位)</param>
/// <returns>字符串</returns>
public static string EncryptByAES(string input, string key)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key.Substring(0, 32));
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = keyBytes;
aesAlg.IV = Encoding.UTF8.GetBytes(AES_IV.Substring(0, 16));
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(input);
}
byte[] bytes = msEncrypt.ToArray();
return ByteArrayToHexString(bytes);
}
}
}
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="input">密文字节数组</param>
/// <param name="key">密钥(32位)</param>
/// <returns>返回解密后的字符串</returns>
public static string DecryptByAES(string input, string key)
{
byte[] inputBytes = HexStringToByteArray(input);
byte[] keyBytes = Encoding.UTF8.GetBytes(key.Substring(0, 32));
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = keyBytes;
aesAlg.IV = Encoding.UTF8.GetBytes(AES_IV.Substring(0, 16));
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream(inputBytes))
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srEncrypt = new StreamReader(csEncrypt))
{
return srEncrypt.ReadToEnd();
}
}
}
}
}
/// <summary>
/// 将指定的16进制字符串转换为byte数组
/// </summary>
/// <param name="s">16进制字符串(如:“7F 2C 4A”或“7F2C4A”都可以)</param>
/// <returns>16进制字符串对应的byte数组</returns>
public static byte[] HexStringToByteArray(string s)
{
s = s.Replace(" ", "");
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
return buffer;
}
/// <summary>
/// 将一个byte数组转换成一个格式化的16进制字符串
/// </summary>
/// <param name="data">byte数组</param>
/// <returns>格式化的16进制字符串</returns>
public static string ByteArrayToHexString(byte[] data)
{
StringBuilder sb = new StringBuilder(data.Length * 3);
foreach (byte b in data)
{
//16进制数字
sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
//16进制数字之间以空格隔开
//sb.Append(Convert.ToString(b, 16).PadLeft(2, '0').PadRight(3, ' '));
}
return sb.ToString().ToUpper();
}
用法:
com.str.Encrypt("2017-05")//结果:68f4a7903a9fe6085d2301ac68cc039ccom.str.Decrypt("68f4a7903a9fe6085d2301ac68cc039c")//结果:2017-05
//加密string str1 = Encrypt.EncryptByAES("2017-05", "12345678900000001234567890000000");
//解密
string str2 = Encrypt.DecryptByAES("68f4a7903a9fe6085d2301ac68cc039c", "12345678900000001234567890000000");
注:前后台的加密Key必须一致。