基於ASP.NET core的MVC站點開發筆記 0x01

来源:https://www.cnblogs.com/lxmwb/archive/2020/07/05/13251340.html
-Advertisement-
Play Games

在斷斷續續的瞭解c#中,最先想到的就是ASP.NET開發了,在C2中應用也慢慢開始比較廣泛了,比如Covenant,有跨平臺的特性,值得看一看。 ...


基於ASP.NET core的MVC站點開發筆記 0x01

我的環境

OS type:mac
Software:vscode
Dotnet core version:2.0/3.1

dotnet sdk下載地址:https://dotnet.microsoft.com/download/dotnet-core/2.0

準備

先到上面提供的下載地址,下載對應平臺的dotnet裝上,然後在命令行視窗輸入dotnet --version查看輸出是否安裝成功。

然後,安裝visual studio code,安裝之後還需要安裝C#拓展,要不然每次打開cs文件都會報錯。

創建項目

新建一個空目錄,例如mvc-test

使用命令dotnet new查看可以新建的項目類型:

第一次嘗試,使用ASP.NET Core Empty就可以,代號是web,使用命令dotnet new web就可以新建一個空項目,項目的名稱就是當前目錄的名字mvc-test

項目結構與預設配置

目錄主要結構和文件功能如下:

Program.cs是程式的主類,Main函數在這裡定義,內容大致可以這麼理解:

CreateDefaultBuilder函數會使用預設的方法載入配置,例如通過讀取launchSettings.json確定當前的發佈環境:

webhost通過ASPNETCORE_ENVIRONMENT讀取發佈環境,然後會讀取對應的配置文件,Development對應appsettings.Development.jsonProduction對應appsettings.json

appsettings文件是整個web應用的配置文件,如果web應用需要使用某個全局變數,可以配置到這個文件裡面去。

webhost在運行前會通過Startup類,進行一些中間件的配置和註冊,以及進行客戶端的響應內容設置:

註:dotnet core 3版本里,取消了WebHost,使用Host以更通用的方式進行程式托管。

dotnet core 3 Program.cs

public static Void Main(string[] args)
{
    Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(builder =>
    {
        builder.UseStartup<Startup>();
    }).Build().Run();
}

獲取配置文件中的值

修改launingSettings.json中設置的發佈環境對應的配置文件,例如appsetttings.Delelopment.json內容,添加一個Welcome欄位配置項,如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "Welcome": "Hello from appsettings.json!!"
}

修改Startup.cs文件,添加IConfiguration config參數,.net core內部會將配置文件內容映射到這個變數:

/// <summary>
/// 註冊應用程式所需的服務
/// </summary>
public void ConfigureServices(IServiceCollection services)
{
}

/// <summary>
/// 註冊管道中間件
/// </summary>
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IConfiguration config)
{
    // 開發環境,使用開發者異常界面
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    var welcome = config["Welcome"];

    // Run一般放在管道末尾,運行完畢之後直接終止請求,所以在其後註冊的中間件,將不會被執行
    app.Run(async (context) =>
    {
        await context.Response.WriteAsync(welcome);
    });
}

在終端中使用命令dotnet run可以運行這個web應用:

瀏覽器訪問http://localhost:5000,可以看到已經成功獲取到Welcome配置項的值:

日誌列印

通過ILogger實現控制台日誌的列印:

public void ConfigureServices(IServiceCollection services)
{
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    IConfiguration config, 
    ILogger<Startup> logger)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    var welcome = config["Welcome"];

    logger.LogInformation(welcome);

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync(welcome);
    });
}

ILogger使用的時候需要指定列印日誌的類名Startup,最終列印效果如下:

服務註冊

上面的IConfiguration可以直接使用,是因為IConfiguration服務已經自動註冊過了。

對於自定義的服務,可以在ConfigureServices中註冊,例如自定義一個服務WelcomeService,項目目錄下新建兩個文件IWelcomeService.csWelcomeService.cs,內容如下:

