标签 .net core 下的文章

在.Net Core 2.1下使用SkiaSharp进行图片处理

在.Net Core下,没有可以支持跨平台的Drawing类库,官网提供的Common.Drawing只能在Windows下使用,那么在.Net Core下该如何处理图片呢?其实有很多第三方提供了解决方案,而我比较喜欢用的是Mono团队提供的SkiaSharp,原因是稳定而且支持的也很好,性能上也还好。

一、SkiaSharp是什么?

1.Skia介绍

Skia是Google旗下的2D图形处理库,下面是援引百科中的词条:

skia是个2D向量图形处理函数库,包含字型、坐标转换,以及点阵图都有高效能且简洁的表现。不仅用于Google Chrome浏览器,新兴的Android开放手机平台也采用skia作为绘图处理,搭配OpenGL/ES与特定的硬件特征,强化显示的效果。

Skia官网中是这样介绍的:

Skia is an open source 2D graphics library which provides common APIs that work across a variety of hardware and software platforms. It serves as the graphics engine for Google Chrome and Chrome OS, Android, Mozilla Firefox and Firefox OS, and many other products.

2.SkiaSharp介绍

SkiaSharp故名思义,就是在.net下使用Skia API的库,是SkiaSharp是由mono团队开发并进行持续维护,至今已经多年了。目前的最新版本是1.60.3,当前支持.net下的:

  • .NET Standard 1.3

  • .NET Core

  • Tizen

  • Xamarin.Android

  • Xamarin.iOS

  • Xamarin.tvOS

  • Xamarin.watchOS

  • Xamarin.Mac

  • Windows Classic Desktop (Windows.Forms / WPF)

  • Windows UWP (Desktop / Mobile / Xbox / HoloLens)

SkiaSharp项目:https://github.com/mono/SkiaSharp

二、SkiaSharp的安装

可以通过nuget命令进行安装:

nuget install skiasharp

或者在要使用的项目下,打开nuget管理器,搜索skiasharp进行安装。

三、SkiaSharp的使用

1.生成缩略图

这里假设已经安装好SkiaSharp 1.60.3版本。
我们先把要缩略的原图加载到内存中:

using (var input = File.OpenRead($"{PlatformServices.Default.Application.ApplicationBasePath}wwwroot/{pic}"))

这里的变量pic是图片的相对路径。
之后实例化一个SKManagedStream

using (var inputStream = new SKManagedStream(input))

最后,把inputStream加载到SKBitmap画布中

using (var original = SKBitmap.Decode(inputStream))

之后重新设置图片的尺寸,也就是完成缩略处理:

        using (var resized = original
           .Resize(new SKImageInfo(width, height), SKBitmapResizeMethod.Lanczos3))
        {
            if (resized == null) return "";
            using (var image = SKImage.FromBitmap(resized))
            {
                using (var output =
                       File.OpenWrite($"{PlatformServices.Default.Application.ApplicationBasePath}wwwroot/{thumb_name}"))
                {
                    image.Encode(SKEncodedImageFormat.Png,quality)
                        .SaveTo(output);
                }
            }
        }

其中,变量widthheight分别为缩略图的宽度和高度,thumb_name为缩略图要保存的文件名,quality是质量,一般设置为75,或者是其他的自己觉得合适的值。
完整的例子:

using System.IO;
using Microsoft.Extensions.PlatformAbstractions;
using SkiaSharp;

        public static string MakeThumb(string pic,string thumb_dir, int width, int height)
        {
            const int quality = 75; //质量为75%
            using (var input = File.OpenRead($"{PlatformServices.Default.Application.ApplicationBasePath}wwwroot/{pic}"))
            using (var inputStream = new SKManagedStream(input))
            using (var original = SKBitmap.Decode(inputStream))
            {
                string[] arr_pic = pic.Split('/');
                string filename = arr_pic[arr_pic.Length - 1];  //完整文件名
                string[] arr_filename = filename.Split('.');
                string ext = "";
                if (arr_filename.Length >= 2)
                {
                    ext = arr_filename[arr_filename.Length - 1];    //最后一个为扩展名
                }
                string thumb_name = $"{filename.Remove(filename.Length - ext.Length - 1)}-{width}x{height}.png";  //文件名,缩略图保存为png
                string save_dir = $"/attach/{thumb_dir}/thumb/{DateTime.Now.ToString("yyyy-MM-dd")}";
                string savepath = $"{PlatformServices.Default.Application.ApplicationBasePath}wwwroot{save_dir}";
                if (!Directory.Exists(savepath))
                {
                    Directory.CreateDirectory(savepath);
                }
                string thumb_file = $"{save_dir}/{thumb_name}";
                using (var resized = original
                   .Resize(new SKImageInfo(width, height), SKBitmapResizeMethod.Lanczos3))
                {
                    if (resized == null) return "";
                    using (var image = SKImage.FromBitmap(resized))
                    {
                        using (var output =
                               File.OpenWrite($"{savepath}/{thumb_name}"))
                        {
                            image.Encode(SKEncodedImageFormat.Png,quality)
                                .SaveTo(output);
                        }
                    }
                }
                return thumb_file;
            }
        }

