【问题标题】:400 Bad Request error when sending POST request to WCF service using JavaScript使用 JavaScript 向 WCF 服务发送 POST 请求时出现 400 Bad Request 错误
【发布时间】:2019-12-05 23:41:50
【问题描述】:

我有一个以这种方式初始化的非常简单的自托管 WCF 应用程序:

Host = new WebServiceHost(this, new Uri(serviceAddress));
var binding = new WebHttpBinding();
binding.ReceiveTimeout = TimeSpan.MaxValue;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.CrossDomainScriptAccessEnabled = true; // TODO: Remove this?
var endpoint = Host.AddServiceEndpoint(typeof(HttpService), binding, "");
Host.Open();

我的 HttpService 类有这个方法:

[OperationContract, WebInvoke(Method = "*")]
public string Evaluate(string query)
{
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");

    if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
    {
        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, PUT, DELETE");
        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Max-Age", "1728000");
        return "";
    }
    else
    {
        try
        {
            return "12345";
        }
        catch (ProcessNotFoundException ex)
        {
            throw new WebFaultException<string>(ex.Message, System.Net.HttpStatusCode.NotFound);
        }
        catch (Exception ex)
        {
            throw new WebFaultException<string>(ex.Message, System.Net.HttpStatusCode.InternalServerError);
        }
    }
}

我在上面添加了 CORS 内容,以便任何人都可以使用 JavaScript 从任何网站使用此 API。我正在尝试使用fetch 运行一些 POST 请求,因为一旦它起作用,我将传递大量数据,因此 GET 方法将不起作用。从 JavaScript 执行以下操作可以正常工作:

fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '1232'});

当我这样做时,我的 Evaluate 方法首先使用 OPTIONS 调用,然后使用 POST 调用,并返回一个没有错误的 XML 字符串。以下也有效(注意正文中的引号):

fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '"hello world"'});

由于两者都是有效的 JSON,我认为任何 JSON 字符串都可以,所以我这样做了,但我收到了 400 Bad Request 错误:

fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '[10]'});

我认为它可能需要一个可以转换为我的方法参数的 JSON,但这也不起作用(给我一个 400 错误):

fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: JSON.stringify({ query: "hey" })})

所以它可以接受数字和字符串,但没有别的,我不知道为什么。我尝试使用所有这些其他属性组合并得到完全相同的结果:

[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Wrapped)]

我只是希望能够发送任何字符串,而不需要对其进行调查并决定它是好是坏,同时还能够执行上面的 CORS 操作。有没有办法做到这一点?

【问题讨论】:

  • 试试[OperationContract, WebInvoke(Method = "*", RequestFormat = WebMessageFormat.Json)] ` ... 虽然我不确定Method = "*" 是否有效
  • @JaromandaX 以完全相同的结果进行了尝试。我想我应该得到一个 Stream 而不是一个字符串(见下面我的答案)

标签: javascript c# wcf post


【解决方案1】:

我找到了一种解决方法,但我仍然不确定为什么它让我传递数字和字符串而不是数组,所以如果有人想出一个实际的解释,我很乐意接受它作为答案。我的解决方案是在我的 Evaluate 方法中使用 Stream 而不是字符串,然后像这样读取流:

using (var memoryStream = new MemoryStream())
{
    query.CopyTo(memoryStream);
    var array = memoryStream.ToArray();
    var lole = Encoding.UTF8.GetString(array);
}

我认为这个想法是,因为我正在做 CORS 的事情并且浏览器在发送 OPTIONS 请求时发送一​​个空正文,WCF 无法将其作为 JSON 处理,所以我必须自己处理所有事情以及通过采取战争流来做到这一点。仍然不知道为什么它让我发送数字和字符串(顺便说一下布尔值)但没有数组(或对象)。

【讨论】:

    【解决方案2】:

    首先,下面的定义只支持使用request body来接受参数,而不是URL Params。

    [OperationContract, WebInvoke(Method = "*")]
    public string Evaluate(string query)
    

    而且,参数的body样式默认是裸露的。 BodyStyle =WebMessageBodyStyle.Bare 这意味着服务器接受字符串而不包装参数的名称。

    正文:'1232'
    正文:'“你好世界”'

    当我们手动将值切换为BodyStyle=WebMessageBodyStyle.Wrapped 时,服务器将接受以下表单。基本上,您的尝试是正确的。这里只是一个小问题。

    {"value":"abcd"}
    

    这是我的例子,希望对你有所帮助。
    服务器合同。

    [OperationContract]
            [WebInvoke(Method ="*",BodyStyle =WebMessageBodyStyle.Wrapped,ResponseFormat =WebMessageFormat.Json)]
            string GetData(string value);
    


    JS
     <script>
            var para1 = { "value": "abcdefg" };
            $.ajax({
                type: "POST",
                url: "http://localhost:8864/Service1.svc/getdata",
                data: JSON.stringify(para1),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: OnSuccessCall,
                error: OnErrorCall
            });
            function OnSuccessCall(response) {
                console.log(response);
            }
            function OnErrorCall(response) {
                console.log(response.status + " " + response.statusText);
            }
    </script>
    

    关于处理 CORS 问题,我添加了一个额外的 global.asax 文件。

    protected void Application_BeginRequest(object sender, EventArgs e)
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
                if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
                {
                    HttpContext.Current.Response.AddHeader("Cache-Control", "no-cache");
                    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
                    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With,Accept");
                    HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
                    HttpContext.Current.Response.End();
                }
            }
    

    这是一个相关文档。
    https://docs.microsoft.com/en-us/dotnet/api/system.servicemodel.web.webmessagebodystyle?view=netframework-4.8
    如果有什么我可以帮忙的,请随时告诉我。

    【讨论】:

    • 谢谢。将 asax 文件添加到 win forms 应用程序是否可以工作,或者这些文件是否仅适用于某些应用程序类型,例如 ASP?从未使用过 asax 文件,所以不知道它的作用。
    • 是的,它只适用于 Aspnet。对于 Winform 应用程序,我们可以使用 IDispatchMessageinspector 接口添加自定义 Http 头。
    猜你喜欢
    • 1970-01-01
    • 2014-08-22
    • 2019-06-01
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    相关资源
    最近更新 更多