深度优先

这个家伙好懒,除了文章什么都没留下

0%

创建两个Web项目,Grpc.Demo.Client和Grpc.Demo.Server,项目结构如图:

Protos/greet.proto代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
syntax = "proto3";

option csharp_namespace = "Grpc.Demo.Server";

package greet;

// The greeting service definition.
service GreetService {
rpc SayHello (HelloRequest) returns (HelloResponse);
rpc GetByNo (GetByNoRequest) returns (EmployeeResponse);
rpc GetAll (GetAllRequest) returns (stream EmployeeResponse);
rpc AddPhoto (stream AddPhotoRequest) returns (AddPhotoResponse);
rpc Save (EmployeeRequest) returns (EmployeeResponse);
rpc SaveAll (stream EmployeeRequest) returns (stream EmployeeResponse);
}

message HelloRequest {
string name = 1;
}

message HelloResponse {
string message = 1;
}

message Employee {
int32 id = 1;
int32 no = 2;
string firstName = 3;
string lastName = 4;
float salary = 5;
}

message GetByNoRequest{
int32 no = 1;
}

message EmployeeResponse{
Employee employee = 1;
}

message GetAllRequest {

}

message AddPhotoRequest {
bytes data = 1;
}

message AddPhotoResponse {
bool isOk = 1;
}

message EmployeeRequest {
Employee employee = 1;
}

MyGreetService.cs 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using Grpc.Core;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Grpc.Demo.Server.Service
{
public class MyGreetService : GreetService.GreetServiceBase
{
private readonly ILogger<MyGreetService> _logger;

public MyGreetService(ILogger<MyGreetService> logger)
{
_logger = logger;
}

public override Task<HelloResponse> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloResponse
{
Message = "Hello " + request.Name + Guid.NewGuid().ToString()
});
}

public override Task<EmployeeResponse> GetByNo(GetByNoRequest request, ServerCallContext context)
{
return Task.FromResult(new EmployeeResponse
{
Employee = new Employee()
{
Id = 123,
No = 123456,
FirstName = Guid.NewGuid().ToString(),
}
});
}

public override async Task GetAll(GetAllRequest request, IServerStreamWriter<EmployeeResponse> responseStream, ServerCallContext context)
{
for (int i = 0; i < 10; i++)
{
await responseStream.WriteAsync(new EmployeeResponse()
{
Employee = new Employee()
{
No = i,
}
});
}
}

public override async Task<AddPhotoResponse> AddPhoto(IAsyncStreamReader<AddPhotoRequest> requestStream, ServerCallContext context)
{
var data = new List<byte>();
while (await requestStream.MoveNext())
{
data.AddRange(requestStream.Current.Data);
}
using MemoryStream memoryStream = new MemoryStream(data.ToArray());

using FileStream fileStream = new FileStream("test.png", FileMode.OpenOrCreate);
memoryStream.WriteTo(fileStream);

return new AddPhotoResponse()
{
IsOk = true
};
}

public override async Task<EmployeeResponse> Save(EmployeeRequest request, ServerCallContext context)
{
var employee = request.Employee;
employee.FirstName = employee.FirstName + "1";
return new EmployeeResponse()
{
Employee = employee
};
}

public override async Task SaveAll(IAsyncStreamReader<EmployeeRequest> requestStream,
IServerStreamWriter<EmployeeResponse> responseStream, ServerCallContext context)
{
var employees = new List<Employee>() {
new Employee(){Id =1}
};
while (await requestStream.MoveNext())
{
employees.Add(requestStream.Current.Employee);
}
foreach (var employee in employees)
{
await responseStream.WriteAsync(new EmployeeResponse()
{
Employee = employee
});
}
}
}
}

服务端Startup.cs 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Grpc.Demo.Server.Service;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Grpc.Demo.Server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
//添加grpc
services.AddGrpc();

services.AddControllersWithViews();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
//注册服务
endpoints.MapGrpcService<MyGreetService>();

endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

客户端HomeController.cs,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Grpc.Demo.Client.Models;
using Grpc.Net.Client;
using Grpc.Core;
using System.IO;
using Google.Protobuf;

namespace Grpc.Demo.Client.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}

