在ABP模板项目中,WebApi模块中通过dynamic webapi技术将所有实现了IApplicationService的类型自动创建对应的动态webapi,进而外部可以直接调用。但是这里的WebApi模块不是真正意义上的.NET WebApi项目,只是一个类库。本文章记录在ABP模板项目的基础上,创建.NET WebApi项目,并沿用ABP模板项目中的数据仓储方式和模块式开发。
一、创建.NET WebApi项目
- 项目结构
- 基于OWIN的token认证
首先在App_Start下生成OWIN的Startup.cs类,并设置为Bearer token认证方式
Nuget安装
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Security.OAuth
Microsoft.Owin.Cors
Startup.cs代码
using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Security.OAuth;
using Owin;
[assembly: OwinStartup(typeof(AbpDemo.TrueWebApi.App_Start.Startup))]
namespace AbpDemo.TrueWebApi.App_Start
{
public class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
WebApiConfig.Register(config);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
}
创建OauthController用于生成token
using AbpDemo.TrueWebApi.App_Start;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Security;
using System;
using System.Security.Claims;
using System.Web.Http;
namespace AbpDemo.TrueWebApi.Controllers
{
public class OauthController : ApiController
{
[HttpPost]
[Route("oauth/token")]
public string Token(string account, string password)
{
var token = string.Empty;
if (/* account validation success *)
{
var oAuthIdentity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, account));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromDays(30));
token = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
}
return token;
}
}
}
以上只是WebApi项目常规生成token的方式,既然项目依赖于ABP,因此有必要以module的形式创建项目怒,并沿用ABP模板项目中的登录方式。
二、Module式WebApi项目
项目中的Controller需要继承AbpApiController
Nuget安装
Abp
Abp.Web
Abp.Web.Api
Abp.Owin
Abp.Zero
EntityFramework
Microsoft.AspNet.WebApi.Cors
- 创建AbpDemoTrueWebApiModule.cs
在App_Start下创建AbpDemoTrueWebApiModule.cs
using Abp.Configuration.Startup;
using Abp.Modules;
using Abp.WebApi;
using System.Reflection;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace AbpDemo.TrueWebApi.App_Start
{
[DependsOn(typeof(AbpWebApiModule),
typeof(AbpDemoApplicationModule),
typeof(AbpDemoCoreModule),
typeof(AbpDemoDataModule))]
public class AbpDemoTrueWebApiModule : AbpModule
{
public override void Initialize()
{
base.Initialize();
Configuration.Modules.AbpWeb().AntiForgery.IsEnabled = false;
}
public override void PreInitialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
var cors = new EnableCorsAttribute("*", "*", "*");
Configuration.Modules.AbpWebApi().HttpConfiguration.EnableCors(cors);
Configuration.Modules.AbpWebApi().HttpConfiguration.SuppressDefaultHostAuthentication();
Configuration.Modules.AbpWebApi().HttpConfiguration.Filters.Add(new HostAuthenticationFilter("Bearer"));
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
- 修改Global.asax.cs
using Abp.Web;
using AbpDemo.TrueWebApi.App_Start;
using System;
namespace AbpDemo.TrueWebApi
{
public class WebApiApplication : AbpWebApplication<AbpDemoTrueWebApiModule>
{
protected override void Application_Start(object sender, EventArgs e)
{
base.Application_Start(sender, e);
}
}
}
- 修改OauthController
这里照搬AbpDemo.WebApi/AccountController
LoginModel.cs
using System.ComponentModel.DataAnnotations;
namespace AbpDemo.Api.Models
{
public class LoginModel
{
public string TenancyName { get; set; }
[Required]
public string UsernameOrEmailAddress { get; set; }
[Required]
public string Password { get; set; }
}
}
OauthController.cs
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.UI;
using Abp.WebApi.Controllers;
using AbpDemo.Authorization;
using AbpDemo.Authorization.Users;
using AbpDemo.MultiTenancy;
using AbpDemo.TrueWebApi.Models;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Threading.Tasks;
using System.Web.Http;
namespace AbpDemo.TrueWebApi.Controllers
{
public class OauthController : AbpApiController
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
private readonly LogInManager _logInManager;
static OauthController()
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
}
public OauthController(LogInManager logInManager)
{
_logInManager = logInManager;
}
[HttpPost]
[Route("oauth/token")]
public async Task<OAuthResult> OAuth(LoginModel loginModel)
{
var oauthResult = new OAuthResult();
try
{
var loginResult = await GetLoginResultAsync(loginModel.UsernameOrEmailAddress, loginModel.Password, loginModel.TenancyName);
var ticket = new AuthenticationTicket(loginResult.Identity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
string token = OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
oauthResult.Code = 0;
oauthResult.Message = "Success";
oauthResult.Data = new TokenOutput { Token = token };
}
catch (Exception ex)
{
oauthResult.Code = 1;
oauthResult.Message = ex.Message;
}
return oauthResult;
}
private async Task<AbpLoginResult<Tenant, User>> GetLoginResultAsync(string usernameOrEmailAddress, string password, string tenancyName)
{
var loginResult = await _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName);
switch (loginResult.Result)
{
case AbpLoginResultType.Success:
return loginResult;
default:
throw CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);
}
}
private Exception CreateExceptionForFailedLoginAttempt(AbpLoginResultType result, string usernameOrEmailAddress, string tenancyName)
{
switch (result)
{
case AbpLoginResultType.Success:
return new ApplicationException("Don't call this method with a success result!");
case AbpLoginResultType.InvalidUserNameOrEmailAddress:
case AbpLoginResultType.InvalidPassword:
return new UserFriendlyException("LoginFailed", "InvalidUserNameOrPassword");
case AbpLoginResultType.UserIsNotActive:
return new UserFriendlyException("LoginFailed", "UserIsNotActiveAndCanNotLogin");
case AbpLoginResultType.UserEmailIsNotConfirmed:
return new UserFriendlyException("LoginFailed", "Your email address is not confirmed. You can not login"); //TODO: localize message
default: //Can not fall to default actually. But other result types can be added in the future and we may forget to handle it
return new UserFriendlyException("LoginFailed");
}
}
}
public class OAuthResult
{
public int Code { get; set; }
public string Message { get; set; }
public TokenOutput Data { get; set; }
}
public class TokenOutput
{
public string Token { get; set; }
}
}
- 修改Startup.cs
using Abp.Owin;
using AbpDemo.TrueWebApi.Controllers;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(AbpDemo.TrueWebApi.App_Start.Startup))]
namespace AbpDemo.TrueWebApi.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseAbp();
app.UseOAuthBearerAuthentication(OauthController.OAuthBearerOptions);
}
}
}
至此,获取token的代码已经完成,现在使用ABP模板项目的默认账号密码:admin/123qwe 进行测试(记得在web.config添加数据库连接字符串),测试结果如下图:
三、Application层访问测试
现在已经可以得到token,那么可以利用这个token直接调用Application层的IApplicationService方法从而获取数据呢?
在AbpDemo.Core中创建Client Entity,并在AbpDemo.Application创建ClientAppService并进行登录认证和权限检查
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using AbpDemo.Clients.Dto;
namespace AbpDemo.Clients
{
public interface IClientAppService : IAsyncCrudAppService<ClientDto, int, PagedResultRequestDto, CreateClientDto, UpdateClientDto>
{
}
}
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Domain.Repositories;
using AbpDemo.Authorization;
using AbpDemo.Clients.Dto;
namespace AbpDemo.Clients
{
[AbpAuthorize(PermissionNames.Pages_Users)]
public class ClientAppService : AsyncCrudAppService<Client, ClientDto, int, PagedResultRequestDto, CreateClientDto, UpdateClientDto>, IClientAppService
{
public ClientAppService(IRepository<Client, int> repository) : base(repository)
{
}
}
}
在ResultController中通过构造函数注入的方式将IClientAppService注入进来,然后直接进行方法调用
using Abp.Application.Services.Dto;
using Abp.Domain.Uow;
using Abp.Web.Models;
using Abp.WebApi.Authorization;
using Abp.WebApi.Controllers;
using AbpDemo.Clients;
using System.Threading.Tasks;
using System.Web.Http;
namespace AbpDemo.TrueWebApi.Controllers
{
[AbpApiAuthorize]
public class ResultController : AbpApiController
{
private readonly IClientAppService _clientAppService;
public ResultController(IClientAppService clientAppService)
{
_clientAppService = clientAppService;
}
[HttpGet]
[UnitOfWork(false, IsDisabled = true)]
public async Task<AjaxResponse> GetClientById(int id)
{
var client = await _clientAppService.Get(new EntityDto<int>(id));
return new AjaxResponse(client);
}
}
}
测试结果如下图:
大功告成!!!