C#操作MongoDB入门 - 荆棘中的百合花 - 博客园

mikel阅读(874)

来源: C#操作MongoDB入门 – 荆棘中的百合花 – 博客园

1、MongoDB安装及配置

(1)下载:

mongodb官网 https://www.mongodb.com/download-center

进入官网下载页,你会发现版本都是windows Server……  但是不要担心,本人亲测win10也可以用,所以放心大胆的下载就可以了。安装过程比较简单,下一步就可以了,就不多说了。

(2)配置

在“E:\MongoDB“(与默认安装位置不同,需要自己找到这个文件夹)目录下新建“data”文件夹,它将会作为数据存放的根文件夹。

在“E:\MongoDB”目录下新建“log”文件夹,作为日志文件夹。

 

    配置Mongo服务端:

以管理员身份打开CMD窗口,按照如下方式输入命令:
> E:
> cd MongoDB

>cd bin
> mongod –dbpath “E:\MongoDB\data”

 

然后在浏览器输入:http://localhost:27017/,可以看到如下提示:

You are trying to access MongoDB on the native driver port. For http diagnostic access, add 1000 to the port number (可能会和这个不太一样,有字就行)

如此,MongoDB数据库服务已经成功启动了。

 

封装服务:

还是以管理员身份运行cmd,进入bin目录,执行下列命令
>mongod -dbpath “D:\MongoDB\data” -logpath “D:\MongoDB\log\MongoDB.log” -install -serviceName “MongoDB”

这里–MongoDB.log就是开始建立的日志文件,–serviceName “MongoDB” 服务名为MongoDB。

接着启动mongodb服务

> E:\MongoDB>NET START MongoDB

服务启动成功后可以测试一下

进入bin目录,输入mongo显示如下信息,则服务启动成功。

E:\MongoDB\bin>mongo
MongoDB shell version: 3.2.9
connecting to: test
>

至此安装配置就完成了。

2、安装MongoDB的C#驱动

打开C#,新建项目,点击 工具>Nuget程序包管理器>管理解决方案的Nuget程序包>联机,

在搜索栏搜索mongodb

安装MongoDB.Driver,MongoDB.Bson,MongoDB.Driver.Core三个程序包。

然后添加引用

using MongoDB.Driver;
using MongoDB.Bson;

3、代码

接下来就是写代码了,以下是我写的最简单的增删改查操作(水平有限,凑合看)

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
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MongoDB.Driver;
using MongoDB.Bson;
namespace MongoDBTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Mongo();
        }
        public void Mongo()
        {
            //建立连接
            var client = new MongoClient();
            //建立数据库
            var database = client.GetDatabase("TestDb");
            //建立collection
            var collection = database.GetCollection<BsonDocument>("foo");
            var document = new BsonDocument
            {
                {"name","MongoDB"},
                {"type","Database"},
                {"count",1},
                {"info",new BsonDocument{{"x",203},{"y",102}}}
            };
            //插入数据
            collection.InsertOne(document);
            var count = collection.Count(document);
            Console.WriteLine(count);
            //查询数据
            var document1 = collection.Find(document);
            Console.WriteLine(document1.ToString());
            //更新数据
            var filter = Builders<BsonDocument>.Filter.Eq("name""MongoDB");
            var update = Builders<BsonDocument>.Update.Set("name""Ghazi");
            collection.UpdateMany(filter, update);
            //删除数据
            var filter1 = Builders<BsonDocument>.Filter.Eq("count", 101);
            collection.DeleteMany(filter1);
            BsonDocument document2 = new BsonDocument();
            document2.Add("name""MongoDB");
            document2.Add("type""Database");
            document2.Add("count""1");
            collection.InsertOne(document2);
        }
    }
}

 

4、参考资料

http://mongodb.github.io/mongo-csharp-driver/2.2/getting_started/ 这个一定要静下心去看,虽然是英文的,但是看代码就行,还是不难的。

http://www.cnblogs.com/qq75077027/category/441114.html 这个是大神写的,讲的很全面。

http://wenku.baidu.com/link?url=NYAYJfx907QwrzVmcvrZAYqZUML6tiqClsJMNIxKjmR MongoDB权威指南中文版。

Redis的事务功能详解_一念永恒-CSDN博客_redis事务

mikel阅读(626)

来源: Redis的事务功能详解_一念永恒-CSDN博客_redis事务

MULTI、EXEC、DISCARD和WATCH命令是Redis事务功能的基础。Redis事务允许在一次单独的步骤中执行一组命令,并且可以保证如下两个重要事项:

