C# -- HttpWebRequest 和 HttpWebResponse 的使用javascript
结合使用HttpWebRequest 和 HttpWebResponse,来判断一个网页地址是否能够正常访问。css
1.举例html
class Program { static void Main(string[] args) { string strUrl = "https://www.baidu.com"; HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(strUrl); HttpWebResponse wrsp = (HttpWebResponse)wreq.GetResponse(); if (wrsp.StatusCode == HttpStatusCode.OK) { Console.WriteLine(strUrl+ " --http response正常\r\n"); } else { Console.WriteLine(strUrl + " --http response出现异常!\r\n"); } Console.ReadKey(); } }
2. 运行结果:前端
C#编写扫雷游戏
翻看了下之前大学学习的一些小项目,忽然发现有个项目比较有意思,以为有必要把它分享出来。固然如今看来,里面有不少的不足之处,但因博主如今已经工做,没有时间再去优化。这个项目就是利用C#编写一个Windows系统下的扫雷小游戏。html5
首先讲下扫雷小游戏的玩法:java
(1)扫雷就是要把全部非地雷的格子揭开即胜利;踩到地雷格子就算失败。jquery
(2)当点开的格子不是地雷区域的时候,该格子会显示一个数字,该数字表示的含义就是该格子周边有多少个地雷。git
(3)同时点开的若是非地雷的格子,周边连贯的非地雷区域都会自动被扫描打开,直到遇到旁边有雷区的时候中止。github
(4)当你判断出格子是地雷的时候,你可使用鼠标右键将该块方格标记为雷区。当不肯定的时候,你可标记个问号以待肯定。web
下面来讲下我大学时候实现这个扫雷小游戏的思路:
(1)由于雷区是一个个格子联合组成的,那咱们可使用winform程序自带的系统按钮控件Button来实现雷区方格。
(2)表明雷区方格的Button按钮须要实现下面几个事件:鼠标左键点击扫雷事件,鼠标右键点击标记雷区事件,鼠标右键点击标记问号区域事件。
(3)为了更好的实现游戏的可玩性,增长一个自由设置地雷数量的小功能,可自行设置雷区包含的地雷数量,设置完成后,自动刷新界面,从新部署地雷。
(4)咱们将雷区的方格存储在一个全局的二维数组中,Form窗体在初始化的时候,自动生成面板区域的Button按钮列表。
(5)为了实现每次玩游戏的时候,地雷分布不一致,咱们在Button列表生成后。随机抽取出某些Button按钮做为地雷分布点,并记录该Button的雷区属性为含有地雷。
(6)算法中的关键:递归算法计算雷区。当点击某个方格的时候,若是该方格是雷区,则直接Game Over,若是不是的话,则咱们须要一个算法去计算旁边区域的地雷数量,以及旁边区域没有地雷的区域,当没有地雷的区域连成一片的时候,咱们须要使用递归算法,去查找二维数组,找到对应的连片非雷区,将之打开。
(7)如何设置方格的状态:当鼠标左键点击的方块区域非雷区的时候,咱们将Button按钮的属性设置为Disabled便可呈现打开的状态。当鼠标左键打开的方格是雷区时候,此时咱们能够将全部地雷区域块的Button的背景图设置为地雷图片,并播放相应的爆炸音效,弹出游戏终止界面便可。当鼠标右键标记雷区或者待肯定区域的时候,只须要更改Button的背景图便可。固然上述全部点击操做,都得判断Button方格区域当前的状态值:初始化状态、已标记为待肯定状态、已标记为雷区。
游戏的最终效果图以下:
咱们下面来剖析下几个关键点,由于代码量比较多,我就不所有详细剖析了。
首先咱们定义一个LeiButton类,这个类继承于系统控件Button,增长x,y,youlei三个字段,x表示二维数组的第一个索引,y表示二维数组的第二个索引值,youlei用于标记Button方块区域按钮的状态(0表示无雷,1表示有雷)。同时咱们使用Button按钮类自带的一个Tag属性标记该方块区域是否被翻开。具体定义以下:
而后咱们在窗体对象Form类中定义一些经常使用的变量之类,以下图,都有相应注释
下面咱们来看下生成Button的二维数组,即生成雷区的Button列表。咱们须要在Form中添加GroupBox组件,而后将动态生成的Button列表添加到这个groupbox组件中。生成Button的二维数组方法体以下:
生成完Button列表后,咱们就开始部署地雷了,地雷随机部署到Button列表中,部署地雷的方法以下:
到了这一步,咱们就应该将雷区的界面渲染出来了,这时候咱们能够将上面两个方法放入窗体的Form_Load事件中便可渲染出游戏界面。以下所示
咱们继续,下面写扫雷的算法,当鼠标左键点开某个方格的时候,若是该方格不是雷区,那咱们须要计算该方格周边的地雷数量,计算方法以下:
下面是个递归计算的核心算法,很是关键。当咱们点开的方格非雷区的时候,周边连片的非雷区的方格块会被打开。这一块的核心算法参考下列代码,row表示行,col表示列
下面咱们来添加鼠标的点击事件,我这边采用的是bt_MouseUp事件来处理。点击后,咱们首先判断游戏是否结束,若是没结束,则进行下列操做,获取到被点击的按钮的x,y值以及点击事件按下的键值(判断按下的是鼠标左键仍是右键)。x,y值获取到了,咱们就能够到Button二维数组中找到对应元素。
假如点击的是鼠标左键,则咱们进行扫雷操做,具体的代码以下:
若是按下的是鼠标右键,则是标记方块是雷区或者待肯定区域,具体代码以下:
最后再给出一个判断是否扫雷完毕的方法。
扫雷游戏的设计大概到此结束,中间还有不少能够优化的地方,好比将扫雷的逻辑代码抽离Form类独立出来等,这些都靠读者自行去优化了。
附上博主源码下载的百度云连接,须要代码学习的可自行下载研究:https://pan.baidu.com/s/1T4zVndyypzY9i9HsLiVtGg 提取码: b2im
最后,附上博主的IT技术学习群,欢迎各位同行入群指导交流。
使用IIS调试ASP.NET网站程序
在实际的开发当中,相信不少的开发者在开发调试ASP.NET网站时候都是直接经过Visual Studio工具的编译运行来调试的。
通常状况下,这种调试方式也不会有多少问题,但有时候咱们会发现这样的一个状况,就是明明在本地调试运行都没有问题的网站,部署到服务器IIS上,就出现没法描述的线上问题。这时候,就要排除服务器环境是否跟本地环境同样,其中一点还要涉及到IIS的配置的排除。
那问题来了,咱们能不能在开发的时候,就部署在IIS上调试,而不是直接使用Visual Studio的运行调试,这样涉及到IIS配置的相关问题就能及早的暴露出来,好比IIS用户的权限调用等。举个很典型的例子,如咱们调用微软的Office Excel组件导出Excel的时候,就须要设置IIS用户组对Com组件的调用权限。
在开发阶段就经过IIS对网站进行调试,能够避开不少由于IIS设置问题而致使的问题。
下面来讲下配置IIS调试的步骤。
第一步 打开你的IIS管理器,没有安装IIS服务器的请上网百度如何安装,此处再也不阐述。在IIS中新建一个网站,给定一个主机域名为test.com,并将网站目录指向你web项目的根目录,以下图。
第二步 网站配置的应用程序池很关键,上图中的应用程序池的名字叫testWeb,那这个应用程序池的.NetFrameWork的版本号应该跟你项目中使用的版本一致。咱们点击上图左侧的菜单【应用程序池】,打开应用程序池列表,选中testWeb名称的应用程序池,确认编辑好该程序池的版本号。以下图
第三步 确保你的IIS的状态为正在运行而非中止的状态,而且确认你网站的状态以及网站对应应用程序池的状态为正在运行。
第四步 设置本地电脑的Host文件,配置本地域名test.com。让浏览器识别test.com域名的指向是本地计算机。(此步骤很是关键,否则你在浏览器中输入test.com会告知你没法解析)
咱们在电脑的C盘中查找系统的Host文件,Host文件通常在如下路径中:C:\Windows\System32\drivers\etc。
找到Host文件后,用文本编辑器打开host文件,推荐使用notepad++文本编辑器。打开后,咱们在最后一行加入127.0.0.1 test.com便可,配置完成后保存(Win10系统可能没法编辑保存,咱们通常采起复制文件出来,编辑完成后覆盖回原文件),在浏览器中输入test.com便可指向本地IIS中的网站,即你刚才配置的网站。
第五步 此时配置完毕后,咱们在本身本地电脑的浏览器中输入test.com的时候,就会指向你本地IIS配置的test.com网站中,即你当前开发的网站。
第六步 若是test.com网站测试中发现相应的问题,咱们就能够经过附加IIS进程的方式对网站进行调试。固然调试的方式与以前不一样了,此时就不是直接点击Visual Studio中的调试按钮或者F5进行调试了。咱们能够经过下面的方式进入调试模式,选择菜单栏上的调试,打开子菜单选中附加到进程,以下图(固然能够经过快捷键Ctrl+Alt+P直接调取出附加界面)
打开后的附加界面以下图,咱们找到w3wp.exe进程,而后点击附加按钮便可。
附加完成后,你就可在代码中加入相应的断点调试了,在前台浏览器中输入test.com访问出现的错误,你可在Visual Studio中找到对应代码段加入断点。
经过上述步骤,便可很好的进行网站的调试,IIS相应的配置问题也能及早发现。
WCF入门教程
这一系列文章的内容是从MSDN中COPY过来的,讲述的是最简单的WCF程序示例:如何在控制台应用程序实现和承载WCF服务,以及如何建立、配置和使用WCF客户端。
文章主体可分为两部分,分别介绍服务器端和客户端的编程实现。细分的话,能够分为六项任务。
- 服务器端
定义WCF服务协定(任务一)
这是建立基本 Windows Communication Foundation (WCF) 服务和可使用该服务的客户端所需的六项任务中的第一项任务。
建立基本 WCF 服务时,第一项任务是为与外界共享的服务建立协定,并在其中描述如何与该服务进行通讯。
具体步骤为:
一、 建立新的控制台应用程序项目。 在“新建项目”对话框中,选中“Visual Basic”或“Visual C#”,并选择“控制台应用程序”模板,并命名为Service。 使用默认的位置。
二、将默认的Service 命名空间更改成 Microsoft.ServiceModel.Samples。
三、为项目提供对 System.ServiceModel 命名空间的引用:右击“解决方案资源管理器”中的“Service”项目,选择“添加引用”项,在弹出的对话框中的“.NET”选项卡里的“组件名称”中选择“System.ServiceModel”,而后单击“肯定”。
下面是编程步骤:
一、为 System.ServiceModel 命名空间添加一个 using 语句。
using System.ServiceModel;
二、建立一个新的ICalculator 接口,并将 ServiceContractAttribute 属性应用于该接口,并将 Namespace 值设置为“http://Microsoft.ServiceModel.Samples”。 此命名空间指定该服务在计算机上的路径,并构成该服务的基址部分。 请注意,在经过采用方括号表示法的属性来批注接口或类时,该属性类能够从其名称中去掉“Attribute”部分。
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
三、在接口中建立方法声明,并将 OperationContractAttribute 属性应用于每一个要做为公共 WCF 协定的一部分公开的方法。
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
下面是建立服务协定的完整代码段:
using System;
// Add the using statement for the Sytem.ServiceModel namespace
using System.ServiceModel;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
// Create the method declaration for the contract.
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}
}
........(整个服务器端的代码未完,等讲完任务三时,再给出完整代码)
实现WCF服务协定(任务二)
一、建立一个新 CalculatorService 类,该类从用户定义的 ICalculator 接口继承而来并实现该接口定义的协定功能。
public class CalculatorService : ICalculator
二、实现每一个算术运算符的功能。
public double Add(double n1, double n2)
{
double result = n1 + n2;
Console.WriteLine("Received Add({0},{1})", n1, n2);
// Code added to write output to the console window.
Console.WriteLine("Return: {0}", result);
return result;
}
public double Subtract(double n1, double n2)
{
double result = n1 - n2;
Console.WriteLine("Received Subtract({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Multiply(double n1, double n2)
{
double result = n1 * n2;
Console.WriteLine("Received Multiply({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Divide(double n1, double n2)
{
double result = n1 / n2;
Console.WriteLine("Received Divide({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
在建立和实现了服务协定后,下一步是运行该服务。 运行服务由三个步骤组成:配置、承载和打开服务。
配置、承载和运行服务(任务三)
- 为服务配置基址
为服务的基址建立 Uri 实例。 此 URI 指定 HTTP 方案、本地计算机、端口号 8000,以及服务协定中为服务命名空间指定的服务路径ServiceModelSample/Services。
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
- 承载服务
- 建立一个新的 ServiceHost 实例以承载服务。 必须指定实现服务协定和基址的类型。 对于此示例,咱们将基址指定为http://localhost:8000/ServiceModelSamples/Services,并将 CalculatorService 指定为实现服务协定的类型。
ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress); |
- 添加一个捕获 CommunicationException 的 try-catch 语句,并在接下来的三个步骤中将该代码添加到 try 块中。
- 添加公开服务的终结点。 为此,必须指定终结点公开的协议、绑定和终结点的地址。 此例中,将 ICalculator 指定为协定,将 WSHttpBinding 指定为绑定,并将 CalculatorService 指定为地址。 在这里请注意,咱们指定的是相对地址。终结点的完整地址是基址和终结点地址的组合。 在此例中,完整地址是 http://localhost:8000/ServiceModelSamples/Services/CalculatorService。
selfHost.AddServiceEndpoint( typeof(ICalculator), new WSHttpBinding(), "CalculatorService"); |
- 启用元数据交换。 为此,必须添加服务元数据行为。 首先建立一个 ServiceMetadataBehavior 实例,将 HttpGetEnabled 属性设置为 true,而后为服务添加新行为。
ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); smb.HttpGetEnabled = true; selfHost.Description.Behaviors.Add(smb); |
- 打开 ServiceHost 并等待传入消息。 用户按 Enter 键时,关闭 ServiceHost。
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
下面是服务器端(即“Service”项目中program.cs文件中)的完整程序代码:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}
// Service class that implements the service contract.
// Added code to write output to the console window.
public class CalculatorService : ICalculator
{
public double Add(double n1, double n2)
{
double result = n1 + n2;
Console.WriteLine("Received Add({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Subtract(double n1, double n2)
{
double result = n1 - n2;
Console.WriteLine("Received Subtract({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Multiply(double n1, double n2)
{
double result = n1 * n2;
Console.WriteLine("Received Multiply({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Divide(double n1, double n2)
{
double result = n1 / n2;
Console.WriteLine("Received Divide({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
}
class Program
{
static void Main(string[] args)
{
// Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
// Create ServiceHost
ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress);
try
{
// Add a service endpoint.
selfHost.AddServiceEndpoint(
typeof(ICalculator),
new WSHttpBinding(),
"CalculatorService");
// Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Start (and then stop) the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
}
若要运行服务,启动项目文件夹下bin目录中的 Service.exe便可。
前面三篇文章是讲服务器端的部署和运行,下面讲讲客户端如何配置和使用。
建立WCF客户端(任务四)
- 经过执行如下步骤,在 Visual Studio 2005 中为客户端建立新项目:
- 在包含该服务(以前文章所述的服务)的同一解决方案中的“解决方案资源管理器”(位于右上角)中,右击当前解决方案,而后选择“添加新项目”。
- 在“添加新项目”对话框中,选择“Visual Basic”或“Visual C#”,选择“控制台应用程序”模板,而后将其命名为 Client。 使用默认的位置。
- 单击“肯定”。
- 为项目提供对 System.ServiceModel 命名空间的引用:在“解决方案资源管理器”中右击“Service”项目,从“.NET”选项卡上的“组件名称”列中选择“System.ServiceModel”,而后单击“肯定”。
- 为 System.ServiceModel 命名空间添加 using 语句:using System.ServiceModel;
- 启动在前面的步骤中建立的服务。(即打开在服务器项目中生成的Service.exe可执行文件)
- 经过执行如下步骤,使用适当的开关运行Service Model Metadata Utility Tool (SvcUtil.exe) 以建立客户端代码和配置文件:
- 经过选择“开始”菜单中的“Microsoft Windows SDK”项下的“CMD Shell”,启动 Windows SDK 控制台会话。
- 导航到要放置客户端代码的目录。 若是使用默认设置建立 Client 项目,则目录为 C:\Documents and Settings\<用户名>\Documents\Visual Studio 2008\Projects\Service\Client。
- 将命令行工具Service Model Metadata Utility Tool (SvcUtil.exe) 与适当的开关一块儿使用以建立客户端代码。 下面的示例生成服务的代码文件和配置文件。
svcutil.exe /language:cs /out:generatedProxy.cs /config:app.config http://localhost:8000/ServiceModelSamples/service
- 在 Visual Studio 中将生成的代理添加到 Client 项目中,方法是在“解决方案资源管理器”中右击“Client”并选择“添加现有项”。 而后选择在上一步中生成的 generatedProxy.cs 文件。
配置WCF客户端(任务五)
在 Visual Studio 中,将在前一过程当中生成的 App.config 配置文件添加到客户端项目中。 在“解决方案资源管理器”中右击该客户端,选择“添加现有项”,而后从 C:\Documents and Settings\<用户名>\Documents\Visual Studio 2008\Projects\Service\Client\bin 目录中选择 App.config 配置文件。
将app.config添加到项目中后,就算是完成了wcf客户端的配置。由于具体的配置信息,咱们在使用svcutil.exe工具时,它就帮咱们配置好并写入了app.config文件。
使用WCF客户端(任务六)
一、为要调用的服务的基址建立 EndpointAddress 实例,而后建立 WCF Client 对象。
//Create an endpoint address and an instance of the WCF Client.
EndpointAddress epAddress = new EndpointAddress("http://localhost:8000/ServiceModelSamples/Service/CalculatorService");
CalculatorClient client = new CalculatorClient(new WSHttpBinding(), epAddress);
二、从 Client 内调用客户端操做。
// Call the service operations.
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Call the Subtract service operation.
value1 = 145.00D;
value2 = 76.54D;
result = client.Subtract(value1, value2);
Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
// Call the Multiply service operation.
value1 = 9.00D;
value2 = 81.25D;
result = client.Multiply(value1, value2);
Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);
// Call the Divide service operation.
value1 = 22.00D;
value2 = 7.00D;
result = client.Divide(value1, value2);
Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
三、在 WCF 客户端上调用 Close。
// Closing the client gracefully closes the connection and cleans up resources.
client.Close();
下面是客户端的完整代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
namespace ServiceModelSamples
{
class Client
{
static void Main()
{
//Step 1: Create an endpoint address and an instance of the WCF Client.
EndpointAddress epAddress = new EndpointAddress("http://localhost:8000/ServiceModelSamples/Service/CalculatorService");
CalculatorClient client = new CalculatorClient(new WSHttpBinding(), epAddress);
// Step 2: Call the service operations.
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Call the Subtract service operation.
value1 = 145.00D;
value2 = 76.54D;
result = client.Subtract(value1, value2);
Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
// Call the Multiply service operation.
value1 = 9.00D;
value2 = 81.25D;
result = client.Multiply(value1, value2);
Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);
// Call the Divide service operation.
value1 = 22.00D;
value2 = 7.00D;
result = client.Divide(value1, value2);
Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
//Step 3: Closing the client gracefully closes the connection and cleans up resources.
client.Close();
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}
若要启动客户端,请在“开始”菜单中的“Microsoft Windows SDK”项下选择“CMD Shell”,从而启动 Windows SDK 控制台会话。 定位至 C:\Documents and Settings\<用户名>\Documents\Visual Studio 2008\Projects\Service\Client\obj\Debug 目录,键入 client,而后按Enter。 操做请求和响应将出如今客户端控制台窗口中,以下所示。
Add(100,15.99) = 115.99
Subtract(145,76.54) = 68.46
Multiply(9,81.25) = 731.25
Divide(22,7) = 3.14285714285714
Press <ENTER> to terminate client.
ASP.Net Core开发(踩坑)指南
ASP.NET与ASP.NET Core很相似,但它们之间存在一些细微区别以及ASP.NET Core中新增特性的使用方法,在此以前也写过一篇简单的对比文章ASP.NET MVC应用迁移到ASP.NET Core及其异同简介,但没有进行深刻的分析和介绍,在真正使用ASP.NET Core进行开发时,若是忽略这些细节可能会出现奇怪的问题,特此将这些细节进行分享。
本文主要内容有:
- 无处不在的依赖注入(Dependency Injection, DI)
- Configuration&Options
- ASP.NET Core 请求管道创建
- ASP.NET Core Mvc
- Web API
- Signalr
- 小结
注:本文基于ASP.Net Core 2.1版本,.Net Core SDK版本须要2.1.401+。长篇预警( ╯□╰ )
无处不在的依赖注入
ASP.NET与ASP.NET Core之间最大区别之一就是内置了依赖注入机制,虽然ASP.NET中也有DI机制,但没有内置容器,通常都须要使用第三方的容器来提供服务,另外依赖注入的概念也不像ASP.NET Core中这样无处不在。
简单来讲依赖注入的目的是为了让代码解耦以提升代码的可维护性,同时也要求代码设计符合依赖致使原则使得代码更加灵活,而其原理实际上就是在应用程序中添加一个对象容器,在应用初始化时将实际的服务“放”到容器中,而后当须要相应服务时从容器中获取,由容器来组装服务。
服务的注册
ASP.NET Core的Startup(注:Startup仅仅只是约定名称,实际使用是在Program类型中建立 WebHost时使用的),该类型中包含两个方法分别是ConfigureServices和Configure,其中ConfigureServices的主要做用就是用来将服务“放”置到容器中
代码来自:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1
替换默认的依赖注入容器
ASP.NET Core的默认容器仅提供了构造注入功能,若是须要使用属性注入等功能或者在迁移时原有应用依赖于其它容器,那么能够经过使用第三方容器实现。
将默认容器替换为其它容器仅需三步:
1. 将ConfigureServices方法的返回类型改成IServiceProvider。
2. 将ASP.NET Core中的服务注册到第三方容器中。
3. 使用第三方容器实现IServiceProvider接口并返回。
官方文档以Autofac为例,Autofac已经实现了ASP.NET Core服务注册到Autofac容器中,以及Autofac容器的IServiceProvider接口封装,仅需安装Autofac以及Autofac.Extensions.DependencyInjection包便可。
详情参考:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1#default-service-container-replacement
使用windsor或其它容器能够参考:
https://stackoverflow.com/questions/47672614/how-to-use-windsor-ioc-in-asp-net-core-2
将Controller注册为服务
虽然Controller在激活时是经过容器来获取Controller的依赖(即构造方法须要的参数),在代码运行的时候给人一种Controller是从容器中组装的错觉,可是实际上默认状况下Controller的组装过程不是直接由容器组装的,若是要让Controller从容器组装,那么在配置MVC服务时须要经过.AddControllerAsServices()方法将Controller注册到容器中:
注:通常状况下是否将Controller注册为服务对Controller的开发和代码的运行并无很大区别,可是若是当容器变动为其它容器,而且使用了容器提供的如属性注入等功能时,若是没有将Controller注册为服务,那么相应的属性注入的过程也不会被触发,简单来讲就是只有将Controller注册为服务,那么实例化Controller的工做才会由容器完成,才会触发或者使用到容器提供的其它特性。
服务的获取
前面介绍了服务的注册,如今来介绍一下在ASP.NET Core中有哪些方法能够获取服务:
1. Controller构造方法参数。
2. 经过Controller注入IServiceProvider类型,经过IServiceProvider来获取服务:
3. 在Action方法或者Mvc过滤器(过滤器的上下文参数中包含HttpContext)中经过HttpContext的RequestServices对象获取服务:
4. 在View上经过@inject注入服务:
5. 在Action方法中,经过FormServices特性注入:
注:通常来讲尽量显式的标明类型的依赖(即经过构造参数的方式声明当前类型所依赖的组件),上面的2和3两点分别都是经过服务提供器在方法内部来获取依赖,这样作依赖对于外界来讲是不可知的,可能会对代码的可维护、可测试性等形成必定影响,这种模式被称为Service Locator模式,在开发过程当中尽量避免Service Locator模式的使用。
经常使用的服务
ASP.NET Core相对于ASP.NET来讲取消了一些经常使用的静态类型,好比HttpContext、ConfigurationManager等,取而代之的是经过将相似的组件以服务的形式注册到容器中,使用时经过容易来获取相应的服务组件,这些经常使用的服务有:
1. IHostingEnvironment:包含了环境名称、应用名称以及当前应用程序所处的根目录及Web静态内容的根目录(默认wwwroot)。
2. IHttpContextAccessor:从名字能够看出,它用来访问当前请求的HttpContext。
3. IConfiguration:ASP.NET Core配置信息对象。
4. IServiceProvider: ASP.NET Core服务提供器。
5. DbContext: 这里的DbContext指的是EFCore的DbContext,在ASP.NET Core中,EFCore的DbContext也是在ConfigureServices方法中进行配置并添加到容器,使用时直接从容器中获取(但要注意的是对于分层结构的开发风格来讲,DbContext不会直接被Controller依赖,而是被Controller中依赖的业务服务类型因此来,就是说编写Controller代码的时候不会直接与DbContext发生直接交互)。
Configuration&Options
在ASP.NET的开发中,一般某个变量须要从配置文件读取,通常都是在相应类型的构造方法中,经过静态类型ConfigurationManager的AppSettings方法来读取并初始化变量。虽然ASP.NET Core也能够在类型中注入IConfiguration实例来直接读取配置文件,但该方法因为Options模式的出现已经再也不建议使用,使用组件经过依赖相应的组件Options能够作到关注点分离,提升程序的灵活性、可拓展性,Options使用方法见文档:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1
ASP.NET Core 请求管道创建
ASP.NET因为是基于IIS请求管道的,ASP.NET应用程序仅仅是管道中的一个处理环节,管道中还包含如身份验证、静态文件处理等环节,但ASP.NET Core不同,它脱离了IIS处理管道,因此整个管道的创建均须要靠程序自身完成,而ASP.NET Core创建管道的代码就是Startup类型的Configure方法,该方法经过IApplicationBuilder实例来添加不一样功能的中间件,经过中间件的串联造成处理管道,下图是ASP.NET Mvc模板生成的管道代码:
图片来自:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-2.1#the-configure-method
该管道主要包含了错误处理(开发环境显示异常信息,其它环境跳转错误页面)中间件、静态文件处理中间件以及Mvc中间件。
更多中间件可参考文档:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index?view=aspnetcore-2.1
ASP.NET Core Mvc
ASP.NET Core Mvc与ASP.NET Mvc相比总体上区别不大,但仍然有不少细节上的变化,下面就开始一一介绍:
路由
路由的做用是将请求根据Url映射到“对应”的处理器上,在Mvc中请求的终点就是Controller的Action方法,而这里所谓的“对应”指的是Url与路由模板的匹配,ASP.NET Core Mvc经过如下的方式添加路由模板:
上图中的路由模板是最经常使用的路由模板,使用花括号内的内容为路由参数及其默认值,Url中经过路由参数控制器名称、活动方法名称来匹配到相应控制器的活动方法。
在注册路由时能够为相应路由添加默认值、路由参数约束以及对应路由的相关附加数据(datatokens):
路由的功能除了处理请求匹配外,还具备连接生成的功能,特别是Mvc程序的View中使用IUrlHelper或TagHelper来生成页面的超连接:
其生成原理是经过连接参数(如上图所示的Controller和Action)去路由表中匹配,而后使用匹配结果中的第一个路由(可能会匹配到多个路由对象,具体内容在后续Area章节介绍)来生成连接。
更多路由信息及路由模板定义参考文档:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1
控制器
ASP.NET Core Mvc的Controller通常继承Controller类型实现,基类Controller中包含了Mvc中经常使用的返回方法(如Json以及View等)以及用于数据存储的ViewBag、ViewData、TempData。
Area
Area是Mvc应用中用来进行功能拆分或分组的一种方式,Area通常有本身的命名空间和目录结构,通常Area的默认目录结构以下:
ASP.NET Core Mvc和ASP.NET Mvc中的概念和用法基本上是一致的,但也存在一些区别:
1. Area下面的Controller须要使用Area特性标明当前Controller属于哪个Area:
注:Area的目录结构不是必须的,只须要经过特性标记的Controller都会被正确识别,但目录结构的改变会致使没法找到View,关于View的查找路径会在后续介绍。
2. Area的路由注册也是在UseMvc方法中完成:
注:携带Area的路由模板须要放在前面,不然在生成经过IUrlHelper或TagHelper生成连接时,因为Controller以及action会匹配到没有area的模板并使用该模板生成连接,致使area参数被忽略,而生成相似:/controller/action?area=area的结果(在生成Url时,ASP.NET Core会将多余的路由参数放置到查询字符串中)
View
View是基于Razor的HTML模板,Razor的详细语法参考文档:
https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-2.1
ASP.NET Core Mvc的View与ASP.NET Mvc中的使用方法基本一致,主要区别以下:
1. 引入了TagHelper,使用TagHelper可让View的代码更接近Html。更多TagHelper信息参考文档:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-2.1
2. Controller将参数传输到View的方法添加了ViewData特性,使用方法以下:
View中访问被ViewData标记的方式:
更多详情参考文档:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/overview?view=aspnetcore-2.1#passing-data-to-views
3. 新增View组件:https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1
配置View的查找路径:
ASP.NET Core能够在ConfigureServices方法中对RazorViewEngineOptions进行配置,以下图所示,在默认查找位置基础上添加了View以及AreaView的查找路径:
模型绑定
模型绑定指的是ASP.NET Core Mvc将请求携带的数据绑定到Action参数的过程,ASP.NET Core Mvc的模型绑定数据源默认使用Form Values、Route Values以及Query Strings,全部值都以Name-Value的形式存在,模型绑定时主要经过参数名称、参数名称.属性名称、参数名称[索引]等方式与数据源的Name进行匹配。
除了默认的数据源以外还能够从Http请求Header、Http请求Body甚至从依赖注入容器中获取数据,要从这些数据源中获取数据须要在相应参数上使用[FromHeader]、[FromBody]、[FromServices]特性。
若是须要获取的数据在不一样数据源中都存在时(Name存在于多个数据源中),还能够经过特性指明从哪个数据源中获取,如[FromForm]、[FromQuery]及[FromRoute]。
须要注意的是[FromBody]默认只支持Json格式的内容,若是须要支持其它格式,如XML须要添加相应的格式化器,添加方法以下图所示:
更多模型绑定及验证内容请参考文档:https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.1
其中模型验证的使用方式与ASP.NET Mvc一致,仍然是经过相应的验证特性对模型或模型属性进行标记。
Action的返回值与Json序列化
说完Action方法参数的绑定,再来看一下Action方法的返回类型,在ASP.NET Mvc中Controller提供了返回页面内容的View方法以及返回Json内容的Json方法(固然还有文件、重定向、404等等其它内容返回方法,详见Controller与ControllerBase类型)。
这里有一个须要注意的地方是当使用Json方法返回一个对象实例时,默认使用首字母小写的驼峰命名方式序列化实例的属性名称,以下图所示:
访问结果:
要使用大写驼峰形式命名须要在配置Mvc服务时添加如下代码来修改Json默认的序列化配置:
注:一样的问题也存在于WebAPI的Ok方法以及Signalr的Json格式协议。
静态资源
因为ASP.NET Core已经再也不使用IIS请求管道,因此对于静态资源的访问来讲须要在请求管道中添加相应的处理中间件来完成:
默认的无参UseStaticFiles方法将wwwroot目录做为静态资源存放目录,若是要添加其它静态内容目录能够再次使用UseStaticFiles方法,并经过StaticFileOptions对目录的访问路径以及实际路径进行配置:
注:因为ASP.NET Core能够在Linux下运行,因此对于Linux来讲路径是大小写敏感的,另外因为Windows和Linux类系统的路径分隔符也不一致,因此为了保证路径的统一,可使用Path.Combine方法,该方法会根据操做系统的不一样对路径进行不一样的处理。
另外对于css及js资源文件的打包、压缩功能,最新版本(ASP.NET Core 2.1)的应用模板以及不会自动添加相关功能,须要在拓展工具中添加Bunlder& Minifier拓展:
而后经过右键js等资源文件来建立bundleconfig.json文件:
WebAPI
API控制器的建立
ASP.NET Core将Mvc和WebAPI进行了合并,它们的实现都直接或间接继承了ControllerBase类型,只不过Mvc的基类Controller在ControllerBase的基础上添加了一些用于处理View的功能。
用ASP.NET Core开发WebAPI时,Controller类型直接继承ControllerBase。而后这个API的Controller就具备了基类的特性,返回一个结果仅须要使用Ok方法便可,以下图所示:
而后在路由表中添加路由:
便可经过/api/default/index访问到这个API:
但对于REST风格的API来讲,它须要经过ApiController特性对Controller类型进行标记,而且经过Route特性来设置路由:
而后就能够经过HTTP谓词来访问API:
但要注意的是在ASP.NET Core中实现的REST风格的Controller,它不会再根据action方法的名称来匹配谓词,因此存在多个方法时会,那怕对方法进行了命名,但仍然会出现如下错误:
为了解决这个问题,须要经过添加谓词特性解决:
模型绑定
WebAPI中的模型绑定与MVC存在一些区别,首先当使用ApiController标记Controller类型时,若是模型绑定验证未经过,会直接返回400错误,不会执行Action方法(免去了使用!ModelState.IsValid进行判断):
执行结果:
其次使用ApiController标记的Controller在执行模型绑定时会使用默认的推断规则,该规则分别从Body、Form、Header、Query、Route、Services(它们分别对应FromBody、FromForm、FromHeader、FromQuery、FromRoute、FromServices特性)中推断获取数据并绑定,为何说推断?
由于有一些特殊的规则:
1. FromBody用于复杂类型推断,若是不是复杂类型(如int、string等)以及特殊的内置类型(IFormCollection文档例子),则不会从Body中获取数据,除非经过[FromBody]特性指明,例子以下:
请求结果:
当使用[FormBody]指明参数数据源后能够正常访问:
注:当请求参数为简单类型时,请求体内容类型须要为application/json,内容不能为Json字符串,使用参数值做为内容便可(上图id没有提供的异常并非由于Json格式问题,而是没有指明从body中获取数据致使的)。
2. 只能存在一个参数从Body中获取数据,若是出现多个参数时,只能保证一个参数从Body中获取数据,其它参数须要指明获取数据的位置:
该API的调用方式以下:
3. FromForm默认只推断文件(IFormFile)及文件集合类型(IFormFileCollection),其他类型默认均不会从Form中获取。
4. 使用FromForm特性时会推断multipart/form-data请求内容类型。
以上推断行为能够经过以下配置禁用:
更多信息参考文档:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-2.1
SignalR
SignalR是用于客户端服务器实时通讯的工具库,从ASP.NET中就具备该功能,ASP.NET Core中的SignalR概念与用法与原来基本一致,但也存在一些区别:
1. 支持更多的客户端,.Net客户端、Java客户端、Js客户端以及非官方的C++客户端、Swift客户端。
2. 当连接SignalR并经过身份验证后,SignalR会保存当前用户连接SignalR的ID以及经过验证后的用户名,能够经过用户名向用户客户端推送消息。
3. 在应用程序中能够经过IHubContext<HubType>方式,对SignalR上下文进行注入,而且能够直接经过该上下文推送数据给已经连接的客户端,IHubContext<HubType>其实是GlobalHost.ConnectionManager.GetHubContext<HubType>()的替代方式。
4. ASP.NET Core中经过app.UserSignalR以及route参数来映射一个Hub,每个Hub拥有独立的上下文,所以若是要使用IHubContext<HubType>来向客户端推送信息,那么必须准确注明Hub的类型,以下图代码应该使用IHubContext<ChatHub>,不能使用除ChatHub之外的类型(基类也不行)。
5. SignalR默认使用Json协议传输数据,默认状况下使用首字母小写的驼峰命名方式序列化对象,要更改该默认行为须要经过一下代码,替换默认的序列化行为:
6. ASP.NET Core的客户端代码(特指Js客户端)有变动,须要对应版本使用。
关于更多SignalR内容请参考文档:https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction?view=aspnetcore-2.1
小结
本文主要介绍了ASP.NET Core中Mvc、WebAPI以及SignalR开发时与原来ASP.NET中的一些细小区别和新特性,总体来讲ASP.NET Core与ASP.NET从使用方式上基本上是一致的,这也使得从ASP.NET迁移到ASP.NET Core变得更加容易,但可能由于这些细小的问题每每会向代码中埋入一些坑,因此特别编写了本文来解释这些问题。
总的来讲ASP.NET Core的文档至关齐全,本文中大部份内容实际都是文档中提到的,因此建议你们在使用ASP.NET Core开发时,首先第一步就是熟读文档,避免遗漏细节。但愿本篇文章对你们有帮助(*^_^*)
参考:
https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-2.1
本文连接:http://www.javashuo.com/article/p-oaqtejjt-v.html
ASP.Net Core Razor+AdminLTE 小试牛刀
AdminLTE
一个基于 bootstrap 的轻量级后台模板,这个前端界面我的感受很清爽,对于一个大后端的我来讲,能够减小较多的时间去承担前端的工做但又必须去独立去完成一个后台系统开发的任务,而且,文档还算比较齐全,对着demo能够完成一个基本的前端框架搭建了。你们若有更为好看的又方便后端上手的前端框架,也能够在留言区分享一下呗。
AdminLTE 文档
在线中文Demo:http://adminlte.la998.com/
在线中文文档:http://adminlte.la998.com/documentation/index.html
Github:https://github.com/almasaeed2010/AdminLTE/releases
AdminLTE 布局
AdminLTE依赖于两个主要框架:JQ和Bootstrap,其余插件能够按需增长。
从文档能够知道,使用AdminLTE主要有四个部分:
- 包装
.wrapper
。包裹整个网站的div。 - 主标题
.main-header
。包含徽标和导航栏。 - 边栏
.sidebar-wrapper
。包含用户面板和侧边栏菜单。 - 内容
.content-wrapper
。包含页眉和内容。
在文档中,能够找到下载地址,本文示例是使用最新的版本V2.4.5。
Asp.Net Core Razor
新建项目Asp.net Core Web应用程序,默认就是Razor Pages,而后添加相应的模块,如图:本文使用的SDK版本为:dotNet Core 2.1。
First
在Asp.Net Core项目中,引用AdminLTE,在wwwroot仅添加如图三个文件夹便可:
- bower_components 基本组件。
- dist adminlte的主要文件。
- plugins 其余插件。
Second
在_Layout.cshtml文件中添加引入相关文件:
<!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <!-- Bootstrap 3.3.7 --> <link rel="stylesheet" href="~/adminlte/bower_components/bootstrap/dist/css/bootstrap.min.css"> <!-- Font Awesome --> <link rel="stylesheet" href="~/adminlte/bower_components/font-awesome/css/font-awesome.min.css"> <!-- Ionicons --> <link href="~/adminlte/bower_components/Ionicons/css/ionicons.min.css" rel="stylesheet" /> <!-- Theme style --> <link rel="stylesheet" href="~/adminlte/dist/css/AdminLTE.min.css"> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link rel="stylesheet" href="~/adminlte/dist/css/skins/_all-skins.min.css"> <!-- Pace style --> <link href="~/adminlte/plugins/pace/pace.min.css" rel="stylesheet" /> <link href="~/css/common.css" rel="stylesheet" /> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> <!-- Google Font --> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
在body中,添加js:
<!-- jQuery 3 --> <script src="~/adminlte/bower_components/jquery/dist/jquery.min.js"></script> <!-- jQuery UI 1.11.4 --> <script src="~/adminlte/bower_components/jquery-ui/jquery-ui.min.js"></script> <!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip --> <script> $.widget.bridge('uibutton', $.ui.button); </script> <!-- Bootstrap 3.3.7 --> <script src="~/adminlte/bower_components/bootstrap/dist/js/bootstrap.min.js"></script> <!-- Slimscroll --> <script src="~/adminlte/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script> <!-- FastClick --> <script src="~/adminlte/bower_components/fastclick/lib/fastclick.js"></script> <!-- AdminLTE App --> <script src="~/adminlte/dist/js/adminlte.min.js"></script> <!-- Skin --> <script type="text/javascript" src="~/adminlte/dist/js/sidebarskins.js" charset="gbk"></script>
sidebarskins.js是本人汉化的侧边栏皮肤
坑1:通常状况,发现某些功能运行不起来的都是引用不正确致使的,这个要耐心对照好Demo来检查,或者直接用Demo来修改吧。
Third
开始使用AdminLTE,这里直接贴图吧,图上有注释和代码折叠比较直观点,重要的地方在放代码。
最后就能够运行项目来预览一下效果了:
都使用到bootstrap,必须得看看移动端的效果,还不错吧。
坑2:须要注意的是,点击这个小图标能够实现左侧边栏收缩展开的功能,当只有侧边栏能够正常收缩展开但Logo无动于衷的时候,你多是少了【sidebar-mini】样式和【logo-mini】logo小图的引用
添加一个登录
登陆界面写得比较简约,我比较喜欢这种风格。前端写得很少,因此还得前端的女票指导一二,否则就是后端的设计的界面了,你懂的。
在Pages文件夹中,添加一个Razor界面,并撸好界面代码:

@page @model AdminLTE.Net.Web.Pages.LoginModel @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>登陆 - AdminLTE.Net.Web</title> <meta name="developer" content="EminemJK"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" rel="stylesheet" /> <link href="~/adminlte/dist/css/AdminLTE.min.css" rel="stylesheet" /> <link href="~/css/login.css" rel="stylesheet" /> </head> <body> <div> <div class="row"> <div class="loginHeader"> <img class="logo-img" src="~/images/banana_logo.ico" /> <h1 class="logo-name">Banana</h1> <div class="clearfix"></div> </div> </div> <div class="row login-bg"> <div class="loginInBox"> @if (!string.IsNullOrEmpty(Model.Message)) { <p class="login-box-msg" style="color:red">@Model.Message</p> } else { <p class="login-box-msg">Sign in to start your session</p> } <form method="post"> <div class="form-group has-feedback"> <input type="text" class="form-control" asp-for="Login.UserName"> <span class="glyphicon glyphicon-user form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" class="form-control" asp-for="Login.Password"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <button type="submit" class="btn btn-primary btn-block btn-flat login-btn">Sign In</button> </form> </div> </div> </div> <footer class="footer"> <strong>Copyright © 2018 <a href="http://www.cnblogs.com/EminemJK/">EminemJK</a>.</strong> All rights reserved. </footer> </body> </html>
在Startup中引入Authentication身份验证:
services.AddAuthentication(CookieService.AuthenticationScheme) .AddCookie(CookieService.AuthenticationScheme, o => { o.LoginPath = new PathString("/Login"); });
Configure方法内调用
app.UseAuthentication();
在Login.cshtml.cs中增长一个OnPostAsync的方法:
[HttpPost] public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { Message = ModelState.Root.Errors[0].ErrorMessage; } else { var user = userService.Login(Login.UserName, Login.Password); if (user != null) { VUserModel model = new VUserModel() { Id = user.Id, UserName = user.UserName, Time = DateTime.Now }; var identity = new ClaimsIdentity(CookieService.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Sid, CookieService.GetDesEncrypt(model))); await HttpContext.SignInAsync(CookieService.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties() { //记住我 IsPersistent = true, //过时时间 ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromMinutes(30)) }); return RedirectToPage("./Index"); } Message = "登陆失败,用户名密码不正确。"; } return Page(); }
userService和CookieService都是在业务层定义的,gayhub会在文章末尾。
在.Net Core Razor中,xx.cshtml.cs中默认触发的是Get和Post方法,
- OnGet
- OnPost
- OnGetAsync
- OnPostAsync
若是是须要自定义的,举个栗子,定义为:OnPostLoginAsync,而后在Form表单提交的【按钮】增长asp-page-handler="Login",详细的推荐你们阅读这篇文章:ASP.NET Core - Razor页面之Handlers处理方法。
接着,而后再Index和须要身份验证的地方都加上Authorize特性便可:
namespace AdminLTE.Net.Web.Pages { [Authorize(AuthenticationSchemes = CookieService.AuthenticationScheme)] public class IndexModel : BasePageModel { public void OnGet() { } } }
踩坑
1、Ajax Post请求,HttpCore 400
function uploadfile() { var file = $("#input-userimg")[0].files[0]; var data = new FormData(); data.append('file', file); $.ajax({ url: "/Account/UserList?handler=Upload", type: 'POST', data: data, contentType: false, processData: false, success: function (returndata) { $("#user-img").attr('src', returndata.path); }, error: function (a, b, c) { alert('上传失败') } }); };
折腾许久,缘由是Razor被设计为能够自动防止跨站请求伪造(CSRF / XSRF)攻击。你没必要编写任何其余代码。Razor页面中自动包含防伪令牌生成和验证。这里请求失败,是由于POST没有提交AntiForgeryToken。
解决方法:
1.增长"XSRF-TOKEN"标识到框架中
//增长了"XSRF-TOKEN"标识,值为表单自动生成的防伪标记 services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
2.页面*.cshtml头部加上
@Html.AntiForgeryToken()
3.ajax引入
function uploadfile() { var file = $("#input-userimg")[0].files[0]; var data = new FormData(); data.append('file', file); $.ajax({ url: "/Account/UserList?handler=Upload", type: 'POST', data: data, contentType: false, processData: false, beforeSend: function (xhr) { xhr.setRequestHeader("XSRF-TOKEN", $('input:hidden[name="__RequestVerificationToken"]').val()); }, success: function (returndata) { $("#user-img").attr('src', returndata.path); }, error: function (a, b, c) { alert('上传失败') } }); };
而后既能够正常访问Handler
2、DataTables参数实例加说明

var table = $('#userListTable').DataTable({ "processing": true, "serverSide": true, "ajax": function (data, callback, settings) { //data的参数请参考: https://segmentfault.com/a/1190000004478726 var param = {}; param.draw = data.draw; param.pageNum = (data.start / data.length) + 1; param.pageSize = data.length; param.sex = $('#select-sex option:selected').val(); param.phone = $('#input-phone').val(); param.name = $('#input-name').val(); $.ajax({ type: "GET", data: param, url: "/Account/UserList?handler=UserPage", dataType: "json", success: function (data) { //成功后回调自动渲染 callback(data); } }); }, 'columns': [ { 'data': 'id' }, { 'data': 'name' }, { 'data': 'userName' }, { 'data': 'sexString' }, { 'data': 'phone' }, { 'data': 'createTime' }, { 'data': 'enableString', 'render': function (data, type, row) { if (row.enable == 1) return '<span style="color:#19be6b" >' + row.enableString + '</span>'; else return '<span style="color:#ed3f14" >' + row.enableString + '</span>'; } }, { 'data': null, 'render': function (data, type, row) { return '<a id="btn-edit" class="btn btn-success btn-xs" title="编辑" onClick=btn_edit(' + row.id + ')><i class="fa fa-edit"></i>编辑</a> ' + '<a id="btn-edit" class="btn btn-danger btn-xs" title="删除" onClick=btn_edit(' + row.id + ')><i class="fa fa-trash " title="删除" style="cursor:pointer"></i>删除</a>'; } }, ], //datatable设置参数 http://www.datatables.club/reference/option/ 'paging': true, //启用分页 'lengthChange': true, //设置每页数量 'searching': false, 'ordering': false, 'info': true, 'autoWidth': false, //设置中文 'language': { "sProcessing": "玩命加载中...", "sLengthMenu": "每页显示显示 _MENU_", "sZeroRecords": "没有匹配结果", "sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项", "sInfoEmpty": "显示第 0 至 0 项结果,共 0 项", "sInfoFiltered": "(由 _MAX_ 项结果过滤)", "sInfoPostFix": "", "sSearch": "搜索:", "sUrl": "", "sEmptyTable": "表中数据为空", "sLoadingRecords": "玩命加载中...", "sInfoThousands": ",", "oPaginate": { "sFirst": "首页", "sPrevious": "上页", "sNext": "下页", "sLast": "末页" }, "oAria": { "sSortAscending": ": 以升序排列此列", "sSortDescending": ": 以降序排列此列" } } });
Last
附上这些天来的成果,发现,我并不适合写前端,太丑了,哈哈。
最后,Show me the code。
Github:https://github.com/EminemJK/AdminLTE.Net.Web
Banana
Banana Github:https://github.com/EminemJK/Banana
Demo中会使用到这两个我的封装的组件:
Banana.Uow是基于Dapper封装的工做单元和仓储;
Banana.Utility是经常使用的工具类,有Redis,加解密,拼音等等;
欢迎你们在Issues中提出意见,你们共同进步。
webservice建立、部署和调用
webservice 能够用于分布式应用程序之间的交互,和不一样程序之间的交互。
下面经过一个简单的例子来建立一个webservice,用的是vs2010开发工具
首先建立一个web应用程序
接着咱们添加一个web服务
一开始WebService1.asmx这个文件中有一个HelloWord方法
这样咱们建立webserver服务就算完成了,接着咱们建立一个加减乘除的方法
这样,咱们的webservice的方法就算编写完成了,接着咱们将其发布到咱们iis服务器上
接着咱们建立一个webform程序来调用咱们的webservice
首先咱们须要添加有个服务引用
接着咱们就能够编写代码测试咱们webserver
这样咱们webservice建立、部署和调用就算完成了。
.net接收post请求并把数据转为字典格式
public SortedDictionary<string, string> GetRequestPost()
{
int i = 0;
SortedDictionary<string, string> sArray = new SortedDictionary<string, string>();
NameValueCollection coll = Request.Form;
String[] requestItem = coll.AllKeys;
for (i = 0; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], Request.Form[requestItem[i]]);
}
coll.Clear();
return sArray;
}
调用:
SortedDictionary<string, string> sPara = GetRequestPost();
.net接收post请求,并转为字符串
Stream s = Request.InputStream;int count = 0;byte[] buffer = new byte[1024];StringBuilder reqXml = new StringBuilder();while ((count = s.Read(buffer, 0, 1024)) > 0){reqXml.Append(Encoding.UTF8.GetString(buffer, 0, count));}s.Flush();s.Close();s.Dispose();