public async Task<IActionResult> Index(int id)
{
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new GreetService.GreetServiceClient(channel);
string msg = string.Empty;
switch (id)
{
case 1:
msg = await SayHelloAsync(client);
break;

case 2:
msg = await GetByNoAsync(client);
break;

case 3:
msg = await GetAllAsync(client);
break;

case 4:
msg = await AddPhotoAsync(client);
break;

case 5:
msg = await SaveAsync(client);
break;

case 6:
msg = await SaveAllAsync(client);
break;

default:
break;
}

ViewData["Message"] = msg;
return View();
}

private async Task<string> SaveAllAsync(GreetService.GreetServiceClient client)
{
var employees = new List<Employee>()
{
new Employee(){Id=2, FirstName ="zhangsan"},
new Employee(){Id=3, FirstName ="lisi"},
};

var cell = client.SaveAll();
var requestStream = cell.RequestStream;
var responseStream = cell.ResponseStream;

var result = new List<Employee>();

var responseTask = Task.Run(async () =>
{
while (await responseStream.MoveNext())
{
result.Add(responseStream.Current.Employee);
}
});

foreach (var employee in employees)
{
await requestStream.WriteAsync(new EmployeeRequest()
{
Employee = employee
});
}

await requestStream.CompleteAsync();
await responseTask;

return string.Join(",", result.Select(p => p.Id).ToList());
}

private async Task<string> SaveAsync(GreetService.GreetServiceClient client)
{
var cell = await client.SaveAsync(new EmployeeRequest()
{
Employee = new Employee
{
FirstName = "zhangsan",
}
});
return cell.Employee.FirstName;
}

private async Task<string> AddPhotoAsync(GreetService.GreetServiceClient client)
{
FileStream fs = System.IO.File.OpenRead("test.png");
using var cell = client.AddPhoto();
var stream = cell.RequestStream;
while (true)
{
byte[] buffer = new byte[1024];
int numRead = await fs.ReadAsync(buffer, 0, buffer.Length);
if (numRead == 0)
{
break;
}
if (numRead < buffer.Length)
{
Array.Resize(ref buffer, numRead);
}
await stream.WriteAsync(new AddPhotoRequest()
{
Data = ByteString.CopyFrom(buffer)
});
}

await stream.CompleteAsync();
var response = await cell.ResponseAsync;
return response.IsOk.ToString();
}

private async Task<string> GetAllAsync(GreetService.GreetServiceClient client)
{
using var response = client.GetAll(new GetAllRequest());
var responseStream = response.ResponseStream;
List<Employee> list = new List<Employee>();
while (await responseStream.MoveNext())
{
list.Add(responseStream.Current.Employee);
}
return string.Join(",", list.Select(p => p.Id).ToList());
}

private async Task<string> GetByNoAsync(GreetService.GreetServiceClient client)
{
var response = await client.GetByNoAsync(new GetByNoRequest()
{
No = 1
});
return response.Employee.LastName;
}

private async Task<string> SayHelloAsync(GreetService.GreetServiceClient client)
{
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
return response.Message;
}
}
}

主要代码如上!

https://www.cnblogs.com/cgzl/p/12354204.html

gRPC的结构

在我们搭建gRPC通信系统之前,首先需要知道gRPC的结构组成。

首先,需要一个server(服务器),它用来接收和处理请求,然后返回响应。

既然有server,那么肯定有client(客户端),client的作用就是向server发送请求,具体就是生成一个请求,然后把它发送到server,然后等待server的响应。

但是它们不必是一对一的关系,在整个系统里,可以有多个server,也可以有多个client。根据实际情况,一个应用程序可能是gRPC的server,也可能是gRPC的client,也可能两者都是。

gRPC里面server和client并不是直接通信的,gRPC可以使用protocol buffer定义的消息来生成代码。

当client发送请求的时候,它会和server端生成的代码进行交互;同样在client端也有生成的代码,client端生成的代码负责提供一个隧道,这个隧道被用来吧client端生成的消息发送给server。

因为server和client两端都有生成的代码,所以如何序列化和反序列化,以及如何进行来回的传输等细节,我们都可以不了解。

但是为了让server和client端来回传输通信,我们还需要一个协议,传输协议就负责把消息来回的传递。所以它并不需要懂得这些消息的内容,生成的代码会负责理解这些消息,但是传输协议需要负责把消息从一端传递到另一端。

