当前位置: 移动技术网 > IT编程>开发语言>.net > 一步步完成“迷你版” 的ASP.NET Core框架

一步步完成“迷你版” 的ASP.NET Core框架

2019年05月11日  | 移动技术网IT编程  | 我要评论

口袋妖怪黑白中文版,中国邮政储蓄银行查询,母乳喂养多长时间好

一 前言

artech 分享了 200行代码,7个对象——让你了解asp.net core框架的本质 。 用一个极简的模拟框架阐述了asp.net core框架最为核心的部分。

这里一步步来完成这个迷你框架。

二 先来一段简单的代码

这段代码非常简单,启动服务器并监听本地5000端口和处理请求。

        static async task main(string[] args)
        {
            httplistener httplistener = new httplistener();
            httplistener.prefixes.add("http://localhost:5000/");
            httplistener.start();
            while (true)
            {
                var context = await httplistener.getcontextasync();
                await context.response.outputstream.writeasync(encoding.utf8.getbytes("hello world"));
                context.response.close();
            }
        }

现在要分离服务器(server) 和 请求处理(handle),那么一个简单设计架构就出来了 :

pipeline =server + httphandler

三 处理器的抽象

处理器要从请求(request)中获取数据,和定制响应(response)的数据。
可以想到我们的处理器的处理方法应该是这样的:

  task handle(/*httprequest httpresponse*/);

它可以处理请求和响应,由于处理可以是同步或者异步的,所以返回task。

很容易想到要封装http请求和响应,封装成一个上下文(context) 供处理器使用(这样的好处,处理器需要的其他数据也可以封装在这里,统一使用),所以要开始封装httpcontext。

封装httpcontext

 public class httprequest
    {
        public uri url  { get; }
        public namevaluecollection headers { get; }
        public stream body { get; }
    }

    public class httpresponse
    {
        public namevaluecollection headers { get; }
        public stream body { get; }

        public int statuscode { get; set; }
    }

    public class httpcontext
    {
        public httprequest request { get; set; }

        public httpresponse response { get; set; }
    }

要支持不同的服务器,则不同的服务器都要提供httpcontext,这样有了新的难题:服务器和httpcontext之间的适配
现阶段的httpcontext包含httprequest和httpresponse,请求和响应的数据都是要服务器(server)提供的。
可以定义接口,让不同的服务器提供实现接口的实例:

    public interface ihttprequestfeature
    {
        uri url { get; }

        namevaluecollection headers { get; }

        stream body { get; }
    }
    public interface ihttpresponsefeature
    {
        int statuscode { get; set; }

        namevaluecollection headers { get; }

        stream body { get; }
    }

为了方便管理服务器和httpcontext之间的适配,定义一个功能的集合,通过类型可以找到服务器提供的实例

   public interface ifeaturecollection:idictionary<type,object>
    {
    }

    public static partial class extensions
    {
        public static t get<t>(this ifeaturecollection features)
        {
            return features.trygetvalue(typeof(t), out var value) ? (t)value : default;
        }

        public static ifeaturecollection set<t>(this ifeaturecollection features,t feature)
        {
            features[typeof(t)] = feature;
            return features;
        }
    }

    public class featurecollection : dictionary<type, object>, ifeaturecollection { }

接下来修改httpcontext,完成适配

    public class httpcontext
    {
       
        public httpcontext(ifeaturecollection features)
        {
            request = new httprequest(features);
            response = new httpresponse(features);
        }
        public httprequest request { get; set; }

        public httpresponse response { get; set; }

    }
    
    public class httprequest
    {
        private readonly ihttprequestfeature _httprequestfeature;
        public httprequest(ifeaturecollection features)
        {
            _httprequestfeature = features.get<ihttprequestfeature>();
        }
       public uri url => _httprequestfeature.url;

       public namevaluecollection headers => _httprequestfeature.headers;

       public stream body => _httprequestfeature.body;
    }

    public class httpresponse
    {
        private readonly ihttpresponsefeature _httpresponsefeature;
        public httpresponse(ifeaturecollection features)
        {
            _httpresponsefeature = features.get<ihttpresponsefeature>();
        }
       public int statuscode
        {
            get => _httpresponsefeature.statuscode;
            set => _httpresponsefeature.statuscode = value;
        }

       public namevaluecollection headers => _httpresponsefeature.headers;

       public  stream body => _httpresponsefeature.body;
        
    }
    public static partial class extensions
    {
        public static task writeasync(this httpresponse response,string content)
        {
            var buffer = encoding.utf8.getbytes(content);
            return response.body.writeasync(buffer, 0, buffer.length);
        }
    }

定义处理器

封装好了httpcontext,终于可以回过头来看看处理器。
处理器的处理方法现在应该是这样:

  task handle(httpcontext context);

接下来就是怎么定义这个处理器了。
起码有两种方式:
1、定义一个接口:

    public interface ihttphandler
    {
        task handle(httpcontext context);
    }

2、定义一个委托类型

public delegate task requestdelegate(httpcontext context);