/* IWelcomeService.cs
 *
 * 該介面類定義了一個getMessage方法。
 */
namespace mvc_test
{
    public interface IWelcomeService
    {
        string getMessage();
    }
}
/* WelcomeService.cs
 *
 * 該類實現了getMessage方法。
 */
 namespace mvc_test
{
    public class WelcomeService : IWelcomeService
    {
        int c = 0;
        public string getMessage()
        {
            c++;
            return "Hello from IWelcomeService Interface!!!" + c.ToString();
        }
    }
}

然後在ConfigureServices中註冊服務:

public void ConfigureServices(IServiceCollection services)
{
        services.AddSingleton<IWelcomeService, WelcomeService>();
}

然後在Configure中使用的時候需要傳參:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    IConfiguration config, 
    ILogger<Startup> logger,
    IWelcomeService welcomeService)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    //var welcome = config["Welcome"];
    var welcome = welcomeService.getMessage();

    logger.LogInformation(welcome);

    // Run一般放在管道末尾,運行完畢之後直接終止請求,所以在其後註冊的中間件,將不會被執行
    app.Run(async (context) =>
    {
        await context.Response.WriteAsync(welcome);
    });
}

運行後結果:

這個例子中,註冊服務使用的函數是AddSingleton,服務的生命周期除了Singleton,還有其他兩個模式:ScopedTransient

這三個模式的區別:

  • Transient:瞬態模式,服務在每次請求時被創建,它最好被用於輕量級無狀態服務;
  • Scoped:作用域模式,服務在每次請求時被創建,整個請求過程中都貫穿使用這個創建的服務。比如Web頁面的一次請求;
  • Singleton:單例模式,服務在第一次請求時被創建,其後的每次請求都用這個已創建的服務;

參考資料:

初始學習使用AddSingleton就行了。

中間件和管道

中間件是一種用來處理請求和響應的組件,一個web應用可以有多個中間件,這些中間件共同組成一個管道,每次請求消息進入管道後都會按中間件順序處理對應請求數據,然後響應結果原路返回:

參考資料:

內置中間件的使用:處理靜態文件訪問請求

新建一個目錄wwwroot,目錄下新建index.html文件:

<html>
    <head>
        <title>TEST</title>
    </head>
    <body>
        <h1>Hello from index.html!!!</h1>
    </body>
</html>

使用之前的代碼,dotnet run運行之後訪問http://localhost:5000/index.html,發現還是之前的結果,並沒有訪問到index.html

這時候需要使用中間件StaticFiles來處理靜態文件的請求,修改Startup.cs的部分內容如下:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    IConfiguration config, 
    ILogger<Startup> logger,
    IWelcomeService welcomeService)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseStaticFiles();

    //var welcome = config["Welcome"];

    app.Run(async (context) =>
    {
        var welcome = welcomeService.getMessage();
        logger.LogInformation(welcome);
        await context.Response.WriteAsync(welcome);
    });
}

重新啟動後可正常訪問到index.html

前面講到請求進入管道之後是安裝中間件添加順序處理的請求,如果當前中間件不能處理,才會交給下一個中間件,所以可以嘗試一下將上面的代碼調整一下順序:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    IConfiguration config, 
    ILogger<Startup> logger,
    IWelcomeService welcomeService)
{
    if (env.IsDevelopment())
    {å
        app.UseDeveloperExceptionPage();
    }

    //var welcome = config["Welcome"];

    app.Run(async (context) =>
    {
        var welcome = welcomeService.getMessage();
        logger.LogInformation(welcome);
        await context.Response.WriteAsync(welcome);
    });

    app.UseStaticFiles();
}

可以看到StaticFiles放到了最後,這樣的話因為index.html請求會先到Run的地方,直接返回了,所以不能進入到StaticFiles里,訪問得到的內容就是:

