目录
一 HTTP Error 400. The request hostname is invalid.
- 在当前项目的工程路径下,找到.vs\config 文件夹下面的applicationhost.config 这个文件。
-
在找到applicationhost.config 文件后,打开,在文件中找到site 那个节点,修改bindings 属性,如下图所示;这其实给在iis中编辑绑定是一样的意思。
-
具体修改的内容有几点注意的:
a. 如果用实际公网域名
<binding protocol="http" bindingInformation="*:9999:localhost" />
, localhost 直接换成公网域名即可,vs不需要使用管理员权限打开
b. 如果用自定义域名
localhost 替换为自定义域名,如果是管理员权限打开不需要编辑系统的hosts文件,如果是普通权限打开,需要编辑hosts文件;
我是win7系统,hosts文件在 C:\Windows\System32\drivers\etc 下面,是隐藏的,所以要从系统菜单-工具-文件夹选项-的显示中选择显示隐藏的文件夹。
我在修改的时候,安装的杀毒软件小红伞阻止修改host,选择允许就可以保存了,不然会提示有其他文件正在打开,不让保存。
c. 如果使用局域网ip
将localhost 换成具体的ip即可,注意一定要用管理员权限打开vs。不然还是不能的。我是需要使用局域网ip,在此处新增了一条binding属性,如下图所示:
- 修改完毕config后,重启下VS,重启的时候选择是否以管理员的身份运行
二 .NET Core CLI Interface
dotnet –info
:查看当前安装的版本。
指定项目使用的SDK版本
:
dotnet new global.json --sdk-version <SDK版本号>
,要注意的是最后的参数是SDK版本,不是.NET Core版本。
dotnet new
:用于创建一个新项目,关于项目类型,可以用命令:
dotnet new --help
查看。
dotnet restore
:恢复项目的依赖项和工具。
dotnet restore
命令使用 NuGet 还原 project.json 文件中被指定的依赖项,以及特定于项目的工具。默认情况下,依赖项和工具的还原是并行完成的。对于依赖项,可以在还原操作时使用–packages参数指定还原包的位置。如果没有指定,则默认使用NuGet包缓存。对于特定项目的工具,dotnet restore 首先还原该工具包,然后继续还原在项目文件中指定的工具依赖项。
dotnet build
#创建MVC工程
[zebra Desktop]$mkdir work && cd work
[zebra work]$dotnet new console -n Zebra.Liu.Console
#添加Nuget包,这是添加了mysql的驱动包
[zebra work]$cd Zebra.Liu.Console/
[zebra Zebra.Liu.Console]$dotnet add package Pomelo.EntityFrameworkCore.MySql
[zebra Zebra.Liu.Console]$ls
Program.cs Zebra.Liu.Console.csproj obj
#编译工程,输出到build文件夹中
[zebra Zebra.Liu.Console]$dotnet build -o build/
[zebra Zebra.Liu.Console]$ls
Program.cs Zebra.Liu.Console.csproj build obj
#多了一个build文件夹,用于存放编译后的文件
[zebra Zebra.Liu.Console]$ls build/
Zebra.Liu.Console.dll #二进制文件(中间语言IL)
Zebra.Liu.Console.pdb #symbol文件,用于调试
Zebra.Liu.Console.deps.json # *.deps.json中列出应用程序的依赖项
Zebra.Liu.Console.runtimeconfig.dev.json
Zebra.Liu.Console.runtimeconfig.json # 指定共享运行时以其版本
上面的试验可以看出build命令最后的产物,如果有项目使用了第三方的依赖,如像上面一样使用了NuGet中的MySQL驱动,那么在执行build命令时,只会从本机的NuGet缓存库中解析依赖项,也就是说,生成的dll文件,不包含MySQL的驱动。这样build的dll文件,从本机拷贝到另外的机器上,可能不能运行。如果要将依赖项也打包,可以使用另一个命令dotnet publish。
publish命令和build命令最大的区别在于:publish命令将应用程序的依赖项,从NuGet缓存复制到输出文件夹中。
build后的程序是否为可执行,主要看.csproj文件中的选项
<!-- .csproj文件内容 -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!--
OutputType 输出类型设置为Exe,说明这个工程,可执行
可执行的dll和不可执行的dll文件最大的区别在于,有没有入口点以及是否可执行
-->
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
</Project>
dotnet watch run
dotnet watch 是一个开发阶段在源文件发生变动的情况下使用 dotnet 命令的工具。 当代码发生变动的时候可以用来执行编译,运行测试,或者发布操作。
dotnet clean
清除上一个生成的输出,包括中间文件夹(obj)和最终输出文件夹(bin)中的文件
dotnet sln
使用dotnet sln命令,可以在*.sln,文件中添加,删除,获取项目。
dotnet sln [<solution_name>] add <project> <project>
dotnet sln [<solution_name>] add <globbing_pattern>
dotnet sln [<solution_name>] remove <project> <project>
dotnet sln [<solution_name>] remove <globbing_pattern>
dotnet sln [<solution_name>] list
注意:dotnet sln命令只会对solution_name.sln文件中操作,要生成*.sln文件,可以使用dotnet new sln命令
dotnet add/remove/list reference
管理项目之间的引用关系
# 项目1引用项目2
[zebra work]$dotnet add 项目1 reference 项目2
三 MSBuild
MSBuild 是 Microsoft Build Engine 的缩写,MSBuild 是 Visual Studio 的生成系统。我们在VS中使用“生成解决方案”的背后就是依赖MSBuild来实现,但我们完全可以在cmd中使用MSBuild来完成应用的构建。
它不仅仅是一个构造工具,应该称之为拥有相当强大扩展能力的自动化平台。MSBuild平台的主要涉及到三部分:执行引擎、构造工程、任务。其中最核心的就是执行引擎,它包括定义构造工程的规范,解释构造工程,执行“构造动作”;构造工程是用来描述构造任务的,大多数情况下我们使用MSBuild就是遵循规范,编写一个构造工程;MSBuild引擎执行的每一个“构造动作”就是通过任务实现的,任务就是MSBuild的扩展机制,通过编写新的任务就能够不断扩充MSBuild的执行能力。所以这三部分分别代表了引擎、脚本和扩展能力。
MSBuild 引入了一种新的基于 XML 的项目文件格式,这种格式容易理解、易于扩展并且完全受 Microsoft 支持。MSBuild 项目文件的格式使开发人员能够充分描述哪些项需要生成,以及如何利用不同的平台和配置生成这些项。另外,项目文件的格式还使开发人员能够创作可重用的生成规则,这些规则可以分解到不同的文件中,以便可以在产品内的不同项目之间一致地执行生成。以下各节描述了 MSBuild 项目文件格式的一些基本要素。
四 发布
1 ASP.NET Core Web Api发布到Linux(CentOS 7 64)
在Linux上安装需要的环境,然后将上面成功运行的项目发布:
将文件夹拷贝到linux下,进入文件夹运行:
dotnet WebApplication1.dll
。
2 linux下ASP.Net core打包docker镜像
在上面发布的core2.2版本文件夹下面创建名称为
Dockerfile
的文件,内容:
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
EXPOSE 8080
WORKDIR /app
COPY . /app
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
对于core3版本:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
COPY . /app
ENTRYPOINT ["dotnet", "demoapi.dll"]
FROM是指定该镜像的运行环境;然后指定容器映射端口8080;最后dotnet运行项目。
把目录文件通过dockerfile加载到docker镜像:
docker build -t gateway:1.0 .
。
调用命令 docker images 会发现已经有这个镜像了。
最后,运行镜像:
docker run -p 8080:8080 testsite:1.0
(8080:8080表示的是容器内端口映射容器外端口)。
3 vs2019创建docker支持
选中”启用Docker支持“,这样可以自动生成Dockerfile文件,这个文件是用来生成Docker镜像;如果没有选中”启用Docker支持“,也可在项目生成后,右击项目-添加-Docke支持;
选择Docker运行系统,代码编写阶段Dockerfile文件可以随意修改和删除,使用Docker调试或生成镜像时重新添加一个就行了。
生成了Dockerfile文件。生成的内容一大堆,在不修改情况下使用VS自带的Docker运行没有问题,但部署时却有麻烦,简化一下就行了;
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
COPY . .
ENTRYPOINT ["dotnet", "SamruoFrame.Service.Cache.Redis.dll"]
Dockerfile文件默认在发布时不会复制到发布文件夹中,属性修改为:始终复制。
4 编辑程序并生成镜像
程序以正常发布生成,使用”文件夹”模式,以上几个发布方式需要Azure,本文不涉及此内容(主要是没钱)
生成镜像有多种方式,我使用本地生成;进入发布目录,运行Powershell,使用命令行生成镜像
docker build -t samruo/redis:1.1 .
//语法:docker build [OPTIONS] PATH | URL | -
//--tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
//需要注意的是 1:name不能用大写;2:语句最后必须加“ .” 且前面要有一个空格。
这里出现了一个错误信息:
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have ‘-rwxr-xr-x’ permissions. It is recommended to double check and reset permissions for sensitive files and directories.
经查是因为我在Windows下生成,但将使用在Linux下,所以有个目录权限问题。考虑到我的程序只用来做服务,且采用集群,不会在本地保存文件,理论上不会有此问题,就先使用这个方法。
5 将镜像放到阿里仓库
为方便集群化部署,以及后继运维方便,将镜像放到仓库中是个好办法。实现Docker仓库的方法有很多,国内和国外都的,Docker Hub速度太慢,我使用阿里的镜像仓库,因为本身服务器是在阿里云。
首先要有个阿里云帐号;登录后进入控制台,搜索”容器镜像服务“,首次使用需要开通服务,根据阿里云的使用经验,貌似使用容器镜像服务免费,还有个”弹性容器实例ECI“是需要收费的,其实就是个Docker,按秒计费的。
第一步:创建命名空间
第二步:创建镜像仓库
仓库创建后,点击仓库名称进入详情页
6 上传镜像和版本更新
1、 登录阿里云Docker Registry
sudo docker login --username=【这里是阿里云登录帐号】 registry.cn-hangzhou.aliyuncs.com
2、将镜像推送到Registry
sudo docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/samruo_docker/redis:[镜像版本号]
sudo docker push registry.cn-hangzhou.aliyuncs.com/samruo_docker/redis:[镜像版本号]
正确执行命令后,镜像将出现在”镜像版本“之中;
版本更新只需要修改命令行后面的”镜像版本号“即可,非常简单。到此,镜像仓库部署完成。
7 在Linux的Docker中使用镜像
建立了镜像后在部署操作将非常简单。执行以下命令前需要执行登录操作,见上节上传镜像,如已登录则无需执行。
//从Registry中拉取镜像,在一台服务器中,同一个版本的镜像只需要拉取一次即可
docker pull registry.cn-hangzhou.aliyuncs.com/samruo_docker/redis:[镜像版本号]
//部署镜像
//语法:docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run --name samruo-redis-1 -d -p 5902:80 registry.cn-hangzhou.aliyuncs.com/samruo_docker/redis:1.1
// --name="nginx-lb": 为容器指定一个名称;
// -d: 后台运行容器,并返回容器ID;
// -p: 指定端口映射,格式为:主机(宿主)端口:容器端口;这里和80端口与Dockerfile文件中的EXPOSE 80对应;
在同一台宿主服务器上只要”主机(宿主)端口“不重复,可以部署多个同一镜像,不会相互干扰,这也将用于集群化需求。成功执行后将只返回容器ID;
查看镜像部署情况:
docker ps -a
。
8 使用VS2019发布镜像到Docker仓库
1.Dockerfile完全不需要修改,可保持自动创建时的内容.EXPOSE可根据开放端口不同,自行调整.
2.使用”发布”->“容器注册表”:首次发布需要创建发布模板,如下:
在下图中填写阿里容器仓库地址:
格式为:https://服务区域地址/命名空间 (具体见自己开通仓库的公网地址)
注意不要填写仓库名称,仓库名称VS将自动补充,也就是项目名称,这也是唯一于手工上传相比不可控之处.
填写用户名和密码,好像没什么用,最好在上传时,以代码登录一下.见以下内容.
保存后分发镜像模板就完成了,以后只要点击发布就能自动上传到私有仓库中.
每次发布镜像最好区分版本号,见上图”图像标记”(话说这翻译也是够直接),修改上图所未标记后,点击”发布”即可.
VS自动创建的Dockerfile文件有20行,发布需要点时间,这时可以打开PowerShell 运行 登录阿里云Docker Registry命令:
$ sudo docker login --username=[阿里帐号] registry.cn-hangzhou.aliyuncs.com
。
我这里执行手动登录的原因,是遇到发布时提示帐号验证错误,应该是VS调用本地Docker Desktop来执行上传,但Docker Desktop本身未登录,所以会执行出错.
好了,现在每次发布新的版本只需要点一下”发布”并修改一下版本号,就可以方便的更新程序镜像了。
9 串口映射到docker
将物理机的串口映射到容器中去:
docker run --rm -it --device /dev/ttyS1:/dev/ttyS0 ubuntu:14.04 /bin/sh
将物理机的串口映射到Web镜像容器中去:
docker run --rm -it --device /dev/ttyS1:/dev/ttyS0 -p 9600:9694 gateway:1.0
10 外部访问
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseUrls("http://*:5000") // 所有IP
.UseStartup<Startup>();
});
关闭防火墙。
五 Centos环境配置
1 centos7的virbr0问题
ifconfig virbr0 down
brctl delbr virbr0
systemctl disable libvirtd.service
2 net模式下自动获取IP地址
cd /etc/sysconfig/network-scripts
vim ifcfg-ens33
3 解决虚拟机复制粘贴不能用的问题
报错信息如下:
Error when getting information for file “//tmp/VMwareDnD/p6v6B6/.”: No such file or directory
;
1、卸载预装的“open-vm-tools”包,检测是否预装了
open-vm-tools
:
yum list installed | grep open-vm-*
;
2、卸载预装的程序包:
yum remove open-vm-tools
;
3、再次确认是否卸载成功:
rpm -qa | grep open-vm-*
;
4、重启虚拟机:
reboot
;
5、安装 VMware Tools:
sudo ./vmware-install.pl
,前置条件检测时,因为我们之前卸载了预装的open-vm-tools依赖包,所以这里会提示不继续安装,不要管它,输入“yes”继续执行安装,接下来的选项基本都是默认一路回车即可。
6、open-vm-tools:
yum install open-vm-tools -y
;
7、重启。
4 安装包配置
离线情况下,直接使用rpm包;
sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm
5 安装 SDK
[root@test src]#mkdir -p /usr/local/dotnetcore
[root@test src]#tar -zxf dotnet-sdk-6.0.201-linux-x64.tar.gz -C /usr/local/dotnetcore
[root@test src]# vi /etc/profile
增加以下几行
#set dotnet core
export DOTNET_ROOT=/usr/local/dotnetcore
export PATH=$PATH:$DOTNET_ROOT
#生效环境变量
[root@test src]#source /etc/profile
六 get和post接口
1 Get参数接受
1、基础类型接收参数
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public string Get(string name, string gender, int age)
{
return $"姓名:{name}\n性别:{gender}\n年龄:{age}";
}
}
}
2、实体类型接收参数
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public string Get([FromQuery] Person person)
{
return $"姓名:{person.Name}\n性别:{person.Gender}\n年龄:{person.Age}";
}
}
}
2 post方式可以直接括号内接受参数
在.NetCore WebApi中,Post方式的api接收参数时不能像Get那些,直接把接受参数写到方法后边括号内,如:
[HttpPost]
public string GetAreaGroupList(string id){
return id;
}
这个是因为api默认推理规则,绑定源推理,借助这些规则,无需通过将属性应用于操作参数来手动识别绑定源,其中几种规则为
[FromBody]针对复杂类型参数进行推断。但其不适用于具有特殊含义的任何复杂的内置类型,如IFormCollection和CancellationToken。
[FromForm]针对IFormFile和IFormFileCollection类型的操作参数进行推断。
[FromRoute]针对与路由模板中的参数相匹配的任何操作参数名称进行推断。
[FromQuery]针对任何其他操作参数进行推断。
但是有时不想这么复杂编写api,只想在括号内一个一个写接受参数,那就把默认推理规则关掉就好了。
.NetCore中禁用api推理规则,在Startup.cs ConfigureServices中这样写:
services.AddControllers(cfg => { cfg.Filters.Add(new ExceptionHandleAttribute()); })
.ConfigureApiBehaviorOptions(options =>
{
//options.SuppressConsumesConstraintForFormFileParameters = true;//禁用当[FromForm]属性批注时,推理multipart/form-data请求内容类型
options.SuppressInferBindingSourcesForParameters = true;//禁用api的推理规则,这样就支持post方式直接括号接受参数和model参数方式
});
3 获取请求body
https://blog.csdn.net/WuLex/article/details/122249541
4 参数传递接受
https://blog.csdn.net/qq_18932003/article/details/119967284
5 控制器与方法返回输出
asp.net core有三种返回 数据 和 HTTP状态码 的方式,最简单的就是直接返回指定的类型实例,如下:
public IEnumerable<WeatherForecast> Get()
{
}
除了这种,也可以返回
IActionResult
实例和
ActionResult
实例。
返回指定的类型只能返回数据,附带不了http状态码,而 IActionResult 实例可以将数据 + Http状态码 一同带给前端,最后就是 ActionResult 它封装了前面两者,可以实现两种模式的自由切换。
在 Action 中返回 IActionResult 实例
如果你要返回 data + httpcode 的双重需求,那么 IActionResult 就是你要找的东西,下面的代码片段展示了如何去实现。
[HttpGet]
public IActionResult Get()
{
if (authors == null)
return NotFound("No records");
return Ok(authors);
}
上面的代码有 Ok,NotFound 两个方法,对应着 OKResult,NotFoundResult, Http Code 对应着 200,404。当然还有其他的如:CreatedResult, NoContentResult, BadRequestResult, UnauthorizedResult, 和 UnsupportedMediaTypeResult,都是 IActionResult 的子类。
在 Action 中返回 ActionResult 实例
ActionResult 是在 ASP.NET Core 2.1 中被引入的,它的作用就是包装了前面这种模式,怎么理解呢? 就是即可以返回 IActionResult ,也可以返回指定类型,从 ActionResult 类下的两个构造函数中就可以看的出来。
public sealed class ActionResult<TValue> : IConvertToActionResult
{
public ActionResult Result {get;}
public TValue Value {get;}
public ActionResult(TValue value)
{
if (typeof(IActionResult).IsAssignableFrom(typeof(TValue)))
{
throw new ArgumentException(Resources.FormatInvalidTypeTForActionResultOfT(typeof(TValue), "ActionResult<T>"));
}
Value = value;
}
public ActionResult(ActionResult result)
{
if (typeof(IActionResult).IsAssignableFrom(typeof(TValue)))
{
throw new ArgumentException(Resources.FormatInvalidTypeTForActionResultOfT(typeof(TValue), "ActionResult<T>"));
}
Result = (result ?? throw new ArgumentNullException("result"));
}
}
有了这个基础,接下来看看如何在 Action 方法中去接这两种类型。
[HttpGet]
public ActionResult<IEnumerable<Author>> Get()
{
if (authors == null)
return NotFound("No records");
return authors;
}
和文章之前的 Get 方法相比,这里直接返回 authors 而不需要再用 OK(authors) 包装,是不是一个非常好的简化呢? 接下来再把 Get 方法异步化,首先考虑下面返回 authors 集合的异步方法。
private async Task<List<Author>> GetAuthors()
{
await Task.Delay(100).ConfigureAwait(false);
return authors;
}
值得注意的是,异步方法必须要有至少一个 await 语句,如果不这样做的话,编译器会提示一个警告错误,告知你这个方法将会被 同步执行,为了避免出现这种尴尬,我在 Task.Delay 上做了一个 await。
下面就是更新后的 Get 方法,注意一下这里我用了 await 去调用刚才创建的异步方法,代码参考如下
[HttpGet]
public async Task<ActionResult<IEnumerable<Author>>> Get()
{
var data = await GetAuthors();
if (data == null)
return NotFound("No record");
return data;
}
如果你有一些定制化需求,可以实现一个自定义的 ActionResult 类,做法就是实现 IActionResult 中的 ExecuteResultAsync 方法即可。
https://www.cnblogs.com/jcsoft/p/12222527.html
https://blog.csdn.net/wojiuguowei/article/details/123968155
6 路由模板