两种方式,本质上没啥区别,委托代码方式更灵活,不用实现一个接口,还符合鸭子模型。
处理器就选用委托类型。
定义了处理器,接下来看看服务器

四 服务器的抽象

服务器应该有一个开始方法,传入处理器,并执行。
服务器抽象如下:

    public interface iserver
    {
        task startasync(requestdelegate handler);
    }

定义一个httplistener的服务器来实现iserver,由于httplistener的服务器需要提供httpcontext所需的数据,所以先定义httplistenerfeature

    public class httplistenerfeature : ihttprequestfeature, ihttpresponsefeature
    {

        private readonly httplistenercontext _context;

        public httplistenerfeature(httplistenercontext context) => _context = context;
        uri ihttprequestfeature.url => _context.request.url;

        namevaluecollection ihttprequestfeature.headers => _context.request.headers;

        namevaluecollection ihttpresponsefeature.headers => _context.response.headers;

        stream ihttprequestfeature.body => _context.request.inputstream;

        stream ihttpresponsefeature.body => _context.response.outputstream;

        int ihttpresponsefeature.statuscode
        {
            get => _context.response.statuscode;
            set => _context.response.statuscode = value;
        }
    }

定义httplistener服务器

  public class httplistenerserver : iserver
    {
        private readonly httplistener _httplistener;

        private readonly string[] _urls;

        public httplistenerserver(params string[] urls)
        {
            _httplistener = new httplistener();
            _urls = urls.any() ? urls : new string[] { "http://localhost:5000/" };
        }
        public async task startasync(requestdelegate handler)
        {
            array.foreach(_urls, url => _httplistener.prefixes.add(url));

            _httplistener.start();
            console.writeline($"服务器{typeof(httplistenerserver).name} 开启,开始监听:{string.join(";", _urls)}");
            while (true)
            {
                var listtenercontext = await _httplistener.getcontextasync();
                var feature = new httplistenerfeature(listtenercontext);

                var features = new featurecollection()
                    .set<ihttprequestfeature>(feature)
                    .set<ihttpresponsefeature>(feature);
                var httpcontext = new httpcontext(features);

                await handler(httpcontext);

                listtenercontext.response.close();
            }
        }
    }

修改main方法运行测试

        static async task main(string[] args)
        {
            iserver server = new httplistenerserver();
            async task foobar(httpcontext httpcontext)
            {
                await httpcontext.response.writeasync("foobar");
            }
            await server.startasync(foobar); 
        }

运行结果如下:

至此,完成了服务器和处理器的抽象。
接下来单看处理器,所有的处理逻辑都集合在一个方法中,理想的方式是有多个处理器进行处理,比如处理器a处理完,则接着b处理器进行处理……
那么就要管理多个处理器之间的连接方式。

五 中间件

中间件的定义

假设有三个处理器a,b,c
框架要实现:a处理器开始处理,a处理完成之后,b处理器开始处理,b处理完成之后,c处理器开始处理。

引入中间件来完成处理器的连接。

中间件的要实现的功能很简单:

  • 传入下一个要执行的处理器;
  • 在中间件中的处理器里,记住下一个要执行的处理器;
  • 返回中间件中的处理器,供其他中间件使用。
    所以中间件应该是这样的:
 //伪代码
 处理器  middleware(传入下一个要执行的处理器)
 {
     return 处理器
     {
         //处理器的逻辑
         下一个要执行的处理器在这里执行
     }
 }

举个例子,现在有三个中间件foomiddleware,barmiddleware,bazmiddleware,分别对应的处理器为a,b,c
要保证 处理器的处理顺序为 a->b->c
则先要执行 最后一个bazmiddleware,传入“完成处理器” 返回 处理器c
然后把处理器c 传入 barmiddleware ,返回处理器b,依次类推。

//伪代码
var middlewares=new []{foomiddleware,barmiddleware,bazmiddleware};
middlewares.reverse();
var  next=完成的处理器;
foreach(var middleware in middlewares)
{
    next=  middleware(next);
}
//最后的next,就是最终要传入iserver 中的处理器

模拟运行时的伪代码:

 //传入完成处理器,返回处理器c
 处理器 bazmiddleware(完成处理器)
 { 
     return 处理器c
            {    
               //处理器c的处理代码
               完成处理器
            };
 }
 //传入处理器c,返回处理器b
  处理器  barmiddleware(处理器c)
 { 
     return 处理器b
            {    
               //处理器b的处理代码
               执行处理器c
            };
 }
  //传入处理器b,返回处理器a
  处理器  foomiddleware(处理器b)
 { 
     return 处理器a
            {    
               //处理器a的处理代码
               执行处理器b
            };
 }

 

这样当处理器a执行的时候,会先执行自身的代码,然后执行处理器b,处理器b执行的时候,先执行自身的代码,然后执行处理器c,依次类推。

所以,中间件的方法应该是下面这样的:

requestdelegate domiddleware(requestdelegate next);

中间件的管理

要管理中间件,就要提供注册中间件的方法和最终构建出requestdelegate的方法。
定义注册中间件和构建处理器的接口: iapplicationbuilder

    public interface iapplicationbuilder
    {
        iapplicationbuilder use(func<requestdelegate, requestdelegate> middleware);

        requestdelegate build();
    }