通過StaticFiles可以成功訪問到index.html,但是如果想要index.html成為預設網站主頁,需要使用中間件DefaultFiles,修改上面代碼為:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    IConfiguration config, 
    ILogger<Startup> logger,
    IWelcomeService welcomeService)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseDefaultFiles();
    app.UseStaticFiles();

    //var welcome = config["Welcome"];

    app.Run(async (context) =>
    {
        var welcome = welcomeService.getMessage();
        logger.LogInformation(welcome);
        await context.Response.WriteAsync(welcome);
    });
}

DefaultFiles內部會自動將/修改為index.html然後交給其他中間件處理,所以需要放在StaticFiles的前面。

使用FileServer也可以實現同樣的效果:

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    IConfiguration config, 
    ILogger<Startup> logger,
    IWelcomeService welcomeService)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseFileServer();

    //var welcome = config["Welcome"];

    app.Run(async (context) =>
    {
        var welcome = welcomeService.getMessage();
        logger.LogInformation(welcome);
        await context.Response.WriteAsync(welcome);
    });
}

中間件的一般註冊方式

除了使用內置的中間件之外,還可以用以下幾種方式註冊中間件:

  • Use
  • UseWhen
  • Map
  • MapWhen
  • Run

UseUseWhen註冊的中間件在執行完畢之後可以回到原來的管道上;
MapMapWhen可以在新的管道分支上註冊中間件,不能回到原來的管道上;
When的方法可以通過context做更多的中間件執行的條件;
Run用法和Use差不多,只不過不需要接收next參數,放在管道尾部;

例如實現返回對應路徑內容:

/// <summary>
/// 註冊應用程式所需的服務
/// </summary>
public void ConfigureServices(IServiceCollection service)
{
    
}

/// <summary>
/// 註冊管道中間件
/// </summary>
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
    // 開發環境,添加開發者異常頁面
    if(env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // Use 方式
    app.Use(async (context, next) =>
    {
        if(context.Request.Path == new PathString("/use"))
        {
            await context.Response.WriteAsync($"Path: {context.Request.Path}");
        }
        await next();
    });

    // UseWhen 方式
    app.UseWhen(context => context.Request.Path == new PathString("/usewhen"),
    a => a.Use(async (context, next) =>
    {
        await context.Response.WriteAsync($"Path: {context.Request.Path}");
        await next();
    }));

    // Map 方式
    app.Map(new PathString("/map"),
    a => a.Use(async (context, next) =>
    {
        // context.request.path 獲取不到正確的路徑
        //await context.Response.WriteAsync($"Path: {context.Request.Path}");
        await context.Response.WriteAsync($"PathBase: {context.Request.PathBase}");
        foreach(var item in context.Request.Headers)
        {
            await context.Response.WriteAsync($"\n{item.Key}: {item.Value}");
        }
    }));

    // MapWhen 方式
    app.MapWhen(context => context.Request.Path == new PathString("/mapwhen"),
    a => a.Use(async (context, next) =>
    {
        await context.Response.WriteAsync($"Path: {context.Request.Path}");
        await next();
    }));

    // Run 放在最後,可有可無,主要為了驗證是否可以回到原來的管道上繼續執行
    app.Run(async (context)=>
    {
        await context.Response.WriteAsync("\nCongratulation, return to the original pipe.");
    });
}

可以看到只有/use/usewhen可以執行到Run

註:這裡碰到一個問題,就是訪問/map路徑的時候獲取到的context.Request.Path為空,其他欄位獲取都挺正常,神奇。不過,可以使用context.Request.PathBase獲取到。

自己封裝中間件

對於上面註冊中間件的幾種方式,比如Use內部如果寫太多的代碼也不合適,所以可以自己封裝中間件,封裝完成之後可以像內置中間件一樣使用UseXxxx的方式註冊。

本例目標要完成一個中間件可以檢測HTTP請求方法,僅接受GETHEAD方法,步驟如下:
新建一個文件夾mymiddleware,新建文件HttpMethodCheckMiddleware.cs,中間件封裝需要實現兩個方法:

  • HttpMethodCheckMiddleware: 構造函數,參數類型為RequestDelegate
  • Invoke: 中間件調度函數,參數類型為HttpContext,返回類型為Task

