通过ajax执行post请求时,返回了Bad Request而不是JSON结果。

25

Javascript

jqXHR = $.ajax({ url: $frm.attr("action"), type: "POST", dataType: "json", cache: false,
  headers: headers, contentType: "application/json;charset=UTF-8", data: ko.mapping.toJSON(data, map),
  beforeSend: function(x) {
    if (x && x.overrideMimeType) {
      return x.overrideMimeType("application/json;charset=UTF-8");
    }
  }
});

jqXHR.fail(function(xhr, err, msg) {  /* xhr.responseText  NEED TO BE JSON!!! */ });

在Chrome中

标题

Request Method:POST
Status Code:400 Bad Request
Request Headersview source
Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,pt-BR;q=0.6,pt;q=0.4
Connection:keep-alive
Content-Length:10
Content-Type:application/json;charset=UTF-8
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36
X-Requested-With:XMLHttpRequest
Request Payloadview source {Id:0}
Response Headersview source
Cache-Control:private
Content-Length:54
Content-Type:application/json; charset=utf-8
Date:Thu, 27 Feb 2014 14:01:59 GMT
Server:Microsoft-IIS/8.0
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:5.1
X-Powered-By:ASP.NET

响应

[{"Name":"Nome","ErrorMessage":"字段是必需的。"}]

在Chrome中工作!


在IE8中

请求头

POST /Motivos/Salvar HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: pt-br
x-requested-with: XMLHttpRequest
Content-Type: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)
Content-Length: 10
Connection: Keep-Alive
Pragma: no-cache

标题(响应)

HTTP/1.1 400 Bad Request
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 5.1
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 27 Feb 2014 13:51:46 GMT
Content-Length: 11

Bad Request

不能工作!!

Asp.net MVC

过滤器

public class HandleExceptionAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            var ex = filterContext.Exception.GetBaseException();
            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    ex.Message,
                    ex.GetType().Name
                }
            };
            filterContext.ExceptionHandled = true;
        }
        else
        {
            base.OnException(filterContext);
        }
    }
}

应用于 GlobalFilterCollection

控制器

[ValidateJsonAntiForgeryToken, HttpPost]
public virtual JsonResult Salvar(TViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        TEntity model;
        if (default(TKey).Equals(viewModel.Id))
        {
            model = Mapper.Map<TEntity>(viewModel);
            AdicionarEntidade(model, viewModel);
        }
        else
        {
            model = Repositorio.Get(viewModel.Id);
            Mapper.Map(viewModel, model, typeof(TViewModel), typeof(TEntity));
            SalvarEntidade(model, viewModel);
        }

        return SalvarResult(model);
    }

    Response.StatusCode = 400;
    return Json(ModelState.ToJson(), JsonRequestBehavior.AllowGet);
}

扩展

public static object ToJson(this ModelStateDictionary dic, params string[] othersMessages)
{
    var states = (from e in dic where e.Value.Errors.Count > 0
                  select new { Name = e.Key, e.Value.Errors[0].ErrorMessage }).ToList();

    if (othersMessages != null)
        foreach (var message in othersMessages)
            states.Add(new { Name = "", ErrorMessage = message });

    return states;
}

问题

  • 为什么没有xhr.responseText对象?
  • 如何以与Chrome恢复的方式检索JSON?

我需要JSON来填充表单!

注释:2014年3月11日

当我在控制器中添加Response.TrySkipIisCustomErrors = true;时,它可以工作!responseText返回JSON。为什么?


如果您在$.ajax中设置了不同的content-type,最好将processData设置为false。另外,在beforeSend中为什么要覆盖mime类型?您可以在服务器上控制返回类型。 - Kerry Liu
你能展示一下在IE和Chrome上,使用ko.mapping.toJSON(data, map)函数后data的值是多少吗? - Lee Gary
@KerryLiu 即使设置了 processData: false,错误仍然发生。关于 beforeSend,这只是一个测试。即使删除了此事件,该错误仍会发生! - ridermansb
@LeeGary 数据:在IE中 "{"Id":"0"}"在Chrome中 "{"Id":"0"}" - ridermansb
是我自己的问题还是你的Chrome(响应?)头也显示状态码400 Bad Request - Brian North
@BrianNorth 是正确的。如果必填字段未填写,则在Chrome中状态是正确的。但在IE中,responseText是错误的。 - ridermansb
6个回答

36

我认为这个问题是由于IIS尝试使用自定义错误响应而不是发送控制器生成的错误消息所导致的。

<system.webServer>
    ...
    <httpErrors existingResponse="PassThrough"></httpErrors>
    ...
</system.webServer>

或者

Response.TrySkipIisCustomErrors = true;

参考 - https://dev59.com/TW865IYBdhLWcg3wFqhV#4029197

查看这篇博客文章http://weblog.west-wind.com/posts/2009/Apr/29/IIS-7-Error-Pages-taking-over-500-Errors

