---
id: httpservice
title: 创建HttpService
---

### 定义

命名空间：TouchSocket.Http <br/>
程序集：[TouchSocket.Http.dll](https://www.nuget.org/packages/TouchSocket.Http)


## 一、说明

**HttpService**是能够提供Http相关服务的基础类型。

## 二、产品特点

- 支持HTTPS。
- **多种数据接收模式**
- **多地址监听**（可以一次性监听多个IP及端口）

## 三、产品应用场景

- HTTP基础使用场景：可跨平台、跨语言使用。


## 四、服务器架构

服务器在收到新客户端连接时，会创建一个HttpSocketClient的派生类实例，与远程HttpClient对应，后续的数据通信均由此实例负责。

<img src={require('@site/static/img/docs/createhttpservice-1.png').default} width="500" />


## 五、支持插件接口

声明自定义实例类，然后实现**IHttpPlugin**接口，即可实现下列事务的触发。或者继承自**HttpPluginBase**类，重写相应方法即可。

|  插件方法| 功能 |
| --- | --- |
| IHttpPlugin | 当收到所有Http请求时。|


## 六、使用HttpService

HttpService的创建，基本和TcpService一致，也可以通过继承实现，下列仅演示最简单实现。

HttpService的相关事务，会通过**插件**触发。

### 6.1 创建HttpService

```csharp showLineNumbers
var service = new HttpService();
service.Setup(new TouchSocketConfig()//加载配置
    .SetListenIPHosts(7789)
    .ConfigureContainer(a =>
    {
        a.AddConsoleLogger();
    })
    .ConfigurePlugins(a =>
    {
        //以下即是插件
        a.Add<MyHttpPlug1>();
        a.Add<MyHttpPlug2>();
        a.Add<MyHttpPlug3>();
        a.Add<MyHttpPlug4>();

        //default插件应该最后添加，其作用是
        //1、为找不到的路由返回404
        //2、处理header为Option的探视跨域请求。
        a.UseDefaultHttpServicePlugin();
    }));
    
service.Start();
```

:::tip 提示

DefaultHttpServicePlugin插件最好添加在插件中，如果没有添加的话，最好自己做好缺省路由和跨域配置。

:::  

### 6.2 使用插件处理请求参数

#### 6.2.1 Query参数

```csharp showLineNumbers
string value = e.Context.Request.Query["key"];
```

#### 6.2.2 Header参数

```csharp showLineNumbers
string value = e.Context.Request.Headers["key"];
```

亦或者

```csharp showLineNumbers
string value = e.Context.Request.Headers[HttpHeaders.Cookie];
```

#### 6.2.3 Form参数

```csharp showLineNumbers
string value = e.Context.Request.Forms["key"];
```

### 6.3 使用插件处理请求

#### 6.3.1 Get请求

```csharp showLineNumbers
public class MyHttpPlug1 : PluginBase, IHttpPlugin<IHttpSocketClient>
{
    public async Task OnHttpRequest(IHttpSocketClient client, HttpContextEventArgs e)
    {
        if (e.Context.Request.IsGet())
        {
            if (e.Context.Request.UrlEquals("/success"))
            {
                //直接响应文字
                e.Context.Response.FromText("Success").Answer();//直接回应
                Console.WriteLine("处理完毕");
                return;
            }
        }

        //无法处理，调用下一个插件
        await e.InvokeNext();
    }
}
```

#### 6.3.2 Get文件请求

```csharp showLineNumbers
public class MyHttpPlug2 : PluginBase, IHttpPlugin<IHttpSocketClient>
{
    public async Task OnHttpRequest(IHttpSocketClient client, HttpContextEventArgs e)
    {
        if (e.Context.Request.IsGet())
        {
            if (e.Context.Request.UrlEquals("/file"))
            {
                try
                {
                    //直接回应文件。
                    e.Context.Response
                        .SetStatus()//必须要有状态
                        .FromFile(@"D:\System\Windows.iso", e.Context.Request);
                }
                catch (Exception ex)
                {
                    e.Context.Response.SetStatus(403)
                        .FromText(ex.Message)
                        .Answer();
                }

                return;
            }
        }
        await e.InvokeNext();
    }
}
```

#### 6.3.3 Get页面请求

```csharp showLineNumbers
public class MyHttpPlug3 : PluginBase, IHttpPlugin<IHttpSocketClient>
{
    public async Task OnHttpRequest(IHttpSocketClient client, HttpContextEventArgs e)
    {
        if (e.Context.Request.IsGet())
        {
            if (e.Context.Request.UrlEquals("/html"))
            {
                //回应html
                var stringBuilder = new StringBuilder();
                stringBuilder.AppendLine("<!DOCTYPE html>");
                stringBuilder.AppendLine("<html>");
                stringBuilder.AppendLine("<head>");
                stringBuilder.AppendLine("<meta charset=\"utf-8\"/>");
                stringBuilder.AppendLine("<title>TouchSocket</title>");
                stringBuilder.AppendLine("</head>");
                stringBuilder.AppendLine("<body>");
                stringBuilder.AppendLine("<div id=\"kuang\" style=\"width: 50%;height: 85%;left: 25%;top:15%;position: absolute;\">");
                stringBuilder.AppendLine("<a id=\"MM\"  style=\"font-size: 30px;font-family: 微软雅黑;width: 100%;\">王二麻子</a>");
                stringBuilder.AppendLine("<input type=\"text\" id=\"NN\" value=\"\" style=\"font-size: 30px;width:100%;position: relative;top: 30px;\"/>");
                stringBuilder.AppendLine("<input type=\"button\" id=\"XX\" value=\"我好\" style=\"font-size: 30px;width: 100%;position: relative;top: 60px;\" onclick=\"javascript:jump()\" />");
                stringBuilder.AppendLine("</div>");
                stringBuilder.AppendLine("</body>");
                stringBuilder.AppendLine("</html>");

                e.Context.Response
                         .SetStatus()//必须要有状态
                         .SetContentTypeByExtension(".html")
                         .SetContent(stringBuilder.ToString());
                e.Context.Response.Answer();
                return;
            }
        }

        await e.InvokeNext();
    }
}
```

#### 6.3.4 Post文件请求

```csharp showLineNumbers
public class MyHttpPlug4 : PluginBase, IHttpPlugin<IHttpSocketClient>
{
    public async Task OnHttpRequest(IHttpSocketClient client, HttpContextEventArgs e)
    {
        if (e.Context.Request.IsPost())
        {
            if (e.Context.Request.UrlEquals("/uploadfile"))
            {
                try
                {
                    if (e.Context.Request.TryGetContent(out var bodys))//一次性获取请求体
                    {
                        return;
                    }

                    while (true)//当数据太大时，可持续读取
                    {
                        var buffer = new byte[1024 * 64];
                        var r = e.Context.Request.Read(buffer, 0, buffer.Length);
                        if (r == 0)
                        {
                            return;
                        }

                        //这里可以一直处理读到的数据。
                    }

                    //下面逻辑是接收小文件。

                    if (e.Context.Request.ContentLength > 1024 * 1024 * 100)//全部数据体超过100Mb则直接拒绝接收。
                    {
                        e.Context.Response
                            .SetStatus(403, "数据过大")
                            .Answer();
                        return;
                    }

                    //此操作会先接收全部数据，然后再分割数据。
                    //所以上传文件不宜过大，不然会内存溢出。
                    var multifileCollection = e.Context.Request.GetMultifileCollection();

                    foreach (var item in multifileCollection)
                    {
                        var stringBuilder = new StringBuilder();
                        stringBuilder.Append($"文件名={item.FileName}\t");
                        stringBuilder.Append($"数据长度={item.Length}");
                        client.Logger.Info(stringBuilder.ToString());
                    }

                    e.Context.Response
                            .SetStatus()
                            .FromText("Ok")
                            .Answer();
                }
                catch (Exception ex)
                {
                    client.Logger.Exception(ex);
                }
            }
        }
        await e.InvokeNext();
    }
}
```

:::tip 提示

该操作支持大型文件，也支持断点续传、支持迅雷加速等。

:::  


## 七、创建加密Ssl的HttpsService

Https服务器，和http服务器几乎一样，只不过增加了一个Ssl的配置。

```csharp showLineNumbers
.SetServiceSslOption(new ServiceSslOption() 
{ 
    Certificate = new X509Certificate2("Socket.pfx", "Socket"), 
    SslProtocols = SslProtocols.Tls12 
})
```


[本文示例Demo](https://gitee.com/RRQM_Home/TouchSocket/tree/master/examples/Http)