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 類實現
- /// <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>