文件內容如下:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace middleware.mymiddleware
{
    /// <summary>
    /// 請求方法檢查中間件,僅處理HEAD和GET方法
    /// </summary>
    public class HttpMethodCheckMiddleware
    {
        private readonly RequestDelegate _next;

        /// <summary>
        /// 構造方法,必須有的
        /// </summary>
        /// <param name="requestDelegate">下一個中間件</param>
        public HttpMethodCheckMiddleware(RequestDelegate requestDelegate)
        {
            this._next = requestDelegate;
        }

        /// <summary>
        /// 中間件調度方法
        /// </summary>
        /// <param name="context">HTTP上下文</param>
        /// <returns>TASK任務狀態</returns>
        public Task Invoke(HttpContext context)
        {
            // 如果符合條件,則將httpcontext傳給下一個中間件處理
            if(context.Request.Method.ToUpper().Equals(HttpMethods.Head)
                || context.Request.Method.ToUpper().Equals(HttpMethods.Get))
            {
                return _next(context);
            }

            // 否則直接返回處理完成
            context.Response.StatusCode = 400;
            context.Response.Headers.Add("X-AllowedHTTPVerb", new[] {"GET,HEAD"});
            context.Response.ContentType = "text/plain;charset=utf-8";  // 防止中文亂碼
            context.Response.WriteAsync("只支持GET、HEAD方法");
            return Task.CompletedTask;
        }
    }
}

這樣就可以直接在Startup中使用了:

app.UseMiddleware<HttpMethodCheckMiddleware>();

還可以編寫一個擴展類,封裝成類似內置中間件的方式UseXxx。新建CustomMiddlewareExtension.cs文件,內容如下:

using Microsoft.AspNetCore.Builder;

namespace middleware.mymiddleware
{
    /// <summary>
    /// 封裝中間件的擴展類
    /// </summary>
    public static class CustomMiddlewareExtension
    {
        /// <summary>
        /// 添加HttpMethodCheckMiddleware中間件的擴展方法
        /// </summary>
        public static IApplicationBuilder UseHttpMethodCheckMiddleware(this IApplicationBuilder app)
        {
            return app.UseMiddleware<HttpMethodCheckMiddleware>();
        }
    }
}

現在就可以直接調用UseHttpMethodCheckMiddleware註冊中間件了.

執行結果截圖省略。

疑問:那個CustomMiddlewareExtension也沒見引用,怎麼就可以直接使用app.UseHttpMethodCheckMiddleware方法了?
有的可能和我一樣,c#都沒有學明白就直接開始擼dotnet了,看到這一臉懵逼,不過經過一番搜索,原來這是c#中對已有類或介面進行方法擴展的一種方式,參考C#編程指南

內置路由

這一節先當瞭解,暫時用處不大,學完也會忘掉

先簡單看一下ASP.NET core內置的路由方式(直接上startup.cs代碼內容):

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;

namespace routing
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection servcies)
        {

        }

        public void Configure(IApplicationBuilder app)
        {
            // 新建一個路由處理器
            var trackPackageRouteHandler = new RouteHandler(context =>
            {
                var routeValues = context.GetRouteData().Values;
                return context.Response.WriteAsync($"Hello! Route values: {string.Join(", ", routeValues)}");
            });
            var routeBuilder = new RouteBuilder(app, trackPackageRouteHandler);
            // 通過MapRoute添加路由模板
            routeBuilder.MapRoute("Track Package Route", "package/{opration}/{id:int}");
            routeBuilder.MapGet("hello/{name}", context =>
            {
                var name = context.GetRouteValue("name");
                return context.Response.WriteAsync($"Hi, {name}!");
            });
            var routes = routeBuilder.Build();
            app.UseRouter(routes);
        }
    }
}

