基于ASP.NET Core 3.x的端点路由(Endpoint Routing)实现控制器(Controller)和操作(Action)分离的接口服务
本文首发于 码友网 -- 《基于ASP.NET Core 3.x的端点路由(Endpoint Routing)实现控制器(Controller)和操作(Action)分离的接口服务》
前言
如题,今天为大家分享一种基于ASP.NET Core 3.x的端点路由(Endpoint Routing)实现控制器(Controller)和操作(Action)分离的接口服务方案。
为什么写这篇文章?为什么控制器(Controller)和操作(Action)分离?这来源由Github上的一个开源ASP.NET Core项目--Ardalis.ApiEndpoints,其中的Readme中描述了为什么要控制器和操作分离,为什么有ApiEndpoints这个项目的出现,引用并总结如下:
常规的MVC模式本质上是一种反模式,这种模式集合了许多但从不相互调用的方法,并且很少在相同的状态下操作。随着项目的发展,一个控制器会变得越来越臃肿,甚至可能无法控制。当你需要创建一个不同类型的接口服务的时候,还得首先创建相应的控制器,无法做到业务逻辑分开处理等等问题。
其实,在常规的MVC或者Web API应用程序中,许多开发者也许已经意识到了这种问题的存在,但仍然没有更好的办法来组织,拆分和管理这些控制器和操作,所以就出现了Ardalis.ApiEndpoints这个项目。
Ardalis.ApiEndpoints简介
如上所述,Ardalis.ApiEndpoints是为了解决分离控制器(Controller)类和操作(Action)服务的解决方案。有了它,你可以按照不同的业务来分开组织并管理服务接口端点,甚至可以为不同服务创建独立的文件夹,就像ASP.NET Razor Pages的项目结构类似,而不同把所有服务放到一个控制器中。下面我们就使用Ardalis.ApiEndpoints来创建一个示例。
Ardalis.ApiEndpoints示例
1.首先,我们创建一个ASP.NET Core 3.x 的Web项目,命名为:EndpointDemo,然后使用Nuget安装Ardalis.ApiEndpoints
2.创建一个路径为[Endpoints/v1/Student/]的文件目录,在此目录中创建一个继承至BaseEndpoint<TRequest, TResponse>的类GetById.cs,其中的TRequest表示接口的请求参数实体类,TResponse表示接口的返回实体类。
3.在GetById.cs类中实现抽象类中的Handle()方法。
4.标记Handle()方法的HTTP请求类型,如:HttpGet,HttpPost...
5.定义返回实体类TResponse,示例中的类名为StudentResponse.cs
代码如下:
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Mvc;
namespace EndpointDemo.Endpoints.v1.Students
{
/// <summary>
/// 获取指定ID的学生信息
/// </summary>
public class GetById : BaseEndpoint<int, StudentResponse>
{
/// <summary>
/// 获取指定ID的学生信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet, Route("api/v1/student/{id:int}")]
public override ActionResult<StudentResponse> Handle(int id)
{
var response = new StudentResponse
{
Id = id,
Name = "Rector"
};
return Ok(response);
}
}
}
StudentResponse.cs
namespace EndpointDemo.Endpoints.v1.Students
{
/// <summary>
/// 返回的学生信息响应实体类
/// </summary>
public class StudentResponse
{
/// <summary>
/// ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
}
}
以上就完成了一个基于ASP.NET Core 3.x的端点服务接口,这里我们并没有创建任何控制器,请求地址为:http://localhost:12345/api/v1/student/{id:int}
Startup.cs文件中需要注册控制器的服务,如:
services.AddControllers();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
以下我们来集成Swagger接口文档,还是使用Nuget安装Swashbuckle.AspNetCore.Annotations,然后在Startup.cs文件中配置Swagger(同时配置了Swagger的权限访问),如下:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System;
using System.IO;
using System.Text;
namespace EndPointDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
c.EnableAnnotations();
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme (Example: 'Bearer 12345abcdef')",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
var filePath = Path.Combine(AppContext.BaseDirectory, "EndpointDemo.xml");
c.IncludeXmlComments(filePath);
});
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtToken:Issuer"],
ValidAudience = Configuration["JwtToken:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtToken:SecretKey"]))
};
});
services.AddControllers();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"));
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
}
修改appsettings.json文件,如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"JwtToken": {
"SecretKey": "SecretKeywqewqeqqqqqqqqqqqweeeeeeeeeeeeeeeeeee",
"Issuer": "http://localhost:56369/"
}
}
接下来,我们使用SwaggerOperation来丰富接口文档的注释,修改GetById.cs文件如下:
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace EndpointDemo.Endpoints.v1.Students
{
/// <summary>
/// 获取指定ID的学生信息
/// </summary>
public class GetById : BaseEndpoint<int, StudentResponse>
{
/// <summary>
/// 获取指定ID的学生信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Authorize]
[HttpGet, Route("api/v1/student/{id:int}")]
[SwaggerOperation(
Summary = "获取指定ID的学生信息",
Description = "获取指定ID的学生信息",
OperationId = "Student.GetById",
Tags = new[] { "StudentEndpoint" }
)]
public override ActionResult<StudentResponse> Handle(int id)
{
var response = new StudentResponse
{
Id = id,
Name = "Rector"
};
return Ok(response);
}
}
}
同时,我还创建了一个Create.cs文件,用来演示[HttpPost]请求,如下:
using System;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace EndpointDemo.Endpoints.v1.Students
{
/// <summary>
/// 创建新的学生记录
/// </summary>
public class Create : BaseEndpoint<NewStudentRequest, StudentResponse>
{
/// <summary>
/// 创建新的学生记录
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost, Route("api/v1/student/create")]
[SwaggerOperation(
Summary = "创建新的学生记录",
Description = "创建新的学生记录",
OperationId = "Student.Create",
Tags = new[] { "StudentEndpoint" }
)]
public override ActionResult<StudentResponse> Handle(NewStudentRequest request)
{
var response = new StudentResponse
{
Name = request.Name,
Id = new Random().Next(1, 100)
};
return Ok(response);
}
}
}
NewStudentRequest.cs
using System.ComponentModel.DataAnnotations;
namespace EndpointDemo.Endpoints.v1.Students
{
/// <summary>
/// 创建学生的实体类
/// </summary>
public class NewStudentRequest
{
/// <summary>
/// 姓名
/// </summary>
[Required]
public string Name { get; set; }
}
}
创建用于用户授权的目录v1/Auth,并创建获取令牌的类GrantToken.cs,代码如下:
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Mvc;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Swashbuckle.AspNetCore.Annotations;
namespace EndpointDemo.Endpoints.v1.Auth
{
/// <summary>
///
/// </summary>
public class GrantToken : BaseEndpoint<AuthInfoRequest, TokenResponse>
{
private readonly IConfiguration _config;
public GrantToken(IConfiguration config)
{
_config = config;
}
[SwaggerOperation(
Summary = "用户登录",
Description = "用户登录",
OperationId = "Auth.GrantToken",
Tags = new[] { "AuthEndpoint" }
)]
[AllowAnonymous]
[HttpPost, Route("api/v1/auth/grant_token")]
public override ActionResult<TokenResponse> Handle(AuthInfoRequest request)
{
if (request == null) return Unauthorized();
var validUser = Authenticate(request);
var token = "";
if (validUser)
{
token = BuildToken();
}
else
{
return Unauthorized();
}
var response = new TokenResponse
{
Token = token
};
return Ok(response);
}
private string BuildToken()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtToken:SecretKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["JwtToken:Issuer"],
_config["JwtToken:Issuer"],
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private bool Authenticate(AuthInfoRequest login)
{
var validUser = login.Username == "admin" && login.Password == "123456";
return validUser;
}
}
}
运行项目,打开地址:http://localhost:56369/swagger 如果运行成功,你将看到如下界面:

这时,如果你直接点击【获取指定ID的学生信息】,接口返回的是401错误,如图:

因为我们还未对接口访问进行授权,那么我们需要先请求授权接口:/api/v1/auth/grant_token,以获取用户令牌,如下:

将获取到的令牌填入授权窗口中,如下:


最后,再请求【获取指定ID的学生信息】,得到正确的接口返回内容,如下:

项目结构如下:

本文为你分享的Ardalis.ApiEndpoints内容就到这里,使用Ardalis.ApiEndpoints,你可在不用创建控制器的场景下任意地组织和管理你的接口服务端点。感谢你的阅读!
本文示例源码托管地址请至原文获取:《基于ASP.NET Core 3.x的端点路由(Endpoint Routing)实现控制器(Controller)和操作(Action)分离的接口服务》
基于ASP.NET Core 3.x的端点路由(Endpoint Routing)实现控制器(Controller)和操作(Action)分离的接口服务的更多相关文章
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(1)
最近使用vscode比较多. 学习了一下如何在mac上使用vscode开发asp.netcore项目. 这里是我写的关于vscode的一篇文章: https://www.cnblogs.com/cgz ...
- 如何基于asp.net core的Identity框架在mysql上作身份验证处理
首先了解这个概念,我一开始也是理解和掌握基本的概念,再去做程序的开发.Identity框架是微软自己提供,基于.net core平台,可拓展.轻量 级.面向多个数据库的身份验证框架.IdentityS ...
- 基于Asp.Net Core的简单社区项目源代码开源
2019年3月27号 更新版本 本项目基于 ASP.NET CORE 3.0+EF CORE 3.0开发 使用vs2019 +sqlserver 2017(数据库脚本最低支持sql server 20 ...
- AServer - 基于Asp.net core Kestrel的超迷你http服务器
AServer是基于ASP.NET Core Kestrel封装的一个超迷你http服务器.它可以集成进你的Core程序里,用来快速的响应Http请求,而不需要集成整个ASP.NET Core MVC ...
- 基于ASP.NET Core 创建 Web API
使用 Visual Studio 创建项目. 文件->新建->项目,选择创建 ASP.NET Core Web 应用程序. 基于 ASP.NET Core 2.0 ,选择API,身份验证选 ...
- 基于ASP.Net Core开发的一套通用后台框架
基于ASP.Net Core开发一套通用后台框架 写在前面 这是本人在学习的过程中搭建学习的框架,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 知其然,知其所以然,并非重 ...
- 基于ASP.NET Core 3.0快速搭建Razor Pages Web应用
前言 虽然说学习新的开发框架是一项巨大的投资,但是作为一个开发人员,不断学习新的技术并快速上手是我们应该掌握的技能,甚至是一个.NET Framework开发人员,学习.NET Core 新框架可以更 ...
- [译]基于ASP.NET Core 3.0的ABP v0.21已发布
基于ASP.NET Core 3.0的ABP v0.21已发布 在微软发布仅仅一个小时后, 基于ASP.NET Core 3.0的ABP v0.21也紧跟着发布了. v0.21没有新功能.它只是升级到 ...
- 基于Asp.Net Core,利用ZXing来生成二维码的一般流程
本文主要介绍如何在.net环境下,基于Asp.Net Core,利用ZXing来生成二维码的一般操作.对二维码工作原理了解,详情见:https://blog.csdn.net/weixin_36191 ...
随机推荐
- Spine学习七 - spine动画资源+ Unity Mecanim动画系统
前面已经讲过 Spine自己动画状态机的动画融合,但是万一有哥们就是想要使用Unity的动画系统,那有没有办法呢?答案是肯定的,接下来,就说说如何实现: 1. 在project面板找打你导入的Spin ...
- Transform与Vector3 的API
Transform.InverseTransformDirection(Vector3 direction) Vector3.ProjectOnPlane(Vector3 vector, Vector ...
- NutUI 视频组件开发心得
引子 说到在项目中引入一个视频,我们肯定会想到 HTML5 为我们提供的 Video 标签,它为我们提供了许多属性和方法,使用起来很方便,当然直接使用也会遇到各种兼容问题,在最初学习 Video 标签 ...
- 8svg 自定义全局组件
0.https://www.npmjs.com/package/vue2-svg-icon 直接使用vue2-svg-icon插件 .如果不使用,就使用下面用法 注意:用阿里图标时候,最好都选择#ff ...
- myeclipse前端界面乱码
框起来的值默认的格式是ISO-8859-1,改为UTF-8
- String.format与搭配转化符的使用
String的format语法搭配转化符,在格式化输出方面效果特别好,值得掌握. 例程: System.out.println("----C1---|----C2---|----C3---| ...
- JavaScript浮点数及其运算
.普及两个函数Math.pow(底数,几次方)Number.toFixed(小数位数)2.浮点数相加function accAdd(arg1,arg2){ var r1,r2,m; try{r ...
- Navicat 闲置时间过长会卡死
前段时间使用navicat连接线上的数据库,Navicat 闲置时间过长会卡死.解决方案:选中数据库,右键点击 编辑连接,修改保持连接间隔为 20秒.非常 so easy ! 1. 选中数据库,右键点 ...
- Apache2.4 下载和安装 - Win10
Apache安装包已放入百度网盘,链接地址在本文最后 1.下载Windows版本的Apahce安装包 a. 访问官网,进入下载页面 https://www.apachelounge.com (apac ...
- jmeter远程调用
jmeter版本相同 JDK版本1.7以上 脚本文件在所有机器上的路径都一致 修改远程机器jmeter bin目录下的jmeter.properties文件配置:server_port=1001 多个 ...