2.把指定的字体打印到图片上

其实图片的文字水印、图片验证码都可以从这个例子上扩充出来。
首先还是要安装SkiaSharp,之后,实例化SKImageInfo

var info = new SKImageInfo(width, height);

创建一个新的SKSurface

using (var surface = SKSurface.Create(info))

设置画布背景透明:

var canvas = surface.Canvas;
canvas.Clear(SKColors.White);

设置SKPaint的参数

       var paint = new SKPaint
        {
            Color = SKColors.Black,//颜色
            IsAntialias = true,//抗锯齿
            Style = SKPaintStyle.Fill,
            TextAlign = SKTextAlign.Center,//居中
            TextSize = 40F,//字号
            Typeface= SkiaSharp.SKTypeface.FromFile(fontpath, 0)//加载字体
        };

这里除了指定字体的路径之外,还可以使用SkiaSharp.SKTypeface.FromFamilyName("微软雅黑",SKTypefaceStyle.Bold)来通过字体名来设置要使用的字体;参数fontpath是字体的物理路径。

参数设置好之后,进行绘图:

var coord = new SKPoint(info.Width / 2, (info.Height + paint.TextSize / 2);
canvas.DrawText(text, coord, paint);

最后,生成图片:

using (var image = surface.Snapshot())
using (var data = image.Encode(SKEncodedImageFormat.Png, 100))

一个简单的例子:

using SkiaSharp;
using System.Linq;
        public static byte[] CreateImage(string fontpath, string text,float font_size=100)
        {
            var info = new SKImageInfo(1100, 480);
            using (var surface = SKSurface.Create(info))
            {
                var canvas = surface.Canvas;
                canvas.Clear(SKColors.White);
                
                var paint = new SKPaint
                {
                    Color = SKColors.Black,
                    IsAntialias = true,
                    Style = SKPaintStyle.Fill,
                    TextAlign = SKTextAlign.Center,
                    TextSize = font_size,
                    Typeface= SkiaSharp.SKTypeface.FromFile(fontpath, 0)
                };
                var coord = new SKPoint(info.Width / 2, (info.Height + paint.TextSize) / 2);
                canvas.DrawText(text, coord, paint);                
                using (var image = surface.Snapshot())
                using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
                {
                    return data.ToArray();
                }
            }
        }

这个是指定的文字内容使用指定的字体直接显示到空白图片上,但是不支持文字换行。我们下面的例子是对上面的进行改进,支持文字换行:

public static byte[] CreateImage(string fontpath, string text,float font_size=100)
{
    //支持文字多行
    List<string> list = text.Split('\n').ToList();
    list.RemoveAll(x => { return string.IsNullOrEmpty(x.Trim()); });    //删除空行
    list.Reverse(); //顺序反转
    float line_height = 1.5F;   //行距
    float height = 480;
    if (list.Count * line_height*font_size >= height)
    {
        height = list.Count * line_height * font_size;
    }
    var info = new SKImageInfo(1100, (int)height);
    using (var surface = SKSurface.Create(info))
    {
        var canvas = surface.Canvas;
        canvas.Clear(SKColors.White);
        
        var paint = new SKPaint
        {
            Color = SKColors.Black,
            IsAntialias = true,
            Style = SKPaintStyle.Fill,
            TextAlign = SKTextAlign.Center,
            TextSize = font_size,
            Typeface= SkiaSharp.SKTypeface.FromFile(fontpath, 0)
        };

        int i = 0;
        list.ForEach(x =>
        {
            var coord = new SKPoint(info.Width / 2, (info.Height + paint.TextSize * (list.Count - i) - paint.TextSize * i * 1.5F) / 2);
            canvas.DrawText(x.Trim(), coord, paint);
            i++;
            
        });

        using (var image = surface.Snapshot())
        using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
        {
            return data.ToArray();
        }
    }
}

测试地址:http://tool.dwz.nz/fonts
源码:https://github.com/hongbai/FontsPrint

四、注意事项

如果要在Linux上使用,还需要同时上传libSkiaSharp.so文件,放到与SkiaSharp.dll同一文件夹下。libSkiaSharp.so文件可以在SkiaSharp的github上下载最新的发行版本,下载地址:https://github.com/mono/SkiaSharp/releases

五、总结

通过以上两个例子,我们可以发现,SkiaSharp的使用方法非常简单方便,而且各方面支持的都很不错,支持跨平台。功能上我暂时只在以上两个例子中使用,如果以后在其他方面用到的话,我会继续更新。代码写的丑,多包涵。

以上。

参考:
Skia Graphics Library
Skia 百度百科
SkiaSharp Github项目

.Net Core 2.1下,使用Oracle数据库

在.Net Core 2.1下,可以通过Dapper框架非常方便的使用Sql Server、MySQL、PostgreSQL等数据库,但是使用Oracle数据库就没那么方便了,比如执行存储过程的时候返回的游标,就没有对应的类型,另外还需要Oracle的驱动等等。
经过了几天的查阅资料,现在整理下在.Net Core 2.1下通过Dapper使用Oracle数据库的方法。
首先是连接数据库,方法与MySQL中的方法一样,见《在.Net Core 2.0中使用MySQL》,之后需要通过NuGet安装以下包:

  • Dapper

  • Dapper.Oracle

  • Oracle.ManagedDataAccess.Client

其中Oracle.ManagedDataAccess.Client是预发行版,需要勾选“包括预发行版”才能搜索到。
准备工作做好,下面是使用方法:
创建Oracle数据库链接:
var Conn = new OracleConnection(ConnString);

之后创建Oracle动态参数:
var parameters = new OracleDynamicParameters();

之后往动态参数中添加存储过程需要的参数:

parameters.Add("p_czlx", value: transfer.czlx);
parameters.Add("p_bzxx", value: transfer.bzxx);
parameters.Add("ResultStr", dbType: OracleMappingType.Varchar2, direction: ParameterDirection.Output);
parameters.Add("ResultCursor", dbType: OracleMappingType.RefCursor, direction: ParameterDirection.Output);

调用存储过程,得到返回的游标和出参:

var data = Conn.QueryFirstOrDefault<Models.UserMoney>("pkg_web.web_crj_gl", param: parameters, commandType: CommandType.StoredProcedure);
string res = parameters.Get<string>("ResultStr");   //出参

其中Models.UserMoney是游标对应的实体。
相对完整的代码:

using (var Conn = new OracleConnection(ConnString))
{
    var parameters = new OracleDynamicParameters();
    parameters.Add("p_czlx", value: transfer.czlx);
    parameters.Add("p_bzxx", value: transfer.bzxx);
    parameters.Add("ResultStr", dbType: OracleMappingType.Varchar2, direction: ParameterDirection.Output);
    parameters.Add("ResultCursor", dbType: OracleMappingType.RefCursor, direction: ParameterDirection.Output);

    var data = Conn.QueryFirstOrDefault<Models.UserMoney>("pkg_web.web_crj_gl", param: parameters, commandType: CommandType.StoredProcedure);
    string res = parameters.Get<string>("ResultStr");   //出参
    //对出参进行后续处理,省略

}

是不是很简单?
说下引用的这几个包的作用,Dapper就是ORM框架,Dapper.Oracle是针对Oracle重写的Dapper动态参数,Oracle.ManagedDataAccess.Client是Oracle官方提供的Oracle驱动,目前只有预发行版,还没有稳定版。
其实在第一次使用.Net Core+Oracle组合的时候,我也走了很多弯路,查到过好几篇博客提供的重写的OracleDynamicParameters方法,但是取出参的时候类型转换都有问题,本着能用现成的绝不自己手写的码畜精神,终于在gayhub(雾,github)上找到一条回复,提到了Dapper.Oracle,安装了之后果然好用。
希望这篇文章能帮到大家。

(完)

.Net Core 2.0的坑——POST提交HTTPS时证书报错的问题

最近接入腾讯云的短信平台,平台的接口是HTTPS的,开发环境是Windows,而生产环境是Linux(CentOS)。在开发调试的时候都正常通过,没问题;而部署到CentOS上之后,会报System.PlatformNotSupportedException: The handler does not support custom handling of certificates with this combination of libcurl (7.29.0) and its SSL backend ("NSS/3.34").错误。这个问题在网上找了一圈,好不容易Google到一个俄文的Blog,里面给出了一个解决方法,但是博主特别强调了一下,这是一个非常糟糕的方法:

yum update
yum install openssl-devel gcc
wget https://curl.haxx.se/download/<latest>.tar.gz
tar -zxf <latest>.tar.gz
cd <latest>
./configure --prefix=/usr/local/curl/ --without-nss --with-ssl=/usr/local/ssl/
make && make install 
 
echo '/usr/local/curl/lib' > /etc/ld.so.conf.d/libcurl.conf && ldconfig 

https://curl.haxx.se/download/<;latest>.tar.gz 是curl的最新版本下载地址,目前是
https://curl.haxx.se/download/curl-7.60.0.tar.gz
之后就可以正常提交了。
感谢原作者提供的方法,原博客:https://ales79.blogspot.com/2018/06/centos-7-libcurl-with-openssl-backend.html

我使用的Post方法如下:

        public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            //直接确认,否则打不开    
            return true;
        }

        /// <summary>
        /// Post提交数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="url">目标地址</param>
        /// <param name="contentType">数据类型</param>
        /// <param name="charset">编码</param>
        /// <returns></returns>
        public static string Post(string data, string url,string contentType,string charset= "utf-8")
        {
            System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接

            string result = "";//返回结果

            int timeout = 30;
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            Stream reqStream = null;

            try
            {
                //设置最大连接数
                ServicePointManager.DefaultConnectionLimit = 200;                
                //设置https验证方式
                if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                {
                    ServicePointManager.ServerCertificateValidationCallback =
                            new RemoteCertificateValidationCallback(CheckValidationResult);
                }

                /***************************************************************
                * 下面设置HttpWebRequest的相关属性
                * ************************************************************/
                request = (HttpWebRequest)WebRequest.Create(url);

                request.Method = "POST";
                request.Timeout = timeout * 1000;

                ////设置代理服务器
                //WebProxy proxy = new WebProxy();                          //定义一个网关对象
                //proxy.Address = new Uri(WxPayConfig.PROXY_URL);              //网关服务器端口:端口
                //request.Proxy = proxy;

                //设置POST的数据类型和长度
                request.ContentType = $"{contentType};charset={charset}";
                byte[] res = System.Text.Encoding.GetEncoding(charset).GetBytes(data);
                request.ContentLength = res.Length;

                //往服务器写入数据
                reqStream = request.GetRequestStream();
                reqStream.Write(res, 0, res.Length);
                reqStream.Close();

                //获取服务端返回
                response = (HttpWebResponse)request.GetResponse();

                //获取服务端返回数据
                StreamReader sr = new StreamReader(response.GetResponseStream(), System.Text.Encoding.GetEncoding(charset));

                result = sr.ReadToEnd().Trim();
                sr.Close();
            }
            catch (System.Threading.ThreadAbortException e)
            {
                System.Threading.Thread.ResetAbort();
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    
                }
            }
            catch (Exception e)
            {
                
            }
            finally
            {
                //关闭连接和流
                if (response != null)
                {
                    response.Close();
                }
                if (request != null)
                {
                    request.Abort();
                }
            }
            return result;
        }