Redis会将一个事务中的所有命令序列化,然后按顺序执行。Redis不可能在一个Redis事务的执行过程中插入执行另一个客户端发出的请求。这样便能保证Redis将这些命令作为一个单独的隔离操作执行。 > 在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行。因此,Redis事务能够保证原子性。EXEC命令会触发执行事务中的所有命令。因此,当某个客户端正在执行一次事务时,如果它在调用MULTI命令之前就从Redis服务端断开连接,那么就不会执行事务中的任何操作;相反,如果它在调用EXEC命令之后才从Redis服务端断开连接,那么就会执行事务中的所有操作。当Redis使用只增文件(AOF:Append-only File)时,Redis能够确保使用一个单独的write(2)系统调用,这样便能将事务写入磁盘。然而,如果Redis服务器宕机,或者系统管理员以某种方式停止Redis服务进程的运行,那么Redis很有可能只执行了事务中的一部分操作。Redis将会在重新启动时检查上述状态,然后退出运行,并且输出报错信息。使用redis-check-aof工具可以修复上述的只增文件,这个工具将会从上述文件中删除执行不完全的事务,这样Redis服务器才能再次启动。

从2.2版本开始,除了上述两项保证之外,Redis还能够以乐观锁的形式提供更多的保证,这种形式非常类似于“检查再设置”(CAS:Check And Set)操作。本文稍后会对Redis的乐观锁进行描述。

一、相关命令
1. MULTI
用于标记事务块的开始。Redis会将后续的命令逐个放入队列中,然后才能使用EXEC命令原子化地执行这个命令序列。

这个命令的运行格式如下所示:

MULTI

这个命令的返回值是一个简单的字符串,总是OK。

2. EXEC
在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。

当使用WATCH命令时,只有当受监控的键没有被修改时,EXEC命令才会执行事务中的命令,这种方式利用了检查再设置(CAS)的机制。

这个命令的运行格式如下所示:

EXEC

这个命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值。 当使用WATCH命令时,如果事务执行中止,那么EXEC命令就会返回一个Null值。

3. DISCARD
清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。

如果使用了WATCH命令,那么DISCARD命令就会将当前连接监控的所有键取消监控。

这个命令的运行格式如下所示:

DISCARD
这个命令的返回值是一个简单的字符串,总是OK。

4. WATCH
当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的。

这个命令的运行格式如下所示:

WATCH key [key …]
这个命令的返回值是一个简单的字符串,总是OK。

对于每个键来说,时间复杂度总是O(1)。

5. UNWATCH
清除所有先前为一个事务监控的键。

如果你调用了EXEC或DISCARD命令,那么就不需要手动调用UNWATCH命令。

这个命令的运行格式如下所示:

UNWATCH
这个命令的返回值是一个简单的字符串,总是OK。

时间复杂度总是O(1)。

二、使用方法
使用MULTI命令便可以进入一个Redis事务。这个命令的返回值总是OK。此时,用户可以发出多个Redis命令。Redis会将这些命令放入队列,而不是执行这些命令。一旦调用EXEC命令,那么Redis就会执行事务中的所有命令。

相反,调用DISCARD命令将会清除事务队列,然后退出事务。

以下示例会原子化地递增foo键和bar键的值:

 

 

 

正如从上面的会话所看到的一样,EXEC命令的返回值是一个数组,其中的每个元素都分别是事务中的每个命令的返回值,返回值的顺序和命令的发出顺序是相同的。

当一个Redis连接正处于MULTI请求的上下文中时,通过这个连接发出的所有命令的返回值都是QUEUE字符串(从Redis协议的角度来看,返回值是作为状态回复(Status Reply)来发送的)。当调用EXEC命令时,Redis会简单地调度执行事务队列中的命令。

三、事务内部的错误
在一个事务的运行期间,可能会遇到两种类型的命令错误:

一个命令可能会在被放入队列时失败。因此,事务有可能在调用EXEC命令之前就发生错误。例如,这个命令可能会有语法错误(参数的数量错误、命令名称错误,等等),或者可能会有某些临界条件(例如:如果使用maxmemory指令,为Redis服务器配置内存限制,那么就可能会有内存溢出条件)。
在调用EXEC命令之后,事务中的某个命令可能会执行失败。例如,我们对某个键执行了错误类型的操作(例如,对一个字符串(String)类型的键执行列表(List)类型的操作)。

可以使用Redis客户端检测第一种类型的错误,在调用EXEC命令之前,这些客户端可以检查被放入队列的命令的返回值:如果命令的返回值是QUEUE字符串,那么就表示已经正确地将这个命令放入队列;否则,Redis将返回一个错误。如果将某个命令放入队列时发生错误,那么大多数客户端将会中止事务,并且丢弃这个事务。