因为响应代码设置为400,IIS将用其自定义错误页内容替换您的内容


2

您的响应返回了 Content-Type: text/html 的 http 头信息,但应该是 application/json。这在 Chrome 中不是问题(控制台中也不会得到 不匹配警告),因为您依赖于 overrideMimeType... 你猜对了,它不适用于 IE。实际上,在旧版的 IE 上,以下代码根本不会被执行:

if (x && x.overrideMimeType) {
  return x.overrideMimeType("application/json;charset=UTF-8");
}

您的解决方案可能是确保内容以正确的内容类型提供。如果您熟悉像Burp Suite这样的篡改工具,您可以即时添加正确的标头并查看是否解决了问题。我可能会避免嵌入像AddHeader这样的方法,并查看是否有一种在更高层次 - 例如路由 - 可以解决此问题的方式。


2
我看到的问题是你试图将JSON的编码设置为UTF-8。通常情况下,这样做没有问题,但在IIS上,它会报告UTF-8不必要的自定义错误。
还有几点需要注意。你正在进行POST操作,因此服务器将期望收到一个JSON文件。如果未提供任何文件,服务器将不知道该怎么处理。

关于UTF-8无意义,因为它在Chrome中可以工作。我已经尝试删除UTF-8,但错误仍然发生! - ridermansb
我正在向服务器发送JSON! - ridermansb
你发送的JSON是什么样子的?你只展示了响应JSON,响应是服务器发送给客户端的。此外,你一次只能发送一个JSON文件。因此,如果你有多个表单同时提交,它们需要被打包成一个JSON文件。 - jemiloii

1
我填写了一个失败的测试,应该会有所帮助。
$.post($frm.attr("action"), ko.mapping.toJSON(data, map))
.done(function (dataVal) {
    //process dataVal
    var mystruct = GenerateCache($.parseJSON(dataVal));
})
.fail(function (jqxhr, textStatus, error) {
    if (jqxhr.responseText.indexOf("YourMoniker") != -1) {
        parseData($.parseJSON(jqxhr.responseText));
    } else {
        var err = textStatus + ', ' + error;
        console.log("Request Failed: " + err);
    }
});


function GenerateCache(data) {
    var obj = function () { };
    obj.prototype = data;
    return new obj();
}

请特别关注.fail部分的错误处理。


我使用$.ajax是因为我设置了头信息。如何在$.post中设置头信息? - ridermansb
我明白了。如果你要尝试设置自己的标头,那可能是唯一且最好的方法。 - CaptainBli
你的头文件中具体需要什么? - CaptainBli
你可以像这样发送 dataType:$.post(url[, data][, success(data, textStatus, jqXHR)][, dataType])。你也可以在 URL 中添加数据到查询字符串,例如:/getData.aspx?param1=test&param2=54...。这样仍然可以发送数据。 - CaptainBli
鉴于$.ajax方法和$.post方法发送的请求完全相同,都是使用method: 'POST',而且OP中显示IE将其作为POST请求发送,从一个方法切换到另一个方法将得到完全相同的结果。 - Brian North
但是如果你实际上不需要它,它会减少额外的工作量。 - CaptainBli

0

这不是你的控制器错误,那个工作正常。你缺少了一个必填字段:IE和Chrome都返回状态码400 Bad Request,但只有Chrome可以正确地处理responseText并给你[{"Name":"Nome","ErrorMessage":"campo obrigatório."}] 这意味着你缺少了一个表单字段。

尽管我已经到处搜索并没有找到任何关于特定IE bug的参考,导致无法处理具有非200状态码的XMLHttpRequest.responseText,但似乎IE正在用自己的响应体替换你的响应体:

Headers (Response)

HTTP/1.1 400 Bad Request
...
Content-Length: 11

Bad Request

该内容表明“content”按照它的方式对待它,是“错误请求”的状态文本,而不是正确的json响应(例如,Chrome读取的内容长度为54)。这可能意味着IE正在丢弃您的响应正文(我很怀疑,那将是非常惊人的)或者它只是没有被“正确处理”。尝试转储您的 jqXHR 对象的其余部分和您的 fail 处理程序的参数,看看是否可以在其中找到它。


0

IE(包括IE11在内的所有版本)将在状态文本中放置“Bad Request”,并忽略您作为消息输入的JSON。

为了在IE上的错误中使用xhr.responseText,您需要抛出异常,而不是返回带有HttpStatusCode.BadRequest;的Json或JsonResult。

所以... 在此之前:

Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = "There is already a distribution set which covers part or all of this period" });

这在Chrome、FF和任何正常的浏览器中都可以工作。

throw new Exception("You have posted invalid datas.");

作为未处理的异常,它将作为响应传递给浏览器,在Chrome、FF甚至IE中都可以工作。虽然不够优雅,但与所有未处理的异常(或者说所有异常)一样,它能够完成让您接收到适当响应的工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接