ASP.Net Core 2.0 在Linux下连接SQL Server数据库问题

在ASP.Net Core 2.0下,通过Dapper来使用SQL Server数据库,在Windows系统下完全正常,而部署到Linux服务器上会出现连不上数据库的情况,从日志里看,报下面的错误:

Connection Timeout Expired. The timeout period elapsed during the post-login phase. The connection could have timed out while waiting for server to complete the login process and respond; Or it could have timed out while attempting to create multiple active connections. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=23; handshake=365; [Login] initialization=0; authentication=2; [Post-Login] complete=28022;

连接超时。开始的时候怀疑是防火墙的原因,检查了一下防火墙正常,而且通过telnet命令检查数据库的1433端口是通的,看来问题是出在.net core上。
通过百度和Google搜索相关的关键字,找到了一篇帖子:《Timeout Connecting to SQL Server instance from Linux》,说的是只有SQL Server 2008 及之前的版本会有这问题,SQL Server 2012及之后修复了这个问题。
检查了下自己的SQL数据库版本,是SQL Server 2008 R2 版的,正在此列。开了腾讯云的SQL Server云数据库连接测试,完全正常。

以上。

ASP.NET Core 2.0下使用log4net记录文件日志

我们知道log4net的日志功能非常强大,而使用方法也比较复杂;在ASP.NET Core 2.0下,可以通过一个第三方的扩展方法来降低我们的使用难度,具体使用方法如下:
我们先新建一个自己的静态类Log4Net,用于之后调用记录日志:

    public static class Log4Net
{
    private static readonly log4net.ILog log =
    log4net.LogManager.GetLogger(typeof(Log4Net));