然而,从Redis 2.6.5版本开始,服务器会记住事务积累命令期间发生的错误。然后,Redis会拒绝执行这个事务,在运行EXEC命令之后,便会返回一个错误消息。最后,Redis会自动丢弃这个事务。

在Redis 2.6.5版本之前,如果发生了上述的错误,那么在客户端调用了EXEC命令之后,Redis还是会运行这个出错的事务,执行已经成功放入事务队列的命令,而不会关心先前发生的错误。从2.6.5版本开始,Redis在遭遇上述错误时,会采用先前描述的新行为,这样便能轻松地混合使用事务和管道。在这种情况下,客户端可以一次性地将整个事务发送至Redis服务器,稍后再一次性地读取所有的返回值。

相反,在调用EXEC命令之后发生的事务错误,Redis不会进行任何特殊处理:在事务运行期间,即使某个命令运行失败,所有其他的命令也将会继续执行。

这种行为在协议层面上更加清晰。在以下示例中,当事务正在运行时,有一条命令将会执行失败,即使这条命令的语法是正确的:

 

 

 

上述示例的EXEC命令的返回值是批量的字符串,包含两个元素,一个是OK代码,另一个是-ERR错误消息。客户端会根据自身的程序库,选择一种合适的方式,将错误信息提供给用户

需要注意的是,即使某个命令执行失败,事务队列中的所有其他命令仍然会执行 —— Redis不会停止执行事务中的命令。

再看另一个示例,再次使用telnet通信协议,观察命令的语法错误是如何尽快报告给用户的:

 

 

 

这一次,由于INCR命令的语法错误,Redis根本就没有将这个命令放入事务队列。

四、为什么Redis不支持回滚?
如果你具备关系型数据库的知识背景,你就会发现一个事实:在事务运行期间,虽然Redis命令可能会执行失败,但是Redis仍然会执行事务中余下的其他命令,而不会执行回滚操作,你可能会觉得这种行为很奇怪。

然而,这种行为也有其合理之处:

只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(在将这个命令放入事务队列期间,Redis能够发现此类问题),或者对某个键执行不符合其数据类型的操作:实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。
Redis已经在系统内部进行功能简化,这样可以确保更快的运行速度,因为Redis不需要事务回滚的能力。

对于Redis事务的这种行为,有一个普遍的反对观点,那就是程序有可能会有缺陷(bug)。但是,你应当注意到:事务回滚并不能解决任何程序错误。例如,如果某个查询会将一个键的值递增2,而不是1,或者递增错误的键,那么事务回滚机制是没有办法解决这些程序问题的。请注意,没有人能解决程序员自己的错误,这种错误可能会导致Redis命令执行失败。正因为这些程序错误不大可能会进入生产环境,所以我们在开发Redis时选用更加简单和快速的方法,没有实现错误回滚的功能。

五、丢弃命令队列
DISCARD命令可以用来中止事务运行。在这种情况下,不会执行事务中的任何命令,并且会将Redis连接恢复为正常状态。示例如下所示:

 

 

 

六、通过CAS操作实现乐观锁
Redis使用WATCH命令实现事务的“检查再设置”(CAS)行为。

作为WATCH命令的参数的键会受到Redis的监控,Redis能够检测到它们的变化。在执行EXEC命令之前,如果Redis检测到至少有一个键被修改了,那么整个事务便会中止运行,然后EXEC命令会返回一个Null值,提醒用户事务运行失败。

例如,设想我们需要将某个键的值自动递增1(假设Redis没有INCR命令)。

首次尝试的伪码可能如下所示:

val = GET mykey
val = val + 1
SET mykey $val
如果我们只有一个Redis客户端在一段指定的时间之内执行上述伪码的操作,那么这段伪码将能够可靠的工作。如果有多个客户端大约在同一时间尝试递增这个键的值,那么将会产生竞争状态。例如,客户端-A和客户端-B都会读取这个键的旧值(例如:10)。这两个客户端都会将这个键的值递增至11,最后使用SET命令将这个键的新值设置为11。因此,这个键的最终值是11,而不是12。

现在,我们可以使用WATCH命令完美地解决上述的问题,伪码如下所示:

WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
由上述伪码可知,如果存在竞争状态,并且有另一个客户端在我们调用WATCH命令和EXEC命令之间的时间内修改了val变量的结果,那么事务将会运行失败。

