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 | application/json; charset=utf-8 |
二、问题
如果客户端设置 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 类实现
-
-
-
-
- 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)
- {
-
- 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)
-
- {
- 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>