您的位置:首页 > 理论基础 > 计算机网络

项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

2018-07-14 01:34 3027 查看

项目开发中的一些注意事项以及技巧总结

 

1、jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View()、PartialView()等,只能返回json以及content等,但是一般我们在开发的时候也是使用json返回的,此时如果需要渲染界面或者是加载局部视图,我们可以在ajax的success的事件中使用$.html()来渲染后台给前端传的View()数据。一开始我遇到这个问题的时候还很纳闷,为什么ajax给后端穿了POST之后,不能加载局部页面,后来一想,是不是因为ajax是异步请求的原因,于是我把ajax的同步开关打开了,同时将后台传输的视图数据直接使用$html()就渲染到了前端页面。具体请参考:https://blog.csdn.net/m0_37302219/article/details/78272081

2 、后端MVC架构,一般前端都是使用Razor视图,使用@来加载后台传过来的数据比如ViewData,以及ViewBag等。此时,我建议在后端的C#中,我们给ViewBag赋值了之后,在前端的cshtml页面中这么做:
@{
List<T> xxx = ViewBag.xxx;
T xxx = ViewBag.xxx;
}
然后直接使用显式类型的变量来做处理。

3、EF Core中,尤其要注意多对多的关联属性的使用。如果使用不当就会加载不出来数据,直接会出一个null值,在这里请参考
https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/
认真的跟着这个博客做一遍就可以理解了。

4、EF Core中,当我们使用code First建模方式生成数据库时,如果生成数据库有问题了(当然一般是没有问题的,但是我就遇到了,可能也是我操作不当)我强烈建议,不要删除这个生成有问题的数据库,千万不要觉得删除了之后,可以再生成一遍,会出问题的。具体请看
http://www.cjjjs.com/paper/lkkj/2018711212518513.html

5、EF Core下一对多的关联中,需要注意(我不怎么用过EF,也不知道在EF中是不是很正常,在这里仅仅说EF Core,可能两者在我要说的这点上是一致的)。我们以官方为例:https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db
一个博客(Blog)对应多个文章(Post),一篇文章对应一个博客(Blog)。此时,当我们使用上下文来获取数据库数据时,代码如下:
List<Blog> Blogs = _context.Blogs.ToList(); 
List<Post> Posts = _context.Posts.ToList(); // 如果不写这句代码,不从数据库里面加载处理关联属性Post的数据,那么Blogs里面的Post关联属性也将会是null值,这点是要特别注意的,只有写了这句代码,Blogs类型的关联属性Post才能有数据。

6、项目中一定要设计好数据库,一般来讲如果有级联关系的,就设置为两个表(这里的级联关系我是这么定义的:假设一个类别下有多条数据,这就是一个级联关系,就像是一个省份下面有多个城市)。在实体方面,我们可以认为是一对多关系,如果是一对多关系,我们就建两个表。设计好了数据表,对我们的开发有着莫大的好处,有时候多加一个字段和少一个字段完全不一样。

7、建议在列表数据中使用ForEach来遍历,也就是一般的List<T>变量,使用List<T>.ForEach来遍历数据处理逻辑

 

基于Repository模式设计项目架构—你可以参考的项目架构设计

 

关于Repository模式,直接百度查就可以了,其来源是《企业应用架构模式》。
我们新建一个Infrastructure文件夹,这里就是基础设施部分,EF Core的上下文类以及Repository层都放在这里面。
新建一个IReposotory的接口,其内容就是封装了基本的CRUD:

public interface IStudentRepository : IRepository<Student>
{
int AddStudent(Student student);
}

然后我们创建一个实体类的Repository:

public class StudentRepository : BaseRepository<Student>, IStudentRepository
{
private readonly SchoolContext _context;

public StudentRepository(SchoolContext context)
: base(context)
{
_context = context;
}

int AddStudent(Student student)
{
_context.Create(student);
}
}

在这里就已经做好了我们要做的了。接下来的就是注入依赖、在控制器里面的使用了。