我们只需要重复执行上述伪码的操作,希望此次运行不会再出现竞争状态。这种形式的锁就被称为乐观锁,它是一种非常强大的锁。在许多用例中,多个客户端可能会访问不同的键,因此不太可能发生冲突 —— 也就是说,通常没有必要重复执行上述伪码的操作。

七、WATCH命令详解
那么WATCH命令实际做了些什么呢?这个命令会使得EXEC命令在满足某些条件时才会运行事务:我们要求Redis只有在所有受监控的键都没有被修改时,才会执行事务。(但是,相同的客户端可能会在事务内部修改这些键,此时这个事务不会中止运行。)否则,Redis根本就不会进入事务。(注意,如果你使用WATCH命令监控一个易失性的键,然后在你监控这个键之后,Redis再使这个键过期,那么EXEC命令仍然可以正常工作。)

WATCH命令可以被调用多次。简单说来,所有的WATCH命令都会在被调用之时立刻对相应的键进行监控,直到EXEC命令被调用之时为止。你可以在单条的WATCH命令之中,使用任意数量的键作为命令参数。

当调用EXEC命令时,所有的键都会变为未受监控的状态,Redis不会管事务是否被中止。当一个客户单连接被关闭时,所有的键也都会变为未受监控的状态。

你还可以使用UNWATCH命令(不需要任何参数),这样便能清除所有的受监控键。当我们对某些键施加乐观锁之后,这个命令有时会非常有用。因为,我们可能需要运行一个用来修改这些键的事务,但是在读取这些键的当前内容之后,我们可能不打算继续进行操作,此时便可以使用UNWATCH命令,清除所有受监控的键。在运行UNWATCH命令之后,Redis连接便可以再次自由地用于运行新事务。

如何使用WATCH命令实现ZPOP操作呢?

本文将通过一个示例,说明如何使用WATCH命令创建一个新的原子化操作(Redis并不原生支持这个原子化操作),此处会以实现ZPOP操作为例。这个命令会以一种原子化的方式,从一个有序集合中弹出分数最低的元素。以下源码是最简单的实现方式:

WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC
如果伪码中的EXEC命令执行失败(例如,返回Null值),那么我们只需要重复运行这个操作即可。

八、Redis脚本和事务
根据定义,Redis脚本也是事务型的。因此,你可以通过Redis事务实现的功能,同样也可以通过Redis脚本来实现,而且通常脚本更简单、更快速。

由于Redis从2.6版本才开始引入脚本特性,而事务特性是很久以前就已经存在的,所以目前的版本才有两个看起来重复的特性。但是,我们不太可能在短时间内移除对事务特性的支持。因为,即使不用求助于Redis脚本,用户仍然能够规避竞争状态,这从语义上来看是适宜的。还有另一个更重要的原因,Redis事务特性的实现复杂度是最小的。

但是,在相当长的一段时间之内,我们不大可能看到整个用户群体都只使用Redis脚本。如果发生这种情况,那么我们可能会废弃,甚至最终移除Redis事务。
————————————————
版权声明:本文为CSDN博主「森林屿麓」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013967628/article/details/84501515

用宝塔本地搭建php,Windows系统如何使用宝塔面板一键快速搭建本地服务器环境(LNMP/LAMP)..._无名沙的博客-CSDN博客

mikel阅读(1140)

来源: 用宝塔本地搭建php,Windows系统如何使用宝塔面板一键快速搭建本地服务器环境(LNMP/LAMP)…_无名沙的博客-CSDN博客

宝塔面板

宝塔面板类似于wamp,xampp等一键集成环境。但是宝塔面板操作更简单,更新更快。个人建议使用宝塔替代wamp。

(LNMP等环境不再赘述,请自行百度。建议使用LNMP环境,即Nginx+MySQL+PHP环境)

下载

前往宝塔面板官网https://www.bt.cn/,点击Windows版,然后点击立即安装,下载好之后是一个很小压缩包,安装程序就在其中。(图片见文章头图)

安装

直接一键安装,宝塔面板的安装目录请添加至杀毒软件的白名单里面,否则可能导致服务无法启动。

安装环境

配置环境

30744f580054b6c2ca640dc0a44a5ccf.png

安装好之后,点击最顶部的环境,依次安装Nginx,PHP,MySQL

10f560fe92db6a0b19e632c188c2003d.png

初始化面板密码

e76b7e7507c0d2a369c03be2bd5d9099.png

这一步很重要,在右上角点击选项(三条横线),然后点击初始化密码。此密码是用来登录web面板得账户与密码。

添加站点

ed7c0d2a96f22e6217a8066191720c6e.png

打开web面板之后,登录。

a5b14130935ebc22b5c52284f32757fc.png

