4.创建并调用自动控制器(api)
创建应用程序服务后, 通常需要创建API控制器以将此服务公开为HTTP(REST)API端点. 典型的API控制器除了将方法调用重定向到应用程序服务并使用[HttpGet],[HttpPost],[Route]等属性配置REST API之外什么都不做.
ABP可以按照惯例 自动 将你的应用程序服务配置为API控制器. 大多数时候你不关心它的详细配置,但它可以完全被自定义.
ABP在确定服务方法的HTTP Method时使用命名约定:
- Get: 如果方法名称以
GetList,GetAll或Get开头.- Put: 如果方法名称以
Put或Update开头.- Delete: 如果方法名称以
Delete或Remove开头.- Post: 如果方法名称以
Create,Add,Insert或Post开头.- Patch: 如果方法名称以
Patch开头.- 其他情况, Post 为 默认方式.
如果需要为特定方法自定义HTTP Method, 则可以使用标准ASP.NET Core的属性([HttpPost], [HttpGet], [HttpPut]... 等等.). 这需要添加Microsoft.AspNetCore.Mvc.Core的Nuget包.
本篇使用的类库
*****.Application :编写自动控制器service的地方
*****.HttpApi.Host:启动项目所有控制器存放地。指向swagger
下边我们使用自动api控制器(service)创建几个api,然后在host中去调用
官方文档地址:https://docs.abp.io/zh-Hans/abp/latest/API/Auto-API-Controllers
上一篇文章我们已经建好了表,现在我们在.Application创建自动控制器
1.在.Application类库里创建service

using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Repositories;
namespace Test1111.Books
{
[RemoteService(IsEnabled = false)]//此注释是将本service的api禁用,因为自动api所有方法名一样只有请求方式不一样不易区分,我们改用host调用service的方法
public class BookAppService :
CrudAppService<
Book, //The Book entity
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto> //Used to create/update a book
//IBookAppService //implement the IBookAppService
{
private readonly IDistributedCache<BookDto> _cache;
public BookAppService(IRepository<Book, Guid> repository, IDistributedCache<BookDto> cache)
: base(repository)
{
_cache = cache;
}
public async Task<BookDto> GetBookAsync(Guid bookId)
{
return await _cache.GetOrAddAsync(
bookId.ToString(), //Cache key
async () => await GetBookFromDatabaseAsync(bookId),
() => new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.Now.AddHours(1)
}
);
}
private async Task<BookDto> GetBookFromDatabaseAsync(Guid bookId)
{
return await this.GetAsync(bookId);
}
}
}
2.在.HttpApi.Host中创建自定义控制器


using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Test1111.Books;
using Volo.Abp.AspNetCore.Mvc;
using static Test1111.QuickDb.CurrentHttpContext;
namespace Test1111.Functions.Books
{
[Route("api/[controller]/[action]")]
[Authorize]
public class BooksController : AbpController
{
private BookAppService _bookAppService;
public BooksController(BookAppService bookAppService)
{
_bookAppService = bookAppService;
}
/// <summary>
/// 查询 本查询为自定义方式 使用Dapper框架,一些复杂的查询我们就用Dapper不使用自动控制器了,
/// 其实下边的几个增删改查我们完全也可以不适用自动控制器,本篇文章只是介绍一下新特性自动控制器
/// </summary>
/// <returns></returns>
[HttpGet]
public dynamic GetAll()
{
return Db.Query<dynamic>("select * from AppBooks");
//使用dapper需要安装 包包管理工具搜索Dapper
}
/// <summary>
/// 查询
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet]
public async Task<BookDto> Get(Guid id)
{
return await _bookAppService.GetBookAsync(id);
}
/// <summary>
/// 创建
/// </summary>
/// <param name="createUpdateBookDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<BookDto> Create([FromBody] CreateUpdateBookDto createUpdateBookDto)
{
return await _bookAppService.CreateAsync(createUpdateBookDto);
}
/// <summary>
/// 修改
/// </summary>
/// <param name="id"></param>
/// <param name="createUpdateBookDto"></param>
/// <returns></returns>
[HttpPut]
public async Task<BookDto> Update(Guid id, [FromBody] CreateUpdateBookDto createUpdateBookDto)
{
return await _bookAppService.UpdateAsync(id, createUpdateBookDto);
}
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
public async Task Delete(Guid id)
{
await _bookAppService.DeleteAsync(id);
}
}
}
3.在.HttpApi.Host创建与数据库链接类

using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Test1111.QuickDb
{
public static class CurrentHttpContext
{
/// <summary>
/// Shortcut to HttpContext.Current.
/// </summary>
public static HttpContext Context => MyHttpContext.Current;
/// <summary>
/// Shortcut to HttpContext.Current.Request.
/// </summary>
public static HttpRequest Request => Context.Request;
/// <summary>
/// Gets the single data context for this current request.
/// </summary>
public static DbConnection Db
{
get
{
DbConnection dbConnection = null;
if (Context != null)
{
dbConnection = Context.Items["DB"] as DbConnection;
}
if (dbConnection == null)
{
var configuration = BuildConfiguration();
var ConnectionString = configuration.GetConnectionString("Default");
dbConnection = new SqlConnection(ConnectionString);
if (Context != null)
{
Context.Items["DB"] = dbConnection;
Context.Response.RegisterForDispose(dbConnection);
}
}
return dbConnection;
}
}
/// <summary>
/// Allows end of reqeust code to clean up this request's DB.
/// </summary>
public static void DisposeDB()
{
if (Context != null)
{
var db = Context.Items["DB"] as DbConnection;
db?.Close();
db?.Dispose();
Context.Items["DB"] = null;
}
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../Test1111.DbMigrator/"))//这里不要照搬我的代码不然会报错,改成自己的命名空间
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace System.Web
{
public static class MyHttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Test1111.QuickDb
{
public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
System.Web.MyHttpContext.Configure(httpContextAccessor);
return app;
}
}
}
.host中的starup添加
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Test1111.QuickDb;
namespace Test1111
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
StaticHttpContextExtensions.AddHttpContextAccessor(services);
services.AddApplication<Test1111HttpApiHostModule>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.UseStaticHttpContext();
app.InitializeApplication();
}
}
}
4.在.host的***HttpApiHostModule文件的ConfigureSwaggerServices中添加以下代码

options.CustomSchemaIds(type => type.FullName);
foreach (var xml in Directory.EnumerateFiles(AppContext.BaseDirectory, "*.xml"))
{
options.IncludeXmlComments(xml, true);
}
options.DocInclusionPredicate((docName, description) => true);
options.OperationFilter<AddResponseHeadersFilter>();
options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
options.OperationFilter<SecurityRequirementsOperationFilter>();
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
Password = new OpenApiOAuthFlow
{
AuthorizationUrl = new("http://localhost:44308/connect/authorize"),//改成自己的地址
TokenUrl = new($"http://localhost:44308/connect/token"),
Scopes = new Dictionary<string, string>()
{
{ IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.OpenId },
{ IdentityServerConstants.StandardScopes.Profile,IdentityServerConstants.StandardScopes.Profile },
}
}
},
});
var scheme = new OpenApiSecurityScheme()
{
Scheme = JwtBearerDefaults.AuthenticationScheme,
BearerFormat = "JWT",
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Description = "Bearer Token"
};
options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, scheme);
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
5.在.host的***HttpApiHostModule文件的ConfigureAuthentication中添加以下代码

private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = "Test1111";
options.BackchannelHttpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
});
}
6.所有工作完成后我们启动.host项目
启动前先将项目的ssl去掉

7.启动项目 登录之后方可使用api
