Unity + iBatis + Asp.net Mvc 系统搭建
之前用EntityFramework Code First做了一些小项目,很是方便;后来在一个 Java 项目中接触了myBatis之后,深深的喜欢上了这种最直接最原始最灵活的数据库操作,所以最终决定改造之前的项目,使用IBatis访问数据库;
一、框架搭建
1)新建一个Asp.net Mvc的应用,.Net使用4.5
2)使用 Nuget 安装 Unity 3.5,因为比较熟悉 Unity;
3)使用 Nuget 安装 Unity Bootstrapper for ASP.NET MVC
4)使用 Nuget 安装 Common Service Locator
5)安装 IBatisNet,版本有点老了,不过还是能稳定工作,先用着吧;
前台,jquery,bootstrap 等
二、配置
1)配置 IBatis
a)web.config 中配置 log 信息
b)provider.config,以及 sqlmap.config 文件,放到了 ~/App_Data下
2)配置 Unity
a)采用配置文件进行配置:
首先到解决方案下,找到 Packages/Unity.xx.xx.xx 下的 UnityConfiguration30.xsd,拷贝到VisualStudio 的 xml/schema目录下,这样编辑配置文件就有语法提示了;
在 ~/App_Data目录下,创建一个 unity.config 文件,内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <!-- assemblies 加入程序集 -->
<assembly name="Mwms.Framework"/>
<assembly name="Microsoft.Practices.Unity.Mvc"/>
<assembly name="System.Web.Mvc"/> <!-- namespaces 加入命名空间,这样就可以直接使用其中的类型了 -->
<namespace name="Mwms.Framework" />
<namespace name="Mwms.Framework.Interfaces" />
<namespace name="Mwms.Framework.Repository" /> <namespace name="System.Web.Mvc"/>
<!-- 配置一个生命周期管理器 -->
<alias alias="PerRequestLifeTimeManager" type="Microsoft.Practices.Unity.PerRequestLifetimeManager, Microsoft.Practices.Unity.Mvc"/> <container> <!-- 包装 IBatis 的 SQLMapper,自行实现, -->
<register type="SqlMapperWrapper" mapTo="SqlMapperWrapper">
<lifetime type="PerRequestLifeTimeManager" />
<constructor>
<param name="configPath" value="~/App_Data/SqlMap.config" />
</constructor>
</register> <!--<register type="IMwLogger" mapTo="DebugLogger" />
<register type="IMwCacher" mapTo="DefaultNetCacher" />--> <!-- Repositories 配置一个泛型仓储实现映射 -->
<register type="IRepository`1" mapTo="IBatisRepository`1" />
<!-- 加入更多类型映射 -->
</container>
</unity>
</configuration>
b)载入UnityContainer 配置:
修改 ~/App_Start 目录下 UnityConfig.cs , 修改 RegisterTypes 方法,载入对应的配置文件;
public static void RegisterTypes(IUnityContainer containerObject)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration(); // TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>(); var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_data", "unity.config"); var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = path };
var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); containerObject.LoadConfiguration(unitySection);
}
c)SqlMap 配置文件中,载入相应的Mapper文件……
其他的,就是IBatis的标准开发流程,实体类,xml配置文件,仓储类,服务接口,服务实现等;
做完之后,就可以直接使用构造函数注入,在 Controller中使用代码访问数据库了,直接用SQL也可以完成很多逻辑
:调用与用户相关的Web API
在前一篇博文中,我们通过以 OAuth 的 Client Credential Grant 授权方式(只验证调用客户端,不验证登录用户)拿到的 Access Token ,成功调用了与用户无关的 Web API。
在这篇博文中,我们将以 OAuth 的 Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。
对应的应用场景是:为自家的网站开发手机 App(非第三方 App),只需用户在 App 上登录,无需用户对 App 所能访问的数据进行授权。
根据 OAuth 规范,客户端获取 Access Token 的请求方式如下:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded grant_type=password&username=johndoe&password=A3ddj3w
根据上面的请求方式,在 C# 中用 HttpClient 实现一个简单的客户端,代码如下:
public class OAuthClientTest
{
private HttpClient _httpClient; public OAuthClientTest()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
} [Fact]
public async Task Get_Accesss_Token_By_Resource_Owner_Password_Credentials_Grant()
{
Console.WriteLine(await GetAccessToken());
} private async Task<string> GetAccessToken()
{
var clientId = "1234";
var clientSecret = "5678"; var parameters = new Dictionary<string, string>();
parameters.Add("grant_type", "password");
parameters.Add("username", "博客园团队");
parameters.Add("password", "cnblogs.com"); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret))
); var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
var responseValue = await response.Content.ReadAsStringAsync();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return JObject.Parse(responseValue)["access_token"].Value<string>();
}
else
{
Console.WriteLine(responseValue);
return string.Empty;
}
}
}
(注:与之前相比,这里的 client_id/client_secret 改为了 Basic Authorization,以更好的遵循 OAuth 规范)
在服务端,基于 Owin OAuth, 针对 Resource Owner Password Credentials Grant 的授权方式,只需重载 OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials() 方法即可。代码如下:
public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
//... public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
//调用后台的登录服务验证用户名与密码 var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket); await base.GrantResourceOwnerCredentials(context);
}
}
完整的CNBlogsAuthorizationServerProvider实现代码如下(与之前相比,context.TryGetFormCredentials 改为了 context.TryGetBasicCredentials):
这样,运行客户端程序就可以拿到 Access Token 了。
接下来,我们拿着以这种方式获取的 Access Token,就可以调用与用户相关的 Web API 了。
在服务端我们通过一个简单的 Web API 测试一下,代码如下:
public class UsersController : ApiController
{
[Authorize]
public string GetCurrent()
{
return User.Identity.Name;
//这里可以调用后台用户服务,获取用户相关数所,或者验证用户权限进行相应的操作
}
}
然后,客户端用以 grant_type=password 方式拿到的 Access Token 调用这个Web API,客户端增加的代码如下:
[Fact]
public async Task Call_WebAPI_By_Resource_Owner_Password_Credentials_Grant()
{
var token = await GetAccessToken();
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Console.WriteLine(await (await _httpClient.GetAsync("/api/users/current")).Content.ReadAsStringAsync());
}
客户端运行结果如下:
"博客园团队"
调用成功!运行结果正是获取 Access Token 时所用的 username 。
结合 ASP.NET 现有的安全机制,借助 OWIN 的威力,Microsoft.Owin.Security.OAuth 的确让开发基于 OAuth 的 Web API 变得更简单。