然后在左侧点击网站,域名就是网址,输入127.0.0.1,创建数据库,记住账号与密码。

在浏览器输入127.0.0.1即可访问新建的网站

其余软件安装

进入web面板之后,在左侧点击软件管理,可以看到PHPmyadmin等软件,直接点击安装即可。

数据库管理请点击相应菜单,直接选择即可。

修改数据库端口

进入web面板之后,点击左侧软件管理,找到MySQL,点击右边的设置

如图,找到端口,修改即可。

9cca176560a83f7a8a99b186c2c845d2.png

其余软件端口修改

ftp,ssh等端口修改与MySQL一致

建站:

上传建站程序到后台,绑定域名即可,譬如小程序后端程序、PC建站程序

ab9b75b15d14f25114c6d1e0df74549b.png

一般而言,小程序都有前端显示代码和后端逻辑层代码,所以上传到服务器上的都是后台文件。

现在的小程序开源软件很多,一般都是开源直接下载到的,截图仅是个案,大家实际操作的时候以对应项目为准。

如果有什么需要指导的也可以加我QQ好友,一起交流1975768644

文章来源:https://www.src1024.com/xy/jc/2135.html

ASP.NET Web API与Owin OAuth:调用与用户相关的Web API - dudu - 博客园

mikel阅读(990)

来源: ASP.NET Web API与Owin OAuth:调用与用户相关的Web API – dudu – 博客园

在前一篇博文中,我们通过以 OAuth 的 Client Credential Grant 授权方式(只验证调用客户端,不验证登录用户)拿到的 Access Token ,成功调用了与用户无关的 Web API。

在这篇博文中,我们将以 OAuth 的 Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。

对应的应用场景是:为自家的网站开发手机 App(非第三方 App),只需用户在 App 上登录,无需用户对 App 所能访问的数据进行授权。

根据 OAuth 规范,客户端获取 Access Token 的请求方式如下:

复制代码
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w
复制代码

根据上面的请求方式,在 C# 中用 HttpClient 实现一个简单的客户端,代码如下:

复制代码
public class OAuthClientTest
{
    private HttpClient _httpClient;

    public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    } 

    [Fact]
    public async Task Get_Accesss_Token_By_Resource_Owner_Password_Credentials_Grant()
    {
        Console.WriteLine(await GetAccessToken());
    }

    private async Task<string> GetAccessToken()
    {
        var clientId = "1234";
        var clientSecret = "5678";

        var parameters = new Dictionary<string, string>();            
        parameters.Add("grant_type", "password");
        parameters.Add("username", "博客园团队");
        parameters.Add("password", "cnblogs.com");

        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
            "Basic",
            Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret))
            );

        var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
        var responseValue = await response.Content.ReadAsStringAsync();
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return JObject.Parse(responseValue)["access_token"].Value<string>();
        }
        else
        {
            Console.WriteLine(responseValue);
            return string.Empty;
        }
    }
}
复制代码

(注:与之前相比,这里的 client_id/client_secret 改为了 Basic Authorization,以更好的遵循 OAuth 规范)

在服务端,基于 Owin OAuth, 针对 Resource Owner Password Credentials Grant 的授权方式,只需重载 OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials() 方法即可。代码如下:

复制代码
public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    //...

    public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    {
        //调用后台的登录服务验证用户名与密码

        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        await base.GrantResourceOwnerCredentials(context);
    }
}
复制代码

完整的CNBlogsAuthorizationServerProvider实现代码如下(与之前相比,context.TryGetFormCredentials 改为了 context.TryGetBasicCredentials):

复制代码
public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        string clientId;
        string clientSecret;
        context.TryGetBasicCredentials(out clientId, out clientSecret);

        if (clientId == "1234"
            && clientSecret == "5678")
        {
            context.Validated(clientId);
        }

        await base.ValidateClientAuthentication(context);
    }

    public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    {
        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        await base.GrantClientCredentials(context);
    }

    public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    {
        //调用后台的登录服务验证用户名与密码

        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        await base.GrantResourceOwnerCredentials(context);
    }
}
复制代码

这样,运行客户端程序就可以拿到 Access Token 了。

接下来,我们拿着以这种方式获取的 Access Token,就可以调用与用户相关的 Web API 了。

在服务端我们通过一个简单的 Web API 测试一下,代码如下:

复制代码
public class UsersController : ApiController
{
    [Authorize]
    public string GetCurrent()
    {
        return User.Identity.Name;
        //这里可以调用后台用户服务,获取用户相关数所,或者验证用户权限进行相应的操作
    }
}
复制代码

