asp.net web api 解决ajax跨站点post请求提交 json 数据问题
一、web api
web api 的 controller ,必须继承 apicontroller:
public class TestController : ApiController
action 可以使用参数绑定直接将 post 请求带的 json 转化为对象,如下:
public string TestMethod([FromBody]PersonInfo pars)
注:要达到这种参数绑定,客户端必须将 设置如下的 content-type 值
Content-Type | |
二、问题
如果客户端设置 content-type 为 application/json ,将导致405 拒绝访问的问题,也就是ajax跨站点请求问题
三、解决方案
1.设置web.config允许跨站点请求
...
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,POST" />
</customHeaders>
</httpProtocol>
</system.webServer>
2.重写参数绑定类 [FromBody] ,换成自己的 [FromBodyBinder]
四、FromBodyBinder 的实现
</pre></p><pre name="code" class="csharp" style="font-size:14px;">FromBodyBinder 类实现
/// <summary>
/// web api 动态参数绑定模型
/// 解决脚本跨域访问问题
/// </summary>
public class FromBodyBinder : ParameterBindingAttribute
{
public override System.Web.Http.Controllers.HttpParameterBinding GetBinding(System.Web.Http.Controllers.HttpParameterDescriptor parameter)
{
return new FromBodyHttpParameterBinding(parameter);
}
}
public class FromBodyHttpParameterBinding : HttpParameterBinding
{
public FromBodyHttpParameterBinding(HttpParameterDescriptor des)
: base(des)
{
_parsType = des.ParameterType;
}
private Type _parsType;
private struct AsyncVoid { }
public override Task ExecuteBindingAsync(System.Web.Http.Metadata.ModelMetadataProvider metadataProvider, HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
//读取post内容
var task = actionContext.Request.Content.ReadAsStreamAsync();
var content = string.Empty;
var sm = task.Result;
sm.Seek(0, SeekOrigin.Begin);
var bytes = sm.ToByteArray();
content = bytes.ToStr(System.Text.Encoding.UTF8);
var obj = content.ToJsonDeserialize(_parsType);
SetValue(actionContext, obj);
TaskCompletionSource<AsyncVoid> tcs = new TaskCompletionSource<AsyncVoid>();
tcs.SetResult(default(AsyncVoid));
return tcs.Task;
}
}
用到一个扩展方法 ToJsonDeserialize(),实现如下
public static object ToJsonDeserialize(this string str,Type type)
{
try
{
if (string.IsNullOrWhiteSpace(str))
{
return null;
}
var serialize = new JavaScriptSerializer();
//针对日期序列化时区的转化
str = Regex.Replace(str, @"\\/Date\((-?\d+)\)\\/", match =>
{
var dt = new DateTime(1970, 1, 1);
dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value));
dt = dt.ToLocalTime();
return dt.ToString("yyyy-MM-dd HH:mm:ss");
});
return serialize.Deserialize(str, type);
}
catch (Exception ex)
{
return null;
}
}
五、测试代码部分
5.1 服务器代码
public class TestController : ApiController
{
public class PersonInfo
{
public int id { set; get; }
public string name { set; get; }
public DateTime create_time { set; get; }
}
public string TestMethod([FromBodyBinder]PersonInfo pars)
//public string TestMethod([FromBody]PersonInfo pars) web api 的写法
{
var result = new
{
receive_time=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") ,
data=pars
};
return result.ToJsonSerialize();
}
}
5.2客户端写法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>页面标题</title>
</head>
<script src="zepto.js"></script>
<body>
<input type="button" id="btn" value="type=button" />
</body>
<script>
$(function(){
$("#btn").click(function(){
var reqdata={
id:"111",
name:"张三",
create_time:"2012-12-12 12:12:12"
};
var reqdatajson = JSON.stringify(reqdata);
alert("请求内容:"+reqdatajson);
var url="http://www.xxx.com/Test/TestMethod";
$.ajax({
url:url,
data: reqdatajson,
dataType: "json",
type: "POST",
success:function(d){
alert("响应内容:"+JSON.stringify(d))
},
error:function(r,s,m){
console.log(r+"---"+s+"-----------"+m)
}
});
});
});
</script>
</html>