從代碼中可知,需要先創建一個路由處理器trackPackageRouteHandler,然後通過RouteBuilderapptrackPackageRouteHandler綁定,而且需要添加一個匹配模板,最後將生成的路由器添加到app中。
其中添加路由匹配模板是使用了不同的方法:

  • MapRoute: 這個方法設定一個路由模板,匹配成功的請求會路由到trackPackageRouteHandler;
  • MapGet: 這個方法添加的模板,只適用於GET請求方式,並且第二個參數可以指定處理請求的邏輯;

上面設置路由的方式過於複雜,所以一般情況下通常使用MVC將對應的URL請求路由到Controller中處理,簡化路由規則。

Controller和Action

在開始MVC路由之前,先來學習一下ControllerAction他們的關係以及如何創建。

Controller一般是一些public類,Action對應Controller中的public函數,所以他們的關係也很明瞭:一個Controller可以有多個Action

Controller如何創建,預設情況下滿足下麵的條件就可以作為一個Controller

  • 在項目根目錄的Controllers
  • 類名稱以Controller結尾並繼承自Controller,或被[Controller]標記的類
  • 共有類
  • 沒有被[NotController]被標記

例如一個Contoller的常用模式如下:

using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
    //...
}

Action就不需要許多條條框框了,只要寫在Controller中的方法函數都會被當成Action對待,如果不想一個函數被當做Action則需要添加[NotAction]標記。

留待測試:

  1. 如果同時添加[Controller][NotController]會發生什麼狀況?是誰在最後誰生效嗎還是報錯?
  2. 是不是只需要滿足Controller尾碼就可以了,不一定非得繼承Controller,繼承他只是為了使用一些已經打包好的父類函數。

MVC路由

首先創建一個HomeController測試路由用,需要創建到Controllers目錄下:

using Microsoft.AspNetCore.Mvc;

namespace routing.Controllers
{
    public class HomeController: Controller
    {
        public string Index()
        {
            return "Hello from HomeController.Index";
        }
    }
}

.net core 2.0.net core 3.0創建路由的方式有所不同,現在分開說一下,先說一下舊的方式。

先在ConfigureServices中註冊MVC服務,然後Configure中配置路由模板:

public void ConfigureServices(IServiceCollection service)
{
    // 註冊服務
    service.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
    if(env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    // 路由模板
    app.UseMvc(routes =>
    {
        routes.MapRoute(template: "{controller}/{action}/{id?}", 
                        defaults: new {controller = "Home", action = "Index"});
    });

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

但是放到dotnet3裡面是會報錯的:

MVCRouteStartup.cs(23,13): warning MVC1005: Using 'UseMvc' to configure MVC is not supported while using Endpoint Routing. To continue using 'UseMvc', please set 'MvcOptions.EnableEndpointRouting = false' inside 'ConfigureServices'. 

提示UseMvc不支持Endpoint Routing,通過查資料(stackoverflow)找到原因,說的很清楚:2的時候MVC路由基於IRoute,3改成Endpoint了,官方推薦將UseMVC使用UseEndpoiont替換:

app.UseRouting(); // 必須寫,如果使用了UseStaticFiles要放在他之前
app.UseEndpoints(endpoionts =>
{
    endpoionts.MapControllerRoute(name: "MVC TEST ROUTE", 
                                pattern: "{controller}/{action}/{id?}",
                                defaults: new {controller = "Home", action = "Index"});
});

ConfigureServices中註冊MVC也有兩種方式:

services.AddMVC();

service.AddControllersWithViews();
service.AddRazorPages();

當然,如果不想把UseMap去掉,那麼可以按照報錯的提示在AddMVC的時候配置一下參數禁用EndpointRoute

services.AddMvc(options => options.EnableEndpointRouting = false);

然後就可以跑起來了:

好,扯了半天報錯,還是回到mvc路由上,上面是簡單演示了一下在Startup中如何創建路由,其實mvc路由有兩種定義方式:

  • 約定路由:上面使用的方式就是約定路由,需要在Startup中配置;
  • 特性路由:使用[Route]直接對controlleraction進行標記;

修改HomeController加上路由標記:

using Microsoft.AspNetCore.Mvc;

namespace routing.Controllers
{
    [Route("h")]
    [Route("[controller]")]
    public class HomeController: Controller
    {
        [Route("")]
        [Route("[action]")]
        public string Index()
        {
            return "Hello from HomeController.Index";
        }
    }
}

通過[controller][action]就可以動態的指代homeindex(路徑不區分大小寫),這樣如果路由會隨著類名或方法名稱的改變自動調整。

並且可以看出,可以多個[Route]標記重疊使用,例如訪問/h/home/index效果一樣:

通過實驗可以看出,特性路由會覆蓋掉約定路由

先總結這些吧,突然發現asp.net core這個東西還是挺先進的,比如依賴註入,Startup中的函數多數都是interface,為什麼直接對介面操作就可以改變一些東西或者讓我們可以自己註冊一個中間件到app上,然後為什麼都不需要引用或者實例化就可以直接用app調用了,這都和依賴註入有關係吧,還有介面的設計理念也好像和其他語言的不太一樣,神奇了。

實驗代碼

放到了github上,部分代碼好像丟失了,不過應該不要緊。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 總結: 常量(掌握) 在程式運行過程中,其值不可以發生改變的量。 Java中的常量:字元串、整數、小數、字元、布爾、null 變數(掌握) 在程式運行過程中,其值可以發生改變的量。 變數的定義格式:數據類型 變數名 = 初始化值; 同一對{}中,定義變數名不能重覆 變數未賦值不能使用 定義long類 ...
  • 總結: 註釋(掌握) 單行註釋:// 多行註釋:/* */ 關鍵字(瞭解) 賦予了特殊含義的單詞 關鍵字的字母全部小寫 編輯器對關鍵字都有高亮顯示 標識符(掌握) 由字母、數字、下劃線“_”、美元符號“$”組成,第一個字元不能是數字 不能使用java中的關鍵字作為標識符 小駝峰式命名:變數名、方法名 ...
  • 1. Collection a) List (有序,可重覆) i. ArrayList底層用數組實現。線程不安全,效率高 ii. LinkedList:底層用雙向鏈表實現。線程不安全,效率高 ii. Vector:底層用數組實現。線程安全,效率低 b) Set (無序,不可重覆) HashSet(內 ...
  • 棧 1. 存放局部變數 2. 不可以被多個線程共用 3. 空間連續,速度快 堆 1. 存放對象 2. 可以被多個線程共用 3. 空間不連續,速度慢。但是靈活 方法區 1. 存放類的信息:代碼、靜態變數、字元串常量等 2. 可以被多個線程共用 3. 空間不連續,速度慢。但是靈活 ...
  • 1 前言 Docker是容器開發的事實標準,而Springboot是Java微服務常用框架,二者必然是會走到一起的。本文將講解如何開發Springboot項目,把它做成Docker鏡像,並運行起來。 2 把Springboot打包成Docker鏡像 Springboot的Web開發非常簡單,本次使用 ...
  • 方法重載(Overload): 兩同(同一個類、同一個方法名)三不同(參數列表不同:;類型、個數、順序) 返回值不同,構成重載嗎? No 形參名稱不同,構成重載嗎? No 方法重寫(0verride): 子類定義一個和父類方法名,參數完全相同的方法。 =: 方法名保持一致 >=: 子類許可權修飾符可以 ...
  • 上篇文章提到FFmpeg解決項目中視頻和語音問題,說道C#和FFmpeg不得不提的2個類庫。1、Xabe.FFmpeg簡單查看了下源碼和demo,發現基於ffmpeg.exe的命令行參數進行處理。2、FFmpeg.AutoGen 把C語言對外API和類型翻譯成C#的API和對象。看了一下這個源碼生成... ...
  • 只讀成員 private struct Point { public Point(double x, double y) { X = x; Y = y; } private double X { get; set; } private double Y { get; set; } private r ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...