//1.restful 是一种架构风格,接口的设计风格
// 一切都是资源,
// 所有资源有统一的接口四种接口:Create--Post,Read--Get,Update--Put/Patch,Delete--Delete
// 基于http,无状态
//2.URI 统一资源标志符,资源的唯一标识;URL 统一资源定位符,资源的地址;URL是URI的子集
//3.RPC(Remote Procedure Call)RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果
//4.网站启动执行Application_Start()-->给Route增加地址规则-->请求进来时会经过路由匹配合适的控制器-->根据HttpMethod找方法(根据方法名开头匹配)
// 方法名以Get开头或者有特性[HttpGet],就是get请求
//5.特性路由,学习博客https://www.cnblogs.com/lgx5/p/10967720.html
// config.MapHttpAttributeRoutes();注册路由时加上这个方法就是启用特性路由
// [Route("api/values/{id}/V2")]方法上加上自定义路由
// 路由可以增加约束 [Route("api/values/{id:int}/V2")],可空 [Route("api/values/{id:int?}/V2")],默认值 [Route("api/values/{id=10}/V2")]
//6.配合IOC
// a.实现下IDependencyResolver,构造函数传入IUnityContainer
// 在GetService和GetServices方法中根据传入的类型构造对象
// b.网站初始化方法中config.DependencyResolver = new UnityDependencyResolver(ContainerFactory.Container);
// 把默认的DependencyResolver换成自己的Resolver,传入Container
//7.权限认证
// 默认不支持Session,可以自行扩展支持(不推荐)
// 1)登录成功,服务器生成token/ticket,账号+密码+时间+其他信息(ip等),返回token/ticket
// 2)请求时,带上token/ticket
// 3)服务器验证token/ticket
// 4)每个方法都验证token/ticket,可以用filter来实现
//8.异常处理Filter
// 自己定义的特性要继承ExceptionFilterAttribute,覆写OnException方法处理异常
// 发现抓不到HttpResponseException这类异常,可以抓到Exception
// 作用域只是方法内部
//9.全局异常处理
// 自己定义异常处理类继承ExceptionHandler,覆写ShouldHandle和Handle
// 替换框架自带的异常处理类config.Services.Replace(typeof(IExceptionHandler), new CustomExceptionHandler());
// 和IOC的初始化冲突了
//10.ActionFilter 方法执行前后可以加动作
// 自定义特性,继承ActionFilterAttribute,覆写OnActionExecuting和OnActionExecuted
//11.跨域 浏览器的同源策略限制
// 但是浏览器自己的行为不受影响,js/css/img/iframe等的地址
// 解决方法:Jsonp和Cors
// Jsonp 用script标签的src请求回来js代码执行,试下跨域
// Cors: 返回头加上Access-Control-Allow-Origin,它的值是允许跨域的域名
// 有个包using System.Web.Http.Cors;可以在不同作用域实现
//12.WebAPI 寄宿
// 必须引用System.Web.dll,除此外还引用其他
// 引用必须的dll:System.Web.Http.SelfHost.dll;System.Web.Http.dll;System.Net.Formatting.Http.dll;System.Net.Http.dll
// 可以安装包Microsoft.AspNet.WebApi和Microsoft.AspNet.WebApi.SelfHost 就把除System.Web.dll外所有dll自动引用
//13.自动生成WebAPI文档 \Areas\HelpPage\Views\Help\DisplayTemplates\index.cshtml
// http://localhost:8087/help/index#endregion
using MySOA.WebAPI.Utinity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Unity;
namespace MySOA.WebAPI
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);//WebAPI注册,MVC是没有这个的
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
#region 开启session
public override void Init()
{
this.AuthorizeRequest += GlobalAuthorizeRequest;
base.Init();
}
private void GlobalAuthorizeRequest(object sender, EventArgs e)
{
//启用WebApi中的session
HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
#endregion
}
}
初始化基本都在这
using MySOA.WebAPI.Utinity;
using MySOA.WebAPI.Utinity.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Http.ExceptionHandling;
namespace MySOA.WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服务
//全局异常处理:用自定义的代替框架原本的
config.Services.Replace(typeof(IExceptionHandler), new CustomExceptionHandler());
//用自己的DependencyResolver替换框架默认的,在自己写的中传入UnityContainer
//实现IOC
config.DependencyResolver = new UnityDependencyResolver(ContainerFactory.Container);
特性初始化时添加,对全局生效
//config.Filters.Add(new CustomBasicAuthorazeAttribute());
允许全局跨域
//config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
// Web API 路由
config.MapHttpAttributeRoutes();
//自己定义路由
config.Routes.MapHttpRoute(
name: "CustomerApi",//默认路由
routeTemplate: "api/{controller}/{action}/{id}",//api开头,第二个是控制器,第三个是参数
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",//默认路由
routeTemplate: "api/{controller}/{id}",//api开头,第二个是控制器,第三个是参数
defaults: new { id = RouteParameter.Optional }
);
}
}
}
特性路由
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MySOA.WebAPI.Controllers
{
//加上这个特性,所有的特性路由就能省略这一节
//如果不想用,根节点前加~,[Route("~api/values/{id:int}/V2")]
//[RoutePrefix("api/Values")]
public class ValuesController : ApiController
{
//部署IIS时遇到无权访问配置文件和无权访问站点的情况,添加了两个用户IIS_IUSERS,IUSR
// GET api/values
[HttpGet]
public IEnumerable<string> LGet()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
[Route("api/values/{id:int}/V2")]//特性路由,并且路由加上了int类型的约束
public string GetV2(int id)
{
return "valueV2";
}
[Route("api/values/{name}/V2")]//特性路由
public string GetV2(string name)
{
return $"value {name}";
}
[Route("api/values/V2/{id}/{name}")]//父类下的资源,两个参数
public string GetV2(string id, string name)
{
return $"value {id}_{name}";
}
// POST api/values
public void Post([FromBody] string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
}
IOC实现
using Microsoft.Practices.Unity.Configuration;
using MySOA.WebAPI.Interface;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;
using Unity;
namespace MySOA.WebAPI.Utinity
{
public class ContainerFactory
{
public static IUnityContainer Container { get; }
static ContainerFactory()
{
#region 读配置固定写法,不用关心
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();//
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"CfgFiles\Unity.config");
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
#endregion
//声明容器
Container = new UnityContainer();
//这个是配置中容器的名字<container>标签
configSection.Configure(Container, "WebAPIContainer");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Dependencies;
using Unity;
namespace MySOA.WebAPI.Utinity
{
//控制器实例化
public class UnityDependencyResolver : IDependencyResolver
{
private IUnityContainer UnityContainer = null;
public UnityDependencyResolver(IUnityContainer unityContainer)
{
this.UnityContainer = unityContainer;
}
public IDependencyScope BeginScope()//作用域
{
return new UnityDependencyResolver(this.UnityContainer.CreateChildContainer());
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposedManage)
{
if (disposedManage)
{
this.UnityContainer.Dispose();
}
}
~UnityDependencyResolver()
{
//必须是false
this.Dispose(false);
}
//下面这两个方法用来构造对象
public object GetService(Type serviceType)
{
try
{
return this.UnityContainer.Resolve(serviceType);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
//这样写是可以的,因为在WebApi中除了会构造我们的对象外还会构造一些框架的对象,会异常,返回null没关系
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return this.UnityContainer.ResolveAll(serviceType);
}
catch (Exception ex)
{
return new List<object>();
Console.WriteLine(ex.ToString());
return null;
}
}
}
}
Form认证
[CustomAllowAnonymous]
[HttpGet]
public string Login(string account, string password)
{
if ("admin".Equals(account) && "123456".Equals(password))
{
FormsAuthenticationTicket ticketObj = new FormsAuthenticationTicket(0, account, DateTime.Now, DateTime.Now.AddHours(1),
true, $"{account}&{password}", FormsAuthentication.FormsCookiePath);
var rst = new { Result = true, Ticket = FormsAuthentication.Encrypt(ticketObj) };
return JsonConvert.SerializeObject(rst);
}
else
{
return JsonConvert.SerializeObject(new { Result = false });
}
}
$("#bntLoginSSO").on("click", function () {
$.ajax({
url: "/api/users/SSOLoginTest",
type: "Get",
data: { "Account": $("#txtAccount").val(), "Password": $("#txtPassword").val() },
success: function (data) {
var rst = JSON.parse(data);
if (rst.Result) {
ticket = rst.Ticket;
alert(ticket);
}
else {
alert("fail");
}
},
dataType: "Json"
});
});
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Security;
namespace MySOA.WebAPI.Utinity.Filters
{
public class CustomBasicAuthorazeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
//方法或者控制器上标记该特性就不验证了
if (actionContext.ActionDescriptor.GetCustomAttributes<CustomAllowAnonymousAttribute>().FirstOrDefault() != null
|| actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<CustomAllowAnonymousAttribute>().FirstOrDefault() != null)
{
return;
}
var authorization = actionContext.Request.Headers.Authorization;
if (authorization != null && this.ValidateTicket(authorization.Parameter))
{
return;//有权限直接返回
}
else
{
//throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);
this.HandleUnauthorizedRequest(actionContext);
}
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
var challengeMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);//告诉浏览器要验证
challengeMessage.Headers.Add("Www-Authenticate", "Basic");//权限信息放在basic
//actionContext.Request.CreateResponse().Headers.Add("Www-Authenticate", "Basic");
actionContext.Response = challengeMessage;
base.HandleUnauthorizedRequest(actionContext);
}
private bool ValidateTicket(string encryptTicket)
{
if (string.IsNullOrEmpty(encryptTicket))
return false;
//Ticket解密
var strTicket = FormsAuthentication.Decrypt(encryptTicket).UserData;
//验证账号密码
return string.Equals(strTicket, "admin&123456");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MySOA.WebAPI.Utinity.Filters
{
public class CustomAllowAnonymousAttribute : Attribute
{
}
}
异常处理Filter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http.Filters;
namespace MySOA.WebAPI.Utinity.Filters
{
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
Console.WriteLine(actionExecutedContext.Exception.Message);//记录日志
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(
System.Net.HttpStatusCode.OK,
new
{
Result = true,
Message = "发生异常"
});//创造一个返回
}
}
}
全局异常处理
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;
namespace MySOA.WebAPI.Utinity.Filters
{
/// <summary>
/// 全局异常处理
/// 发现和Unity的IOC冲突了,注释自己定义的UnityDependencyResolver后起作用
/// </summary>
public class CustomExceptionHandler : ExceptionHandler
{
/// <summary>
/// 自己确定哪些情况的异常要处理--自己写规则
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
//public override bool ShouldHandle(ExceptionHandlerContext context)
//{
// string url = context.Request.RequestUri.AbsoluteUri;
// return url.Contains("/api");
//}
/// <summary>
/// 异常处理
/// </summary>
/// <param name="context"></param>
public override void Handle(ExceptionHandlerContext context)
{
检查Exception类型,异常的详细信息
//if (context.Exception is HttpException)
//{
//}
context.Result = new ResponseMessageResult(context.Request.CreateResponse(
System.Net.HttpStatusCode.OK,
new
{
Result = false,
Message = "发生异常",
Bug = context.Exception.Message
}));
}
}
}
ActionFilter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace MySOA.WebAPI.Utinity.Filters
{
public class CustomActionFilterAttribute: ActionFilterAttribute
{
/// <summary>
/// 执行前
/// </summary>
/// <param name="actionContext"></param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
Console.WriteLine("执行前");
}
/// <summary>
/// 执行后
/// </summary>
/// <param name="actionExecutedContext"></param>
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
Console.WriteLine("执行后");
actionExecutedContext.Response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:8087");
}
}
}
寄宿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace MySOA.WebAPI.SelfBoarding
{
class Program
{
static void Main(string[] args)
{
try
{
var config = new HttpSelfHostConfiguration("http://localhost:7077");
config.Routes.MapHttpRoute(name: "DefaultApi",//默认路由
routeTemplate: "api/{controller}/{id}",//api开头,第二个是控制器,第三个是参数
defaults: new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("服务已经启动");
Console.WriteLine("输入任意字符关闭");
Console.ReadKey();
server.CloseAsync().Wait();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MySOA.WebAPI.SelfBoarding
{
//加上这个特性,所有的特性路由就能省略这一节
//如果不想用,根节点前加~,[Route("~api/values/{id:int}/V2")]
//[RoutePrefix("api/Values")]
public class TestController : ApiController
{
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
}