我们完全可以自己来定制自己的Repository模式下的项目。其实整个的架构没有什么,我们只是将所有的CRUD操作封装到了IRepository接口里面,然后在BaseRepository中实现了一遍,而且如果你细心的话,你会发现IRepository里面的CRUD操作都是基于已有的扩展方法里面的,就是linq扩展方法的Add等源码,同时我们在BaseRepository类中,提供了DbSet<TEntity>属性以及查询数据集IQueryable<DbSet<TEntity>>,这也是有必要的,可以省却我们很多不必要的代码,因为我们所有的CRUD都是基于这两个的。然后我们基于BaseRepository来实现实体类的Repository,同时继承按需增加的IEntityRepository接口。但是在这里要注意,我们将DbContext的子类都放在了Infrastructure文件夹里面,是因为,一般我们继承自DbContext的子类都是操作数据库的中间类,属于基础设施一块,所以将其放在Infrastructure文件夹比较合适。

参考资料:
MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext

分享基于Entity Framework的Repository模式设计(附源码)

《ASP.NET MVC框架揭秘》源码中的示例项目源码 S1402

 

 

EF Core中的多对多映射如何实现?

 

EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的。但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考《你必须掌握的EntityFramework 6.X与Core 2.0》一文。在这里我就详细的说下如何在EF core下实现。
首先就是实体类的建立:

public class Post
{
public int PostId { get; set; }
public string Title { get; set; }

public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}

public class Tag
{
public int TagId { get; set; }
public string Text { get; set; }

public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}

public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; }

public int TagId { get; set; }
public Tag Tag { get; set; }
}

接下来就是映射了。派生自DbContext的上下文类:

public class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>().ToTable("PostTags");
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
}
}

这样就完成了我们的多对多映射了。我们只是通过多建立了一个表,将两个实体类的Id作为联合主键。

在Identity框架中,如果你细心点,你会发现有个userroles表,这个表是就是用来做Users表和Roles表的映射的。那么接下来我们只要新建一个实体类,随后在上下文类中映射到表:

modelBuilder.Entity<UserRoles>.ToTable("userroles");

这样就可以了。然后我们就可以很方便的给用户添加角色了。

参考链接:https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/

 

asp.net core下的如何给网站做安全设置

 

首先,我们来看下stack overflow网站的请求头文件:

可以看到一些我们熟悉或是陌生的HTTP头部文件字段。
在这里我们在对HTTP输入流的头部文件中,做一些基本的防护。首先要明确,既然我们是对HTTP头部做处理,那么就需要在Startup.cs类的 
Configuration方法中做处理,因为这里就是处理HTTP输入流的。

首先做一些基本的处理,比如中间件和基本的类:

这里的头部信息是我们定义好的,用来增加或是删除头部信息,然后就是我们的中间件:

public class SecurityHeadersMiddleware
{
private readonly RequestDelegate _next;
private readonly SecurityHeadersPolicy _policy;

public SecurityHeadersMiddleware(RequestDelegate next, SecurityHeadersPolicy policy)
{
_next = next;
_policy = policy;
}

public async Task Invoke(HttpContext context)
{
IHeaderDictionary headers = context.Response.Headers;

foreach (var headerValuePair in _policy.SetHeaders)
{
headers[headerValuePair.Key] = headerValuePair.Value;
}

foreach (var header in _policy.RemoveHeaders)
{
headers.Remove(header);
}

await _next(context);
}
}

基于IApplicationBuilder接口做一个中间件的扩展方法:

public static class MiddlewareExtensions
{
public static IApplicationBuilder UseSecurityHeadersMiddleware(this IApplicationBuilder app, SecurityHeadersBuilder builder)
{
SecurityHeaderPolicy policy = builder.Build();
return app.UseMiddleware<SecurityHeadersMiddleware>(policy);
}
}

封装好相关的安全类:

public class SecurityHeadersBuilder
{
private readonly SecurityHeadersPolicy _policy = new SecurityHeadersPolicy();

public SecurityHeadersBuilder AddDefaultSecurePolicy()
{
AddFrameOptionsDeny();
AddXssProtectionBlock();
AddContentTypeOptionsNoSniff();
AddStrictTransportSecurityMaxAge();
RemoveServerHeader();

return this;
}

public SecurityHeadersBuilder AddFrameOptionsDeny()
{
_policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.Deny;
return this;
}

public SecurityHeadersBuilder AddFrameOptionsSameOrigin()
{
_policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.SameOrigin;
return this;
}

public SecurityHeadersBuilder AddFrameOptionsSameOrigin(string uri)
{
_policy.SetHeaders[FrameOptionsConstants.Header] = string.Format(FrameOptionsConstants.AllowFromUri, uri);
return this;
}

public SecurityHeadersBuilder RemoveServerHeader()
{
_policy.RemoveHeaders.Add(ServerConstants.Header);
return this;
}

public SecurityHeadersBuilder AddCustomHeader(string header, string value)
{
_policy.SetHeaders[header] = value;
return this;
}

public SecurityHeadersBuilder RemoveHeader(string header)
{
_policy.RemoveHeaders.Add(header);
return this;
}

public SecurityHeadersPolicy Build()
{
return _policy;
}
}