然后,客户端用以 grant_type=password 方式拿到的 Access Token 调用这个Web API,客户端增加的代码如下:

复制代码
[Fact]
public async Task Call_WebAPI_By_Resource_Owner_Password_Credentials_Grant()
{
    var token = await GetAccessToken();
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    Console.WriteLine(await (await _httpClient.GetAsync("/api/users/current")).Content.ReadAsStringAsync());
}
复制代码

客户端运行结果如下:

"博客园团队"

调用成功!运行结果正是获取 Access Token 时所用的 username 。

结合 ASP.NET 现有的安全机制,借助 OWIN 的威力,Microsoft.Owin.Security.OAuth 的确让开发基于 OAuth 的 Web API 变得更简单。

ASP.NET Web API与Owin OAuth:使用Access Toke调用受保护的API - dudu - 博客园

mikel阅读(805)

来源: ASP.NET Web API与Owin OAuth:使用Access Toke调用受保护的API – dudu – 博客园

在前一篇博文中,我们使用OAuth的Client Credential Grant授权方式,在服务端通过CNBlogsAuthorizationServerProvider(Authorization Server的一个实现)成功发放了Access Token,并在客户端成功拿到了Access Token。

那Access Token有什么用呢?在OAuth中对Resource Server(比如Web API)访问权限的验证都是基于Access Token。不管是什么样的客户端来调用,Resource Server总是铁面无私,只认Access Token。

ASP.NET Web API中启用OAuth的Access Token验证非常简单,只需在相应的Controller或Action加上[Authorize]标记,比如:

复制代码
[Authorize]
public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}
复制代码

加上[Authorize]之后,如果不使用Access Token,调用API时就会出现如下的错误:

{"Message":"Authorization has been denied for this request."}

这时你也许会问,为什么一加上[Authorize]就会有这个效果?原来的Forms验证怎么不起作用了?

原因是你在用Visual Studio创建ASP.NET Web API项目时,VS自动帮你添加了相应的代码,打开WebApiConfig.cs,你会看到下面这2行代码:

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

就是这2行代码,改变了[Authorize]的作用。

ASP.NET Web API中启用OAuth验证就这么简单(简单的背后是微软实现了基于OWIN的OAuth,实现源代码在Katana项目中)。

那在客户端如何使用Access Token调用Web API呢?

也很简单,只要在http请求头中加上Bearer:Token即可,客户端调用示例代码如下:

复制代码
    public class OAuthClientTest
    {
        private HttpClient _httpClient;

        public OAuthClientTest()
        {
            _httpClient = new HttpClient();
            _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
        }

        [Fact]
        public async Task Call_WebAPI_By_Access_Token()
        {
            var token = await GetAccessToken();
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            Console.WriteLine(await (await _httpClient.GetAsync("/api/values")).Content.ReadAsStringAsync());
        }

        private async Task<string> GetAccessToken()
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("client_id", "1234");
            parameters.Add("client_secret", "5678");
            parameters.Add("grant_type", "client_credentials");

            var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
            var responseValue = await response.Content.ReadAsStringAsync();                

            return JObject.Parse(responseValue)["access_token"].Value<string>();
        }
    }
复制代码

运行结果如下:

["value1","value2"]

搞定!

ASP.NET Web API与基于Owin实现的OAuth的整合,让原本复杂的问题变得简单。

在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token - dudu - 博客园

mikel阅读(901)

来源: 在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token – dudu – 博客园

OAuth真是一个复杂的东东,即使你把OAuth规范倒背如流,在具体实现时也会无从下手。因此,Microsoft.Owin.Security.OAuth应运而生(它的实现代码在Katana项目中),帮助开发者偷了不少工,减了不少料。

这篇博文试图通过一个简单的示例分享一下如何基于Microsoft.Owin.Security.OAuth,使用Client Credentials Grant授权方式给客户端发放access token。

Client Credentials Grant的授权方式就是只验证客户端(Client),不验证用户(Resource Owner),只要客户端通过验证就发access token。举一个对应的应用场景例子,比如我们想提供一个“获取网站首页最新博文列表”的WebAPI给iOS App调用。由于这个数据与用户无关,所以不涉及用户登录与授权,不需要Resource Owner的参与。但我们不想任何人都可以调用这个WebAPI,所以要对客户端进行验证,而使用OAuth中的 Client Credentials Grant 授权方式可以很好地解决这个问题。

具体实现方式如下:

1)用Visual Studio 2013/2015创建一个Web API项目,VS会生成一堆OAuth相关代码。