目前,好像gRPC只能使用Protocol Buffer这一个传输协议。但是gRPC在设计的时候,它的传输层是可插拔的,所以如果我们想把Protocol Buffer使用某种JSON或XML的协议替换掉,是可行的。如果你有特定的需求使用Protocol Buffer无法实现的话,那么你也可以创建自己的传输协议。

设计步骤

总共应该分三步。设计原则是从里到外(看上面结构图)。

所以:

  1. 首先我们应该定义消息(message)。这些消息使用Protocol Buffer来进行定义

  2. 定义完消息,我们使用Proto-c编译器来生成server和client端的代码,它们会负责把消息在两端之间来回传递

  3. 现在,我们就可以写client和server了。

gRPC 生命周期

gRPC或者RPC的生命周期可以参考上图。

首先,需要创建一个隧道,该隧道会包装实际用来传输消息的线路协议。

例如如果我们的server和client之间使用HTTP/2协议,那么这个隧道就会包装一个server和client之间的TCP连接。

这些隧道的优点是,它们只需要创建一次。一旦隧道创建了,你就可以在你应用程序的生命周期之内持续的使该隧道来回发送消息。

隧道建立好之后,就该创建client了。client也是可以复用的,不必没有个rpc调用都重建client。但是在调用之前,我们需要把client建立好。

现在client进入隧道,这个client通常是提供给我们的,我们不需要自己实现任何代码。使用Proto-c编译消息定义生成的代码将会给我们提供client需要的一切。我们只需要提供隧道即可。

client创建好之后,client就准备好给server发送请求了。这一步是必须的,gRPC无法让server端初始化请求发送给client端,请求都是client端初始化的。

但是client初始化请求之后,server端是可以发送多个响应回来的,这个以后再说。这时,client可以随着请求发送一些metadata(元数据),这些metadata是关于请求的,但不是请求对象本身。

请求被发送以后呢,server可以(但不是必须)把metadata返回。所以,你实际上可以在client和server之间进行这种“预约对话”。client可以发送一些metadata,然后server可以把一些metadata发送回来,这些都是发生在server开始处理请求之前。

生命周期的最后一部分就是发送和接收消息。就以简单的情况为例,现在server就应该把响应发送回去了,因为client已经发送了请求,所以响应就是要返回。

注意,关于metadata需要注意的是,gRPC内置的身份认证系统是用来做client和server的身份认证的。

但是这个metadata也为你提供了检查实际用户身份的机制。所以,如果你需要认证或者授权实际用户,就需要在RPC请求这个级别来实现。也就是在这里。

如果是client和server的身份认证,以后再写。。

身份认证

这里指的不是用户的身份认证,而是指多个server和client之间,它们如何识别出来谁是谁,并且能安全的进行消息传输。

在身份认证这方面,gRPC一共有4种身份认证的 机制:

  • 不采取任何措施的连接,也就是不安全的连接。
  • TLS/SSL 连接。
  • 基于 Google Token 的身份认证。
  • 自定义的身份认证提供商。

针对第一种不安全的连接,client和server默认将会采用HTTP/1,没有其他特殊的安全措施,也就是使用明文在网络上传输。所以尽量别用不安全连接,容易被截获。 但是不安全连接却不需要其他任何特殊的处理,不需要CA证书等等,所以适合于快速建立gRPC的情况,后期再添加其他安全措施也行。

如果采用了TLS/SSL,那么想截获传输的消息就比较困难了,而且默认也是使用HTTP/2。HTTP/2的很多实现根本就不支持不安全连接,所以gRPC也不会尝试使用这些不安全连接,但是如果gRPC发现它是在一个安全的连接上面,它就会尝试把这些连接升级到HTTP/2,这时你的消息的传输速度就会变得更快,因为HTTP/2协议的效率更高。

但是需要注意的是,client会对这些证书进行验证,所以不能因为这是一个安全连接,那么就啥也不用干。 client会去检查证书的授权来确保证书的真实性。所以如果你使用的是生成的证书,那么你还需要在client端做一些额外的工作来确保client能够识别出这些server证书的合理性。

当使用基于 Google Token 的身份认证方式时,需要注意的是它需要安全的连接,所以你可以把这种认证方式想象为在SSL/TLS上面的一层。所以你需要有安全连接,在此之上,你才能使用基于Google Token的认证方式。