最后注入到HTTP的输入流中:

app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder()
.AddDefaultSecurePolicy()
);

然后我们浏览一下网页,就可以在HTTP的头部信息中看到:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000
X-Powered-By: ASP.NET

还有一个就是CSRF的防护,如果之前你用过ASP.NET MVC,在最基本的MVC模板中,可能你会留意到已有的cshtml页面中的form表单有这么一句:

@Html.AntiForgeryToken()

这就是微软在MVC框架中为我们提供的防护CSRF的方法。我们在表单中直接使用上面那句代码就可以了,然后在表单提交的Action方法中:

[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult AntiForm(string message)
{
return Content(message);
}

使用[ValidateAntiForgeryToken]属性,来验证CSRF。


参考链接:
How to add security headers in ASP.NET Core using custom middleware(如何使用自定义中间件在ASP.NET Core中添加安全标头)

初探CSRF在ASP.NET Core中的处理方式

代码地址:
https://github.com/RyanOvO/aspnetcore-fileup-demo

 

 

获取服务端https证书

最近开发一个需求,涉及获取服务端https证书。一般进行https调用我们都不太关心底层细节,直接使用WebClient或者HttpWebRequest来发送请求,这两种方法都无法获取证书信息,需要用到ServicePoint,这个类用于提供HTTP连接的管理。

 

写个Demo,拿新浪首页试一下:

using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace GetServerCertificateDemo
{
class Program
{
static void Main(string[] args)
{
//用WebClient访问新浪首页
var http = new WebClient();
var uri = new Uri("https://www.sina.com.cn");
http.DownloadString(uri);

//通过Uri获取ServicePoint
var servicePoint = ServicePointManager.FindServicePoint(uri);

//取服务端证书,X509Certificate格式,转一下
var serverCert = new X509Certificate2(servicePoint.Certificate);
Console.WriteLine("颁发给:{0}", serverCert.Subject);
Console.WriteLine("颁发者:{0}", serverCert.Issuer);
Console.WriteLine("序列号:{0}", serverCert.SerialNumber);
Console.WriteLine("指  纹:{0}", serverCert.Thumbprint);
Console.WriteLine("起  始:{0}", serverCert.NotBefore);
Console.WriteLine("过  期:{0}", serverCert.NotAfter);
}
}
}

 

运行看效果:

上半部分是程序运行结果,下面是用Firefox查看的服务端证书信息,各项信息都能对应上。如果程序中涉及多个不同服务器的访问也没关系,关键在于根据Uri获取ServicePoint,然后取到的证书就是此服务器的了。

 

 

Js异常捕获

 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
function demo(){
try{
alert(str);
}catch(e){
alert(e);
}
}
demo();
</script>

<form>
<input id="txt" type="text"/>
<input id="btn" type="button" onclick="demo1()" value="按钮" />
</form>
<script>
function demo1(){
try{
var e=document.getElementById("txt").value;
if(e==""){
throw "请输入";
}
}catch(e){
alert(e);
}

}
</script>
</body>
</html>

 

js事件

onLoad    网页加载事件

onUnload    关闭网页事件

 

反射:获取Class对象的三种方式

 

数据库_mysql多表操作

 

 

数据库_mysql多表操作

 

多表操作

       实际开发中,一个项目通常需要很多张表才能完成。例如:一个商城项目就需要分类表(category)、商品表(products)、订单表(orders)等多张表。且这些表的数据之间存在一定的关系。

1.1    表与表之间的关系

l  一对多关系:

n  常见实例:客户和订单,分类和商品,部门和员工.

n  一对多建表原则:在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键.

l  多对多关系:

n  常见实例:学生和课程、用户和角色

n  多对多关系建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键.

l  一对一关系:(了解)

n  在实际的开发中应用不多.因为一对一可以创建成一张表.

n  两种建表原则:

u  外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一unique。

u  外键是主键:主表的主键和从表的主键,形成主外键关系。

1.2    外键约束

现在我们有两张表“分类表”和“商品表”,为了表明商品属于哪个分类,通常情况下,我们将在商品表上添加一列,用于存放分类cid的信息,此列称为:外键

此时“分类表category”称为:主表,“cid”我们称为主键。“商品表products”称为:从表,category_id称为外键。我们通过主表的主键和从表的外键来描述主外键关系,呈现就是一对多关系。

         外键特点:

u  从表外键的值是对主表主键的引用。

u  从表外键类型,必须与主表主键类型一致。

 

l  声明外键约束       

语法:alter table 从表 add [constraint] [外键名称] foreign key (从表外键字段名) references 主表 (主表的主键);

[外键名称]用于删除外键约束的,一般建议“_fk”结尾

    altertable 从表 drop foreignkey 外键名称

l  使用外键目的:

n  保证数据完整性

 

1.3    一对多操作

1.3.1    分析

  • category分类表,为一方,也就是主表,必须提供主键cid

  • products商品表,为多方,也就是从表,必须提供外键category_id

  • 1.3.2    实现:分类和商品

     

    ###创建分类表

    create table category(

      cidvarchar(32) PRIMARY KEY ,

      cnamevarchar(100)        #分类名称

    );

     

    # 商品表

    CREATE TABLE `products` (

      `pid`varchar(32) PRIMARY KEY  ,

      `name`VARCHAR(40) ,

      `price`DOUBLE

    );

     

    #添加外键字段

    alter table products add column category_id varchar(32);

     

    #添加约束

    alter table products add constraint product_fkforeign key (category_id) references category (cid);

     

    1.3.3    操作

    #1 向分类表中添加数据

    INSERT INTO category (cid ,cname) VALUES('c001','服装');

     

    #2 向商品表添加普通数据,没有外键数据,默认为null

    INSERT INTO products (pid,pname) VALUES('p001','商品名称');

     

    #3 向商品表添加普通数据,含有外键信息(数据存放在)

    INSERT INTO products (pid ,pname ,category_id)VALUES('p002','商品名称2','c001');

     

    #4 向商品表添加普通数据,含有外键信息(数据不存在) -- 不能异常

    INSERT INTO products (pid ,pname ,category_id)VALUES('p003','商品名称2','c999');

     

    #5 删除指定分类(分类被商品使用) -- 执行异常

    DELETE FROM category WHERE cid = 'c001';

     

    1.4    多对多

    1.4.1    分析

  • 商品和订单多对多关系,将拆分成两个一对多。

  • products商品表,为其中一个一对多的主表,需要提供主键pid

  • orders 订单表,为另一个一对多的主表,需要提供主键oid

  • orderitem中间表,为另外添加的第三张表,需要提供两个外键oid和pid

 

 

1.4.2    实现:订单和商品

 

### 商品表[已存在]

 

### 订单表

create table `orders`(

  `oid`varchar(32) PRIMARY KEY ,

 `totalprice` double   #总计

);

 

### 订单项表

create table orderitem(

  oidvarchar(50),-- 订单id

  pidvarchar(50)-- 商品id

);

 

###---- 订单表和订单项表的主外键关系

alter table `orderitem` add constraintorderitem_orders_fk foreign key (oid) references orders(oid);

 

###---- 商品表和订单项表的主外键关系

alter table `orderitem` add constraintorderitem_product_fk foreign key (pid) references products(pid);

 

### 联合主键(可省略)

alter table `orderitem` add primary key (oid,pid);

 

 

1.4.3    操作

#1 向商品表中添加数据

INSERT INTO products (pid,pname) VALUES('p003','商品名称');

 

#2 向订单表中添加数据

INSERT INTO orders (oid ,totalprice)VALUES('x001','998');

INSERT INTO orders (oid ,totalprice)VALUES('x002','100');

 

#3向中间表添加数据(数据存在)

INSERT INTO orderitem(pid,oid)VALUES('p001','x001');

INSERT INTO orderitem(pid,oid)VALUES('p001','x002');

INSERT INTO orderitem(pid,oid)VALUES('p002','x002');

 

#4删除中间表的数据

DELETE FROM orderitem WHERE pid='p002' AND oid ='x002';

 

#5向中间表添加数据(数据不存在) -- 执行异常

INSERT INTO orderitem(pid,oid)VALUES('p002','x003');

 

#6删除商品表的数据 -- 执行异常

DELETE FROM products WHERE pid = 'p001';

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