2)打开Startup.Auth.cs ,精简一下代码,我们只需要实现以Client Credentials Grant授权方式拿到token,其它无关代码全部清除,最终剩下如下代码:

复制代码
public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        var OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new CNBlogsAuthorizationServerProvider(),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        app.UseOAuthBearerTokens(OAuthOptions);
    }
}
复制代码

3)创建一个新的类 CNBlogsAuthorizationServerProvider,并继承自 OAuthAuthorizationServerProvider,重载 OAuthAuthorizationServerProvider() 与 GrantClientCredentials() 这两个方法。代码如下:

复制代码
public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        string clientId;
        string clientSecret;
        context.TryGetFormCredentials(out clientId, out clientSecret);

        if (clientId == "1234" && clientSecret == "5678")
        {
            context.Validated(clientId);
        }

        return base.ValidateClientAuthentication(context);
    }

    public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    {
        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "iOS App"));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        return base.GrantClientCredentials(context);
    }
}
复制代码

在 ValidateClientAuthentication() 方法中获取客户端的 client_id 与 client_secret 进行验证。

在 GrantClientCredentials() 方法中对客户端进行授权,授了权就能发 access token 。

这样,OAuth的服务端代码就完成了。这么简单?是的,就这么简单,因为有了Microsoft.Owin.Security.OAuth。

4)然后写客户端调用代码测试一下:

复制代码
public class OAuthClientTest
{
    private HttpClient _httpClient;

    public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    }

    [Fact]
    public void Get_Accesss_Token_By_Client_Credentials_Grant()
    {
        var parameters = new Dictionary<string, string>();
        parameters.Add("client_id", "1234");
        parameters.Add("client_secret", "5678");
        parameters.Add("grant_type", "client_credentials");

        Console.WriteLine(_httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters))
            .Result.Content.ReadAsStringAsync().Result);
    }
}
复制代码

运行结果如下:

{"access_token":"8PqaWilv_SJT7vRXambP7Mebyaf3KO1GXYHsqA-oPMOQF6xk1YpluczOZGo-WwATU5YmGb0wSR0cUQMC8RSZfwO8nwom7yG11FIANhy2PNiqTg2CYdJF0sf0ggFs6it_i3mc_m1iEFCK2dLBPDJXPI24wngCPR0wP_zugZvyKv314BM0PQmnnwg3kLXR1DISKRbs5-i59VCtFSZgkM7A0w","token_type":"bearer","expires_in":1209599}

搞定!

【更新】

建议使用Basic Authentication传递clientId与clientSecret,服务端CNBlogsAuthorizationServerProvider中的TryGetFormCredentials()改为TryGetBasicCredentials(),客户端的调用代码如下:

复制代码
public class OAuthClientTest
{
    private HttpClient _httpClient;

    public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    }

    [Fact]
    public void Get_Accesss_Token_By_Client_Credentials_Grant()
    {
        var clientId =  "1234";
        var clientSecret = "5678";        
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                "Basic",
                Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));
                
        var parameters = new Dictionary<string, string>();        
        parameters.Add("grant_type", "client_credentials");       

        Console.WriteLine(_httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters))
            .Result.Content.ReadAsStringAsync().Result);
    }
}
复制代码

【参考资料】

ASP.Net MVC: Creating an OAuth client credentials grant type token endpoint

【MongoDB】Element '_id' does not match any field or property of class MongoDBDemo.Student._GreAmbWang的博客-CSDN博客

mikel阅读(848)

来源: 【MongoDB】Element ‘_id’ does not match any field or property of class MongoDBDemo.Student._GreAmbWang的博客-CSDN博客

问题
在查询数据时出现异常

var res = collection.Find(Builders<Student>.Filter.Eq(“Name”, “哈哈”)).ToList();
异常

 

分析
因为在MongoDB数据中,有_id字段,而Student类中没有_id字段

 

解决方法
在类添加_id属性

public ObjectId _id { get; set; }

在类添加BsonIgnoreExtraElements特性

[MongoDB.Bson.Serialization.Attributes.BsonIgnoreExtraElements]
public class Student

————————————————
版权声明:本文为CSDN博主「GreAmbWang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38211198/article/details/100716198

未能加载文件或程序集“log4net, Version=2.0.8.0_伊人回眸泪倾城的博客-CSDN博客

mikel阅读(673)

来源: 未能加载文件或程序集“log4net, Version=2.0.8.0_伊人回眸泪倾城的博客-CSDN博客

异常信息
System.IO.FileLoadException
HResult=0x80131040
Message=未能加载文件或程序集“log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

发生异常原因
A项目 引用用了 B项目,结果A 项目引用了低版本的 log4net