    static void SetConfig()
    {
        XmlDocument log4netConfig = new XmlDocument();
        log4netConfig.Load(File.OpenRead("log4net.config"));

        var repo = log4net.LogManager.CreateRepository(
            Assembly.GetEntryAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy));

        log4net.Config.XmlConfigurator.Configure(repo, log4netConfig["log4net"]);
    }

    /// <summary>
    /// 信息
    /// </summary>
    /// <param name="Message"></param>
    public static void LogInfo(string Message)
    {
        if (!log.IsInfoEnabled)
            SetConfig();
        log.Info(Message);
    }

    /// <summary>
    /// 信息
    /// </summary>
    /// <param name="Message"></param>
    /// <param name="ex"></param>
    public static void LogInfo(string Message, Exception ex)
    {
        if (!log.IsInfoEnabled)
            SetConfig();
        log.Info(Message, ex);
    }

    /// <summary>
    /// 错误日志
    /// </summary>
    /// <param name="Message"></param>
    public static void ErrorInfo(string Message)
    {
        if (!log.IsErrorEnabled)
            SetConfig();
        log.Error(Message);
    }

    /// <summary>
    /// 错误日志
    /// </summary>
    /// <param name="Message"></param>
    /// <param name="ex"></param>
    public static void Error(Exception ex)
    {
        if (!log.IsInfoEnabled)
            SetConfig();
        log.Error("异常", ex);
    }

    /// <summary>
    /// 错误日志
    /// </summary>
    /// <param name="Message"></param>
    /// <param name="ex"></param>
    public static void ErrorInfo(string Message, Exception ex)
    {
        if (!log.IsErrorEnabled)
            SetConfig();
        log.Error(Message, ex);
    }

    /// <summary>
    /// Debug日志
    /// </summary>
    /// <param name="Message"></param>
    public static void DebugInfo(string Message)
    {
        if (!log.IsDebugEnabled)
            SetConfig();
        log.Debug(Message);
    }

    /// <summary>
    /// Debug日志
    /// </summary>
    /// <param name="Message"></param>
    /// <param name="ex"></param>
    public static void DebugInfo(string Message, Exception ex)
    {
        if (!log.IsDebugEnabled)
            SetConfig();
        log.Debug(Message, ex);
    }

    /// <summary>
    /// 警告
    /// </summary>
    /// <param name="Message"></param>
    public static void WarnInfo(string Message)
    {
        if (!log.IsWarnEnabled)
            SetConfig();
        log.Warn(Message);
    }

    /// <summary>
    /// 警告
    /// </summary>
    /// <param name="Message"></param>
    /// <param name="ex"></param>
    public static void WarnInfo(string Message,Exception ex)
    {
        if (!log.IsWarnEnabled)
            SetConfig();
        log.Warn(Message,ex);
        
    }

    /// <summary>
    /// 致命错误
    /// </summary>
    /// <param name="Message"></param>
    public static void FataInfo(string Message)
    {
        if (!log.IsFatalEnabled)
            SetConfig();
        log.Fatal(Message);
    }

    /// <summary>
    /// 致命错误
    /// </summary>
    /// <param name="Message"></param>
    /// <param name="ex"></param>
    public static void FataInfo(string Message,Exception ex)
    {
        if (!log.IsFatalEnabled)
            SetConfig();
        log.Fatal(Message, ex);
    }