最后一个是自定义身份认证,你可能想要的是OAuth 2.0这种认证协议,但是gRPC并没有自带OAuth 2.0协议,但是还是有很多用于不同语言的插件可以支持OAuth 2.0的。所以如果有需要的话,可以去官网查一下。

你也可以自己实现一个身份认证协议,但是自己实现的肯定是和语言有关的,而且gRPC也会尽量配合这种语言。所以不是让你的认证协议像gRPC这样工作,而是让你尽量用该语言惯用的方式。所以使用C#开发一个身份认证提供商和使用Go语言可能会不太一样。这块的详细信息需要去官网查阅。

消息传输类型

gRPC的消息传输类型有4种。

  • 第一种是一元的消息,就是简单的请求--响应。
  • 第二种是server streaming(流),server会把数据streaming回给client。
  • 第三种是client streaming,也就是client会把数据streaming给server。
  • 最后是双向streaming。

一元消息

这里有一个server,一个client。gRPC从client发送请求到server开始,然后server做一些处理,生成一个响应并返回。所以在这次远程调用里,有一个请求,一个响应。

这个Protocol Buffer的消息格式大约是这样:

rpc 方法名(请求类型) returns(响应类型)

在这里,即使请求的时候不需要带有数据(参数),你仍然需要传递一个空的请求类型的对象。所以在gRPC里就必须有请求类型和响应类型,因为gRPC不知道你带没带数据,而且未来你有可能需要带上 数据。

Server Streaming

Server Streaming的请求和响应管道还是一样的,但不同的是,虽然一切也是始于client到server的一个请求,然后server处理完之后或者当server正在生成响应的时候,server会一次发送一部分结果回来,也就是把响应sreaming回来。

一个常见的用例就是流式视频。你发送一个请求,想要看某种类型的动作片,然后server会把视频数据的一部分缓冲流发送回来,这样client就不需要等到整个视频一次性返回再看,一次返回一块即可。

当使用这种远程调用的时候,我们只需要在响应类型前面加一个关键字stream即可:

rpc 方法名(请求类型) returns(stream 响应类型)

这样的话,server就相当于会返回一个数组的响应,但是一次只返回一个。

Client Streaming

与Server streaming对应的就是Client streaming。

常见的用例就是上传文件,你可能需要缓冲,这样的话就会把请求分为多块来执行,一次包含一部分数据。需要注意的时候,在发送期间,server会一直等待,直到整个请求都被接收到。在接收到整个请求之前,server不会做任何处理动作。最后当server接收到所有数据并处理完之后,server会发送一个响应返回给client。

不难猜,client streaming的格式是这样的:

rpc 方法名(stream 请求类型) returns(响应类型)

这个远程调用就相当于,一个请求数据的数组,一次发送一个元素,最后所有请求处理完成后返回单个响应。

双向Streaming

最后一种就是双向streaming。在这种方式下,client会发送一个初始的请求,也许接下来还会发送几个请求,与此同时server就开始把响应发送回来了,这时client可以继续发送额外的请求。所以整个过程非常的异步,而且没有什么预定义的结构来规定请求和响应如何构造。所以你也可以想到,在你的server和client之间,肯定是需要异步处理的。

双向streaming的格式如下:

rpc 方法名(stream 请求类型) returns((stream 响应类型)

这也就意味着一个数组的数据将会被发送,一个数组的数据也将会被响应,但都是一次只发送一个数据。

https://www.cnblogs.com/xszjk/articles/5517638.html

1
2
using System.ComponentModel.DataAnnotations;
@Html.ValidationSummary("Errors have occurred:")//显示视图总的验证信息

通过为模型类增加数据描述的DataAnnotations,我们可以容易地为应用程序增加验证的功能。DataAnnotations允许我们描述希望应用在模型属性上的验证规则,ASP.NET MVC将会使用这些DataAnnotations,然后将适当的验证信息返回给用户。

常用的DataAnnotations

Required

属性值必须非空或者不能只是空格,如果允许全空格可以

1
[Required(AllowEmptyStrings = true)`]

DisplayName

显示名–定义表单字段的提示名称

1
[DisplayName("名称")]

StringLength

字符串长度不能超过给定的最大长度,也可以指定最小长度:

1
[StringLength(10, MinimumLength=2)]

Range

范围–数字的可输入范围

1
[Range(10, 20)]

属性值必须在指定的数值范围内,可以使用数值类型的最大最小值比如int.MinValue、int.MaxValue

Bind

绑定–列出在将请求参数绑定到模型的时候,包含和不包含的字段

ScaffoldColumn

支架列-在编辑表单的时候,需要隐藏起来的的字符

1
[ScaffoldColumn(true|false)]

Compare

比较-与制定的字段值进行比较具体见代码

1
[Compare("MyOtherProperty")]

两个属性必须相同值,比如我们要求用户重复输入两次邮件地址时有用

RegularExpre ssion

正则表达式匹配

字符串值必须匹配正则表达式,默认大小写敏感,可以使用(?i)修饰符关闭大小写敏感,比如

1
[RegularExpre ssion("(?i)mypattern")]

DataType

在前端显示的文本框类型

Remote

服务端验证

客户端验证

任何平台都可以靠js来实现客户端验证,但是我这里探讨的是MVC的数据验证。那MVC的客户端数据验证有什么不同呢?在View里添加一行代码:(注意要添加在Form前)@Html.EnableClientValidation();

注意点:这里,其实是这个函数把Model验证转换成了javascript代码,对!它只能转换Model验证,BLL验证无法转换,因为BLL验证涉及到复杂的代码,不可能全部转换成javascript吧?并且BLL验证很多还需要和数据库交互。那如果想把BLL验证也做成”客户端”验证怎么办?(只有可能用ajax实现无刷新验证,而不是真正的客户端验证)

关于Model层数据验证的一些验证规则与相关验证属性标签小集

(1)常规验证(必填字段、字符串长度、正则表达式验证、范围验证、数值、日期等)

1.必填字段

1
2
[Required]
public string FirstName { get; set; }

2.字段长度至多n位:

1
2
[StringLength(160)]
public string FirstName { get; set; }

要求至少n位:

1
2
[StringLength(160, MinimumLength=3)]
public string FirstName { get; set; }

3.正则验证

1
2
[RegularExpre ssion(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}")]
public string Email { get; set; }

4.范围

1
2
[Range(35,44, UIHint("IntSlider"))]//左右托动块
public int Age {get;set;}

小数的情况:

1
2
[Range(typeof(decimal),"0.00","49.99")]
public decimal Price{get;set;}

5.服务端参与的验证

1
2
[Remote("CheckUserName", "Account", ErrorMessage = "该姓名已存在")]
public string UserName { get; set; }

Remote异步请求验证,在[HttpGet]时获取指定Controller里面的指定方法验证,次方法必须是[HttpGet]标记的,返回类型为Json类型的JavaScript对象。

然后在AccountController里指定一个CheckUserName方法:

1
2
3
4
5
public JsonResult CheckUserName(string username)
{
var result = Membership.FindUsersByName(username).Count == 0;
return Json(result, JsonRequestBehavior.AllowGet);
}

6.比较

1
2
3
4
5
[RegularExpre ssion(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}")]
public string Email { get; set; }
[Compare("Email")]
public string EmailConfirm { get; set; }
public string EmailConfirm { get; set; }

7.自定义错误消息

正则:

1
2
[RegularExpre ssion(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}",ErrorMessage="Emaildoesn’tlooklikeavalidemailaddress.")]
publicstringEmail{get;set;}

具体示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
[DisplayName("分组名称")]
[Required(ErrorMessage="请输入账号")]
[StringLength(50,ErrorMessage="请勿输入超过50个字")]
public string Name { get; set; }

[DisplayName("排序字段")]
[DataType(DataType.Password)]
[Required(ErrorMessage="请输入排序字段")]
[RegularExpre ssion(@"^([d]+)$",ErrorMessage="请输入正确的Email.")]
public string Sequence { get; set; }

[DisplayName("备注")]
[DataTyle(DataType.MultilineText)]//数据显示为多文本
[Required(AllowEmptyStrings=true,ErrorMessage="请输入中文姓名")]
[StringLength(50,ErrorMessage="请勿输入超过50个字")]
public string Describe { get; set; }

[DisplayName("账号")]
[Required(ErrorMessage="请输入账号")]
[StringLength(50,ErrorMessage="请勿输入超过50个字")]
public string Account{get;set;}

[DisplayName("密码")]
[DataType(DataType.Password)]
[Required(ErrorMessage="请输入密码")]
[StringLength(50,ErrorMessage="请勿输入超过50个字")]
public string Password{get;set;}

[Required]
[DataType(DataType.Password)]
[Display(Name="Currentpassword")]
public string OldPassword{get;set;}

[Required]
[StringLength(100,ErrorMessage="The{0}mustbeatleast{2}characterslong.",MinimumLength=6)]
[DataType(DataType.Password)]
[Display(Name="Newpassword")]
public string NewPassword{get;set;}

[Display(Name="确认密码")]
[Compare("LogPassword",ErrorMessage="密码必须一致")]
public virtual string LogPasswordConfirm{get;set;}

[Display(Name="Rememberme?")]
public bool RememberMe{get;set;}

[DisplayName("姓名")]
[Required(ErrorMessage="请输入姓名")]
[StringLength(50,ErrorMessage="请勿输入超过50个字")]
public string ChName{get;set;}

[DisplayName("昵称")]
[Required(ErrorMessage="请输入昵称")]
[StringLength(50,ErrorMessage="请勿输入超过50个字")]
public string NickName{get;set;}

[Display(Name="出生日期")]
[Range(typeof(DateTime),"2011-12-31","1950-1-1",ErrorMessage="{0}的范围是{1}到{2}")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]**//显示格式**
public virtual DateTime? Birthday{get;set;}//表示值可为Null

[DisplayName("年龄")]
[Range(1,int.MaxValue,ErrorMessage="请输入大于等于1的数")]
public int Age{get;set;}

[DisplayName("Email")]
[Required(ErrorMessage="请输入Email")]
[StringLength(255,ErrorMessage="请勿输入超过255个字")]
[RegularExpre ssion(@"^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4})$",ErrorMessage="请输入正确的Email.")]
[DataType(DataType.EmailAddress)]
public string Email{get;set;}

[DisplayName("电话号码")]
[RegularExpre ssion(@"^((0d{2,5}-)|(0d{2,5}))?d{7,8}(-d{3,4})?$", ErrorMessage = "电话格式有误。n有效格式为:n①本区7或8位号码[-3或4位分机号码,可选]n②(3~5位区号)7或8位号码[-3或4位分机号码,可选]n③3~5位区号-7或8位号码[-3或4位分机号码,可选]n示例:023-12345678;(023)1234567-1234")]
public string Phone { get; set; }

[Display(Name = "身份证号码")]
[RegularExpre ssion(@"d{17}[d|X]|d{15}", ErrorMessage = "{0}的格式不正确")]
public virtual string IdentityNo { get; set; }

[DisplayName("是否为管理员")]
public bool IsAdmin { get; set; }

[DisplayName("代表图示")]
[Required(ErrorMessage = "请选择代表图示")]

[Range(1, 3, ErrorMessage = "输入的值必须介于1到3之间")]
public int AvatarIcon { get; set; }

[Display(Name = "逻辑删除标识")]
public virtual int IsLogicDelete { get; set; }

[DisplayName("验证码")]
public string AuthCode { get; set; }

[DisplayName("创建日期")]
public DateTime CreateTime { get; set; }

[DisplayName("网址")]
[RegularExpre ssion(@"http(s)?://([w-]+.)+[w-]+(/[w- ./?%&=]*)?", ErrorMessage = "请输入合法的网址!示例:
https://abc.a;http://www.abc.dd") ]
public string Httpaddress { get; set; }

普通文本:

1
2
3
[Required(ErrorMessage="Your last name is required")]
[StringLength(160, ErrorMessage="Your last name is too long")]
public string LastName { get; set; }

占位符:

1
2
3
[Required(ErrorMessage="Your {0} is required.")]
[StringLength(160, ErrorMessage="{0} is too long.")]
public string LastName { get; set; }

数据库约束验证

如果不希望用户使用123456这样的过于简单的密码,可以使用check约束来实现:

1
2
ALTER TABLE staffinfoes WITH NOCHECKADD CONSTRAINT chk_logPassword
CHECK (logPassword NOT IN('123','123123','123456'))

其中,staffinfoes是表名称,chk_logPassword是约束名称,logPassword是列名称