B项目应用了高版本的
2.0.8.0,

编译A项目时候, 会提示如上错误;

解决方案
调整B项目引用的log4net 版本改为和 A项目 引用的同一个版本 1.2.10.0 即可;就是 B 项目引用log4net 的路径有问题;
————————————————
版权声明:本文为CSDN博主「静心物语313」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xcl13014673050/article/details/89317225

类型初始值设定项引发异常的解决方案 _shfongfeng的专栏-CSDN博客_类型初始值设定项引发异常

mikel阅读(6838)

来源: 类型初始值设定项引发异常的解决方案 _shfongfeng的专栏-CSDN博客_类型初始值设定项引发异常

 

上午打开VS编译程序的时候,突然出现了这个运行时错误“类型初始值设定项引发异常”。昨天还没有这个错误呢,今天就突然出现,搞得我一头雾水。上网搜了一下,发现有很多人遇到了这个问题。经过一番折腾解决了这个问题后,发现很多人都没有意识到其症结所在,于是写这个随笔,给大家点启发,也给自己做个备忘。

      症状描述:
我的程序需要连接一台MQ服务器(其实就是个台式机,每天下班关机),如果服务器没有开,程序就会抛出异常。以前编译时老是忘开服务器,经常会出现MQ访问失败的异常。这次我原本以为也是这个异常,没想到出来的是“类型初始值设定项引发异常”。
今天的错误为什么会和以往不一样呢?昨天临下班时,把一个类的单件模式做了重构,使用了静态初始化方法。代码如下:

private static readonly WorkerManager instance = new WorkerManager();
static WorkerManager() { }
private WorkerManager()
{
Initialize();
}
public static WorkerManager Instance
{
get

{ return instance; }
}

访问MQ的语句在Initialize方法里,问题就应该出现在这里了。
      解决方法:
当然,把MQ服务器打开问题就解决了,但是究竟为什么会出现“类型初始值设定项引发异常”呢?原来类的静态成员在初始化时如果出现异常,访问类的其它静态成员或对该类进行初始化都会抛出这个异常。请看下面的代码:

 public class Test
{
public static Foo Test1 = new Foo();
public static string Test2 = “Test2”;
public string Test3 = “Test3”;
public Test() { }
}

 public class Foo
{
public Foo()
{
throw new Exception();
}
}

在访问Test.Test2以及new Test()时都会抛出这个异常。我的代码中,由于Initialize()出现异常,instance实例化失败,所以访问Instance时就抛出了这个异常。
      总结:

TypeInitializationExeption在MSND中的描述为:当类初始值设定项不能初始化类型时,将创建 TypeInitializationException 并向其传递由该类型的类初始值设定项引发的异常引用。

我们知道,类型初始化或者访问类型的静态成员时,都会对类中的其他静态成员进行初始化,并执行静态构造函数(如果有的话)。在这些过程中如果任一环节出现例如下面的代码:

class ClassHelper

{

public static string Field = Do(“Initial the static field”);
public static string StaticString = “Initaial static string”;
public string NonStaticString = “Initial non static string”;
public ClassHelper()
{
NonStaticString = “Change non static string in instance constructor”;
StaticString = “Change static string in instance constructor”;
}
public static string Do(string field)
{
Console.WriteLine(field);
throw new Exception();
return field;
}

}

那么产生TypeInitializationException的情况就包含以下几种:

1. 访问类的某一静态成员,而其他静态成员的初始化(或静态构造函数中)产生异常。例如访问ClassHelper.StaticString,由于静态成员Field的初始化产生异常,因此调用ClassHelper.StaticString会抛出TypeInitializationException。

2. 访问类的某一静态成员,该静态成员的初始化(或静态构造函数中)产生异常。例如访问ClassHelper.Field。

3. 对该类进行初始化,而类中的某个静态成员初始化(或静态构造函数中)产生异常。例如ClassHelper helper = new ClassHelper()。

byte[] to int64_new_smile的专栏-CSDN博客

mikel阅读(829)

来源: byte[] to int64_new_smile的专栏-CSDN博客

private Int64 BytesToInt64 (byte[] sourceBytes)

{

if (null==sourceBytes)

{

throw new ArgumentNullException();

}

Int64 temp = new Int64();

byte tempByte;

for (int i = 0; i < 8; i++)

{

tempByte = sourceBytes[i];

temp += (tempByte & 0xFFFFFFFFFFFF) << (8 * i);

}

return temp;

}

字节数组转换为int64,
————————————————
版权声明:本文为CSDN博主「new_smile」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/new_smile/article/details/19194505