该类下需要通过NuGet安装下面几个扩展:

  1. Microsoft.Extensions.Logging

  2. Microsoft.Extensions.Logging.Log4Net.AspNetCore

  3. log4net

之后log4net.config配置文件内容:

<log4net>
    <appender name="Console" type="log4net.Appender.ConsoleAppender">
        <layout type="log4net.Layout.PatternLayout">
            <!-- Pattern to output the caller's file name and line number -->
            <conversionPattern value="%5level [%thread] - STACK: %exception{stacktrace} - MESSAGE: %message%newline" />
        </layout>
    </appender>

    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
        <!--<file value="Log4Net" />
        <appendToFile value="true" />
    <rollingStyle value="Date" />
    <datePattern value="yyyyMMdd&quot;.log&quot;" />
        <maximumFileSize value="100KB" />
        <maxSizeRollBackups value="2" />-->
    <param name="File" value="logfile/" />
    <param name="AppendToFile" value="true" />
    <param name="RollingStyle" value="Date" />
    <param name="DatePattern" value="yyyyMMdd&quot;.log&quot;" />
    <param name="StaticLogFileName" value="false" />

        <layout type="log4net.Layout.PatternLayout">
            <!--<conversionPattern value="%level %thread - STACK: %exception{stacktrace} - MESSAGE: %message%newline" />-->
      <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
        </layout>
    </appender>

    <root>
        <level value="ALL" />
        <appender-ref ref="Console" />
        <appender-ref ref="RollingFile" />
    </root>
