GithubHelp home page GithubHelp logo

beetlex-io / fasthttpapi Goto Github PK

View Code? Open in Web Editor NEW
647.0 59.0 175.0 49.81 MB

a lightweight and high-performance http/websocket service component in the dotnet core platform that supports TLS.

License: Apache License 2.0

C# 87.97% HTML 7.36% JavaScript 4.64% Go 0.03%
dotnetcore http websocket fasthttp webapi websockets-server fasthttpapi dotnet https-server ssl rpc-api rpc-service

fasthttpapi's Introduction

BeetleX.FastHttpApi is a lightweight and high-performance HTTP service component in the dotnet core platform that supports WebSocket and SSL.

samples

[https://github.com/beetlex-io/BeetleX-Samples]

Web Framework Benchmarks

Round 20 benchmarks-round20

Using

Install BeetleX.FastHttpApi

Install-Package BeetleX.FastHttpApi

Base sample code

    [Controller]
    class Program
    {
        private static BeetleX.FastHttpApi.HttpApiServer mApiServer;
        static void Main(string[] args)
        {
            mApiServer = new BeetleX.FastHttpApi.HttpApiServer();
            mApiServer.Options.LogLevel = BeetleX.EventArgs.LogType.Trace;
            mApiServer.Options.LogToConsole = true;
            mApiServer.Debug();//set view path with vs project folder
            mApiServer.Register(typeof(Program).Assembly);
            //mApiServer.Options.Port=80; set listen port to 80
            mApiServer.Open();//default listen port 9090  
            Console.Write(mApiServer.BaseServer);
            Console.Read();
        }
        // Get /hello?name=henry 
        // or
        // Get /hello/henry
        [Get(Route="{name}")]
        public object Hello(string name)
        {
            return $"hello {name} {DateTime.Now}";
        }
        // Get /GetTime  
        public object GetTime()
        {
            return DateTime.Now;
        }
    }

Url Map

mApiServer.Map("/", (ctx) =>
{
    ctx.Result(new TextResult("map /"));
});

mApiServer.Map("/user/{id}", async (ctx) =>
{
    ctx.Result(new TextResult((string)ctx.Data["id"]));
});

Url rewrite

mApiServer.UrlRewrite.Add("/api/PostStream/{code}/{datacode}", "/api/PostStream");
mApiServer.UrlRewrite.Add("/api/PostStream/{code}", "/api/PostStream");
mApiServer.UrlRewrite.Add(null, "/gettime", "/time", null);

Action route

[RouteMap("/map/{code}")]
[RouteMap("/map/{code}/{customer}")]
public object Map(string code, string customer)
{
    return new { code, customer };
}

Hosting and DI services

Install-Package BeetleX.FastHttpApi.Hosting

    public class Program
    {
        static void Main(string[] args)
        {
            HttpServer host = new HttpServer(80);
            host.UseTLS("test.pfx", "123456");
            host.Setting((service, option) =>
            {
                service.AddTransient<UserInfo>();
                option.LogToConsole = true;
                option.LogLevel = BeetleX.EventArgs.LogType.Info;
            });
            host.Completed(server =>
            {

            });
            host.RegisterComponent<Program>();
            host.Run();
        }
    }

    [Controller]
    public class Home
    {
        public Home(UserInfo user)
        {
            mUser = user;
        }

        public object Hello()
        {
            return mUser.Name;
        }

        private UserInfo mUser;
    }

    public class UserInfo
    {
        public string Name { get; set; } = "admin";
    }

Windows service

class Program
{
    private static HttpServer mServer;

    static void Main(string[] args)
    {
        mServer = new HttpServer(80);
        mServer.IsWindowsServices = true;
        mServer.Setting((service, option) =>
        {
            option.LogToConsole = true;
            option.WriteLog = true;
            option.LogLevel = BeetleX.EventArgs.LogType.Info;
        });
        mServer.RegisterComponent<Home>();
        mServer.Run();
    }
}
[Controller]
public class Home
{
    public object Hello(string name)
    {
        return $"hello {name}";
    }
}

EntityFrameworkCore extensions

BeetleX.FastHttpApi.EFCore.Extension

class Program
{
    static void Main(string[] args)
    {
        HttpApiServer server = new HttpApiServer();
        server.AddEFCoreDB<NorthwindEFCoreSqlite.NorthwindContext>();
        server.Options.Port = 80;
        server.Options.LogToConsole = true;
        server.Options.LogLevel = EventArgs.LogType.Info;
        server.Options.SetDebug();
        server.Register(typeof(Program).Assembly);
        server.AddExts("woff");
        server.Open();
        Console.Read();
    }
}
[Controller]
public class Webapi
{
    public DBObjectList<Customer> Customers(string name, string country, EFCoreDB<NorthwindContext> db)
    {
        Select<Customer> select = new Select<Customer>();
        if (!string.IsNullOrEmpty(name))
            select &= c => c.CompanyName.StartsWith(name);
        if (!string.IsNullOrEmpty(country))
            select &= c => c.Country == country;
        select.OrderBy(c => c.CompanyName.ASC());
        return (db.DBContext, select);
    }

    [Transaction]
    public void DeleteCustomer(string customer, EFCoreDB<NorthwindContext> db)
    {
        db.DBContext.Orders.Where(o => o.CustomerID == customer).Delete();
        db.DBContext.Customers.Where(c => c.CustomerID == customer).Delete();
    }

    public DBValueList<string> CustomerCountry(EFCoreDB<NorthwindContext> db)
    {
        SQL sql = "select distinct country from customers";
        return (db.DBContext, sql);
    }
}

Setting https

  • HttpConfig.json
 "SSL": true,
 "CertificateFile": "you.com.pfx",
 "CertificatePassword": "******",
  • Code
mApiServer.ServerConfig.SSL=true;
mApiServer.ServerConfig.CertificateFile="you.com.pfx";
mApiServer.ServerConfig.CertificatePassword="******";

Defined result

  • Text result
    public class TextResult : ResultBase
    {
        public TextResult(string text)
        {
            Text = text == null ? "" : text;
        }
        public string Text { get; set; }
        public override bool HasBody => true;
        public override void Write(PipeStream stream, HttpResponse response)
        {
            stream.Write(Text);
        }
    }

Download result

        public object DownloadImport(string id)
        {
           
            Dictionary<string, object> result = new Dictionary<string, object>();
            return new DownLoadResult(Newtonsoft.Json.JsonConvert.SerializeObject(result), $"test.json");
        }
  • Use
        public object plaintext()
        {
            return new TextResult("Hello, World!");
        }

Cookies

        public object SetCookie(string name, string value, IHttpContext context)
        {
            Console.WriteLine(context.Data);
            context.Response.SetCookie(name, value);
            return $"{DateTime.Now}{name}={value}";
        }

        public string GetCookie(string name, IHttpContext context)
        {
            Console.WriteLine(context.Data);
            return $"{DateTime.Now} {name}= {context.Request.Cookies[name]}";
        }

Header

        public void SetHeader(string token,IHttpContext context)
        {
            context.Response.Header["Token"]=token;
        }

        public string GetHeader(string name, IHttpContext context)
        {
            return  context.Request.Header[name];
        }

Data bind

  • Url

/hello?name=xxxor/hello/henry

        [Get(Route = "{name}")]
        public object Hello(string name, IHttpContext context)
        {
            return $"hello {name} {DateTime.Now}";
        }

/SetValue?id=xxx&value=xxxxor/SetValue/xxx-xxx

        [Get(Route = "{id}-{value}")]
        public object SetValue(string id, string value, IHttpContext context)
        {
            return $"{id}={value} {DateTime.Now}";
        }
  • Json

{"name":"xxxx","value":"xxx"}

        [Post]
        [JsonDataConvert]
        public object Post(string name, string value, IHttpContext context)
        {
            Console.WriteLine(context.Data);
            return $"{name}={value}";
        }

or

        [Post]
        [JsonDataConvert]
        public object Post(Property body, IHttpContext context)
        {
            Console.WriteLine(context.Data);
            return $"{body.name}={body.value}";
        }
  • x-www-form-urlencoded

name=aaa&value=aaa

        [Post]
        [FormUrlDataConvert]
        public object PostForm(string name, string value, IHttpContext context)
        {
            Console.WriteLine(context.Data);
            return $"{name}={value}";
        }
  • multipart/form-data
        [Post]
        [MultiDataConvert]
        public object UploadFile(string remark, IHttpContext context)
        {
            foreach (var file in context.Request.Files)
                using (System.IO.Stream stream = System.IO.File.Create(file.FileName))
                {
                    file.Data.CopyTo(stream);
                }
            return $"{DateTime.Now} {remark} {string.Join(",", (from fs in context.Request.Files select fs.FileName).ToArray())}";
        }
  • Read stream
        [Post]
        [NoDataConvert]
        public object PostStream(IHttpContext context)
        {
            Console.WriteLine(context.Data);
            string value = context.Request.Stream.ReadString(context.Request.Length);
            return value;
        }

Filter

  • Defined filter
    public class GlobalFilter : FilterAttribute
    {
        public override bool Executing(ActionContext context)
        {
            Console.WriteLine(DateTime.Now + " globalFilter execting...");
            return base.Executing(context);
        }
        public override void Executed(ActionContext context)
        {
            base.Executed(context);
            Console.WriteLine(DateTime.Now + " globalFilter executed");
        }
    }
  • Use
        [CustomFilter]
        public string Hello(string name)
        {
            return DateTime.Now + " hello " + name;
        }

or

    [Controller]
    [CustomFilter]
    public class ControllerTest
    {
    
    }
  • Skip filter
        [SkipFilter(typeof(GlobalFilter))]
        public string Hello(string name)
        {
            return DateTime.Now + " hello " + name;
        }

Parameters validation

public bool Register(
      [StringRegion(Min = 5)]string name,
      [StringRegion(Min = 5)]string pwd,
      [DateRegion(Min = "2019-1-1", Max = "2019-2-1")]DateTime dateTime,
      [EmailFormater]string email,
      [IPFormater]string ipaddress,
      [NumberRegion(Min = 18, Max = 56)]int age,
      [DoubleRegion(Min = 10)]double memory
                  )
        {
           return true;
        }

Async action

        [Get(Route = "{name}")]
        public Task<String> Hello(string name)
        {
            string result = $"hello {name} {DateTime.Now}";
            return Task.FromResult(result);
        }

        public async Task<String> Wait()
        {
            await Task.Delay(2000);
            return $"{DateTime.Now}";
        }

Cross domain

        [Options(AllowOrigin = "www.ikende.com")]
        public string GetTime(IHttpContext context)
        {
            return DateTime.Now.ToShortDateString();
        }

Websocket

  • Server
[Controller]
    class Program
    {
        private static BeetleX.FastHttpApi.HttpApiServer mApiServer;
        static void Main(string[] args)
        {
            mApiServer = new BeetleX.FastHttpApi.HttpApiServer();
            mApiServer.Debug();
            mApiServer.Register(typeof(Program).Assembly);
            mApiServer.Open();
            Console.Write(mApiServer.BaseServer);
            Console.Read();
        }
        // Get /hello?name=henry 
        // or
        // Get /hello/henry
        [Get(R"{name}")]
        public object Hello(string name)
        {
            return $"hello {name} {DateTime.Now}";
        }
        // Get /GetTime  
        public object GetTime()
        {
            return DateTime.Now;
        }
    }
  • Hello

Request json

{
      url: '/Hello', 
      params: { name: 'test' }
}
  • GetTime

Request json

{
      url: '/GetTime', 
      params: { }
}

fasthttpapi's People

Contributors

beetlex-io avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fasthttpapi's Issues

Question about performance test

I saw some performance test reports in Readme, some of which made me wonder, so I did a performance test

Environment

image

Benchmark Tools: wrk
dotnet sdk version: 2.1.300
asp.net core version: 2.1

TestCode

FastHttpApi Code : https://github.com/IKende/FastHttpApi/tree/master/samples/HttpApiServer.JsonBody

ASP.NET Core Web Api Code:

 [HttpGet]
public IActionResult Get(string name)
{
    return new JsonResult( DateTime.Now + " hello " + name);
}

Benchmark Report

FastHttpApi

mac:FastHttpApi yangxiaodong$ wrk -t 4 -c 20 -d 10 http://localhost:9090/hello?name=word
Running 10s test @ http://localhost:9090/hello?name=word
  4 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     7.43ms   42.43ms 476.30ms   97.51%
    Req/Sec     2.03k   763.60     3.93k    75.64%
  16388 requests in 10.01s, 3.69MB read
  Socket errors: connect 0, read 16388, write 0, timeout 0
Requests/sec:   1637.30
Transfer/sec:    377.35KB

Asp.Net Core web api

mac:WebApi yangxiaodong$ wrk -t 4 -c 20 -d 10 http://localhost:5000/api/values?name=word
Running 10s test @ http://localhost:5000/api/values?name=word
  4 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.07ms   10.62ms 158.47ms   92.55%
    Req/Sec     4.33k   722.51    14.24k    82.79%
  172684 requests in 10.10s, 32.11MB read
Requests/sec:  17096.93
Transfer/sec:      3.18MB

FastHttpApi QPS : 1637
ASP.NET Core QPS: 17096

For the ASP.NET CORE test report in readme, I suspect you did not adjust the log level when you tested.

性能测试时,打印大量出错信息

[15:46:26] [Error] session [::1]:46079@3767 error send data completed SocketError Success!@object reference not set to an instance of an object.
at BeetleX.TcpServer.SendCompleted(SocketAsyncEventArgs e)
[15:46:26] [Error] session [::1]:46078@3766 error [::1]:46078 send data completed process handler error Object reference not set to an instance of an object.!@object reference not set to an instance of an object.
at BeetleX.FastHttpApi.HttpApiServer.SendCompleted(ISession session, SocketAsyncEventArgs e)
at BeetleX.TcpServer.SendCompleted(SocketAsyncEventArgs e)

文件切片上传问题

https://github.com/moxiecode/plupload 做文件切片上传时,服务端会切片解析不全的问题。出现的是有规律的。
服务端代码大概如下
[Post]
[MultiDataConvert]
public object UploadFile(string name, int chunk, int chunks,int offset,string originalname, IHttpContext context)
{
//Console.WriteLine($"--------------------- {name} chunk {chunk} chunks {chunks} ");

        if (context.Request.Files.Count==0)
        {
            Console.WriteLine("Files count ===0");
        }
        if (chunk==3)
        {

        }
        foreach (var file in context.Request.Files)
        {
            using (System.IO.Stream stream = System.IO.File.Open(name, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
            {
                stream.Seek(stream.Length, System.IO.SeekOrigin.Begin);
          
                byte[] buf = new byte[file.Data.Length];
                file.Data.Read(buf,0,buf.Length);

                stream.Write(buf,0,buf.Length);
                stream.Flush();
                Console.WriteLine($"{name} chunk {chunk} chunks {chunks} file {file.Data.Length}");
            }
        }

        return $"{DateTime.Now} {name} {string.Join(",", (from fs in context.Request.Files select fs.FileName).ToArray())}";
    }

Linux下使用https访问启动几分钟后就挂了

[08:15:32] [Error] session [::ffff:219.145.5.89]:10579@1 error [::ffff:219.145.5.89]:10579 create session ssl error Connection reset by peer@Connection reset by peer
at System.Net.Sockets.Socket.BeginSend(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, AsyncCallback callback, Object state)
at BeetleX.Buffers.PipeStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Threading.Tasks.TaskFactory1.FromAsyncTrim[TInstance,TArgs](TInstance thisRef, TArgs args, Func5 beginMethod, Func`3 endMethod)
at System.IO.Stream.BeginEndWriteAsync(Byte[] buffer, Int32 offset, Int32 count)

错误信息是这样的

一个POST访问的参数问题

开发前端时遇以一个怪问题,后端一个服务接口用的FastHttpApi,平时调用都正常,postman访问几百次都正常,在某个时间用浏览器开发调试前端时调用这个服务接口后,POST 方式提交数据 IHttpContext.Data为空,只要出现一次后,再用各种工具重复调用接口数据都是空,只有重启服务后,恢复正常。后面又可能随机再次出现。进一步确认的是IHttpContext.Data的值是有的,是在某种情况下
string result;
context.Data.TryGetString(this.Name, out result);
return result; 这段取值 为空

看什么时候方便支持一下IOC等功能

 看什么时候支持这种依赖注入,以前很多项目可能就会无缝切换到FastHttpApi上面来
    /// <summary>
    ///     IoC 初始化
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    private IServiceProvider InitIoC(IServiceCollection services)
    {
        // 数据库链接字符串
        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

        services.AddScoped<IDatabaseContext, MySqlContext>();

        services.AddTransient<IUnitOfWork, UnitOfWork>();

}

捕获websocket 客户端掉线

通过HttpDisconnect事件可以捕获websocket 客户端掉线,判断HttpToken.WebSocket

  mApiServer.HttpDisconnect += (o, e) =>
            {
                HttpToken httpToken = (HttpToken)e.Session.Tag;
                if(httpToken.WebSocket)
                {

                }
}

限定Action是get或者Post提交

`[Controller(BaseUrl = "/WxApp/PushMessage")]
public class WebController : IController
{
private BeetleX.FastHttpApi.HttpApiServer mServer;
[NotAction]
public void Init(BeetleX.FastHttpApi.HttpApiServer server)
{
mServer = server;
}

    public string Get()
    {
        return ("GET:"+DateTime.Now.ToString());
    }

    public string Post()
    {
        return("POST"+DateTime.Now.ToString());
    }
}`

get 请求 /WxApp/PushMessage 调用get方法

数据参数定义

是否支持post这种参数格式 { "Data": { "StoryID": 0, "Order": 0 }, "Uid": "string", "Timestamp": 0, "Signature": "string", "Salt": "string" }

怎么限定Action是get或者post

`[Controller(BaseUrl = "/WxApp/PushMessage")]
public class WebController : IController
{
private BeetleX.FastHttpApi.HttpApiServer mServer;
[NotAction]
public void Init(BeetleX.FastHttpApi.HttpApiServer server)
{
mServer = server;
}

    public string Get()
    {
        return ("GET:"+DateTime.Now.ToString());
    }

    public string Post()
    {
        return("POST"+DateTime.Now.ToString());
    }
}`

如上代码,怎么实现 GET请求/WxApp/PushMessage输出GET+时间
POST请求/WxApp/PushMessage输出POST+时间

demo

System.TypeLoadException: 'Method 'Init' in type 'BeetleX.FastHttpApi.Admin._Admin' from assembly 'BeetleX.FastHttpApi.Admin, Version=0.7.6.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.'

Websocket 请求参数格式问题

https://github.com/IKende/FastHttpApi/blob/6de8b648cc80d4a6365fb6190643e693e4dc365b/src/ActionHandlerFactory.cs#L395-L400

客户端发送登录请求

image

匹配服务端Controller

    [Controller(BaseUrl = "/User/")]
    public class User
    {
        public object Login(string username, string password)
        {
            Console.WriteLine($"{username}, {password}");
            return new LoginResult
            {
                State = true,
                SessionId = Guid.NewGuid().ToString("N"),
                UserName = username
            };
        }
    }

报错无法找到_requestid项

image

请教下多个参数应该如何写JSON?

添加服务查看插件

Install-Package BeetleX.FastHttpApi.Admin -Version 0.8.0

加载插件并设置登陆信息

            mApiServer.Options.Manager = "admin";
            mApiServer.Options.ManagerPWD = "123456";
            mApiServer.Options.FileManager = true;
            mApiServer.Register(typeof(FastHttpApi.Admin.APIAssembly).Assembly);

访问地址

http://host:port/_admin/



是否可以设置不以方法名做路由?

比如如下方法想以 ip:port/medias/10003形式的地址访问 而不是 ip:port/getmedias/10003,再就是{mediaid} 不能写成{media_id},是否下划线在参数中是不允许使用的
[Get(Route = "{mediaid}")]
public object GetMedias(string mediaid)

options问题

options问题
设置了options的表头后。
接口在无请求状态一段时间后,options貌似失效了。中间过程是1点到4点多中间是无数据请求。空置状态。然后4点多再次请求。提示的是 cors不支持跨域。疑似options失效。很费解。这。。。

serverGC提示问题

image
image

true

都设置了,但是还是提示[Warring] no serverGC mode,please enable serverGC mode!

DataFrame doesn't collect via GC

Hey! I'm making web socket server with broadcasting.
I have constant count of connections (for example 500, separate for room by 30 users). One user sends to the server message, server broadcast to another in the same room.
And after a stress test I'm looking up some way to resolve issue: GC doesn't collect DataFrame object. I tried to turn on Server GC but result still the same, DataFrame objects still grows. I tried to make debug field "false"...

返回自定义的JSON

组件默认返回的json附加了一些信息,如果需要返回自己的Json内容可以通过方法返回JsonResult对象

return new JsonResult($"hello {name}");

也可以在控制器上标记

    [Controller]
    [DefaultJsonResultFilter]
    public class Controller 
    {
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.