实现:

    public class applicationbuilder : iapplicationbuilder
    {
        private readonly list<func<requestdelegate, requestdelegate>> _middlewares = new list<func<requestdelegate, requestdelegate>>();
        public iapplicationbuilder use(func<requestdelegate, requestdelegate> middleware)
        {
            _middlewares.add(middleware);
            return this;
        }
        public requestdelegate build()
        {
            _middlewares.reverse();
            requestdelegate next = context => { context.response.statuscode = 404; return task.completedtask; };
            foreach (var middleware in _middlewares)
            {
                next = middleware(next);
            }
            return next;
        }       
    }

定义中间件测试

在program 类里定义三个中间件:

        static requestdelegate foomiddleware(requestdelegate next)
        {
            return async context =>
            {
                await context.response.writeasync("foo=>");
                await next(context);
            };
        }
        static requestdelegate barmiddleware(requestdelegate next)
        {
            return async context =>
            {
                await context.response.writeasync("bar=>");
                await next(context);
            };
        }
        static requestdelegate bazmiddleware(requestdelegate next)
        {
            return async context =>
            {
                await context.response.writeasync("baz=>");
                await next(context);
            };
        }

修改main方法测试运行

        static async task main(string[] args)
        {
            iserver server = new httplistenerserver();

            var handler = new applicationbuilder()
                .use(foomiddleware)
                .use(barmiddleware)
                .use(bazmiddleware)
                .build();
            await server.startasync(handler); 
        }

运行结果如下:

六 管理服务器和处理器

为了管理服务器和处理器之间的关系 抽象出web宿主
如下:

 public interface iwebhost
    {
        task startasync();
    }

    public class webhost : iwebhost
    {
        private readonly iserver _server;

        private readonly requestdelegate _handler;

        public webhost(iserver server,requestdelegate handler)
        {
            _server = server;
            _handler = handler;

        }
        public task startasync()
        {
            return _server.startasync(_handler);
        }
    }

main方法可以改一下测试

        static async task main(string[] args)
        {
            iserver server = new httplistenerserver();

            var handler = new applicationbuilder()
                .use(foomiddleware)
                .use(barmiddleware)
                .use(bazmiddleware)
                .build();

            iwebhost webhost = new webhost(server, handler);
            await webhost.startasync();
        }

要构建webhost,需要知道用哪个服务器,和配置了哪些中间件,最后可以构建出webhost
代码如下:

 public interface iwebhostbuilder
    {
        iwebhostbuilder useserver(iserver server);

        iwebhostbuilder configure(action<iapplicationbuilder> configure);

        iwebhost build();
    }

    public class webhostbuilder : iwebhostbuilder
    {
        private readonly list<action<iapplicationbuilder>> _configures = new list<action<iapplicationbuilder>>();
        private iserver _server;
        public iwebhost build()
        {
            //所有的中间件都注册在builder上
            var builder = new applicationbuilder();
            foreach (var config in _configures)
            {
                config(builder);
            }
            return new webhost(_server, builder.build());
        }

        public iwebhostbuilder configure(action<iapplicationbuilder> configure)
        {
            _configures.add(configure);
            return this;
        }

        public iwebhostbuilder useserver(iserver server)
        {
            _server = server;
            return this;
        }
    }

给iwebhostbuilder加一个扩展方法,用来使用httplistenerserver 服务器

    public static partial class extensions
    {
        public static iwebhostbuilder usehttplistener(this iwebhostbuilder builder, params string[] urls)
        {
            return builder.useserver(new httplistenerserver(urls));
        }
    }

修改mian方法

        static async task main(string[] args)
        {
            await new webhostbuilder()
                .usehttplistener()
                .configure(app=>
                app.use(foomiddleware)
                .use(barmiddleware)
                .use(bazmiddleware))
                .build()
                .startasync();              

        }

完成。

七 添加一个usemiddleware 扩展 玩玩

        public static iapplicationbuilder usemiddleware(this iapplicationbuilder application, type type)
        {
            //省略实现
        }

        public static iapplicationbuilder usemiddleware<t>(this iapplicationbuilder application) where t : class
        {
            return application.usemiddleware(typeof(t));
        }

添加一个中间件

    public class quxmiddleware
    {
        private readonly requestdelegate _next;

        public quxmiddleware(requestdelegate next)
        {
            _next = next;
        }

        public async task invokeasync(httpcontext context)
        {

           await context.response.writeasync("qux=>");

            await _next(context);
        }
    }
    public static partial class extensions
    {
        public static iapplicationbuilder usequx(this iapplicationbuilder builder)
        {
            return builder.usemiddleware<quxmiddleware>();
        }
    }

使用中间件

    class program
    {
        static async task main(string[] args)
        {
            await new webhostbuilder()
                .usehttplistener()
                .configure(app=>
                app.use(foomiddleware)
                .use(barmiddleware)
                .use(bazmiddleware)
                .usequx())
                .build()
                .startasync();              

        }

运行结果

最后,期待artech 新书。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网