</log4net>

最后,修改Startup.cs文件的Configure方法,增加新参数如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

在Configure方法中,添加:

loggerFactory.AddLog4Net();

完整如下:

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            loggerFactory.AddLog4Net();

            app.UseStaticFiles();

            app.UseMvc();
        }

测试:
我们写个测试的来看看,在Program类的静态方法中,添加段代码测试一下:

        public static void Main(string[] args)
        {
            Logger.Log4Net.LogInfo("test测试");

            try
            {
                int a = 5;
                int b = 1 / (a - 5);
            }
            catch (Exception e)
            {
                Logger.Log4Net.ErrorInfo("异常信息", e);
            }
            BuildWebHost(args).Run();
        }

运行之后,我们会在根目录下的logfile文件夹中,看到出现了一个日志文件:20180513.log
打开,内容如下:

2018-05-13 22:43:12,039 [1] INFO  Biz126.Logger.Log4Net - test测试
2018-05-13 22:43:12,930 [1] ERROR Biz126.Logger.Log4Net - 异常信息
System.DivideByZeroException: Attempted to divide by zero.
   at Biz126.WebAPI.Program.Main(String[] args) in D:\Items\Log测试\Biz126.WebAPI\Program.cs:line 23
2018-05-13 22:43:18,274 [5] INFO  Microsoft.AspNetCore.Hosting.Internal.WebHost - Request starting HTTP/1.1 GET http://localhost:65176/api/values  
2018-05-13 22:43:18,753 [5] INFO  Microsoft.AspNetCore.Hosting.Internal.WebHost - Request finished in 479.9045ms 404 

符合预期,以上。

分类

最新文章

最近回复

  • 老徐: 已经加上了,抱歉才看到
  • 青山: 某种原因,暂停友链,抱歉。
  • 搬瓦工: 朋友 交换链接吗
  • 飞刀说: 名称:飞刀说 描述:...
  • 青山: 计划搬迁到腾讯云,正...
  • 河边的飞刀: 网站名称:飞刀说 网...
  • 老徐: 具体要哪个呢?
  • 老徐: 是不是有点老?
  • 青山: 哇,林志炫
  • 老白: 哇,这改的可以,能不...

归档

标签云

C# .net core asp.net 情感 SQL mongodb sql server EasyUI 安全 激活 linux 身份验证 https typecho .net sql注入 kms MVC IIS 高并发 IE 坑爹 服务器 mysql Oracle Combobox Datagrid 口语 数据抓取

其它