【问题标题】:POST string to ASP.NET Web Api application - returns nullPOST 字符串到 ASP.NET Web Api 应用程序 - 返回 null
【发布时间】:2012-11-26 02:24:31
【问题描述】:

我正在尝试将字符串从客户端传输到 ASP.NET MVC4 应用程序。

但是我接收不到字符串,要么是空,要么就是找不到post方法(404错误)

传输字符串的客户端代码(控制台应用程序):

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:49032/api/test");
request.Credentials = new NetworkCredential("user", "pw");
request.Method = "POST";
string postData = "Short test...";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;

Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();

WebResponse response = request.GetResponse();
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
dataStream = response.GetResponseStream();

StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
reader.Close();
dataStream.Close();
response.Close();
Console.ReadLine();

ASP.NET Web API 控制器:

public class TestController : ApiController
{
    [Authorize]
    public String Post(byte[] value)
    {
        return value.Length.ToString();
    }
}

在这种情况下,我可以调用“Post”方法,但“value”是NULL。 如果我将方法签名更改为(字符串值),它将永远不会被调用。

即使“没有”[Authorize] 设置,它也有同样奇怪的行为。 -> 所以它与用户认证无关。

任何想法我做错了什么? 感谢您的帮助。

【问题讨论】:

    标签: c# asp.net asp.net-mvc post asp.net-web-api


    【解决方案1】:

    您似乎在您的 Web API 控制器操作中使用了一些 [Authorize] 属性,我看不出这与您的问题有什么关系。

    那么,让我们开始实践吧。下面是一个简单的 Web API 控制器的样子:

    public class TestController : ApiController
    {
        public string Post([FromBody] string value)
        {
            return value;
        }
    }
    

    还有一个消费者:

    class Program
    {
        static void Main()
        {
            using (var client = new WebClient())
            {
                client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
                var data = "=Short test...";
                var result = client.UploadString("http://localhost:52996/api/test", "POST", data);
                Console.WriteLine(result);
            }
        }
    }
    

    您无疑会注意到 Web API 控制器属性的 [FromBody] 装饰以及客户端 POST 数据的 = 前缀。我建议您阅读 how does the Web API does parameter binding 以更好地理解这些概念。

    [Authorize] 属性而言,这可用于保护服务器上的某些操作,使其仅对经过身份验证的用户可访问。实际上,您在这里要达到的目标还不清楚。顺便说一下,您应该在问题中更清楚地说明这一点。您是在尝试了解参数绑定在 ASP.NET Web API 中的工作原理(如果这是您的目标,请阅读我链接到的文章)还是尝试进行一些身份验证和/或授权?如果第二个是您的情况,您可能会发现我在此主题上写的 following post 很有趣,可以帮助您入门。

    如果在阅读了我链接到的材料后,你和我一样对自己说,WTF 伙计,我需要做的就是将一个字符串发布到服务器端端点,我需要做所有这些吗?没门。然后结帐ServiceStack。您将有一个与 Web API 进行比较的良好基础。我不知道微软的那些家伙在设计 Web API 时在想什么,但是说真的,我们应该为我们的 HTML(想想 Razor)和 REST 的东西有单独的基本控制器吗?这不会是严重的。

    【讨论】:

    • 非常有趣,如果我使用您的客户端代码,它可以工作。它适用于 var data = "=...";如果我删除“=”它不起作用。
    • @user1011394,是的,这是一个 WTF!结帐ServiceStack。你会喜欢的。
    • @DarrelMiller ServiceStack 在 HTTP 方面做得很好(不知道是什么让你不这么想),你可以Customize the HTTP Response 随便你,return any Response you wish 和它的HTML Support is implemented as it should be in a Web Fx 即 HTML 只是另一个内容-类型。不共享基类不是好的设计,这是一种自我强加的人为限制。 SS 使用相同的接口执行 HTML + Self-Host。
    • @DarrelMiller 应用程序开发人员可能使用的所有内容都有常量。如果您指的是所有 HTTP 动词的哈希集,那是框架用来确定符号是否是 HTTP 方法的最佳存储。
    【解决方案2】:

    如果您接受您正在使用 HTTP 的事实,Web API 会非常好地工作。当您开始尝试假装您正在通过电线发送对象时,它开始变得混乱。

     public class TextController : ApiController
        {
            public HttpResponseMessage Post(HttpRequestMessage request) {
    
                var someText = request.Content.ReadAsStringAsync().Result;
                return new HttpResponseMessage() {Content = new StringContent(someText)};
    
            }
    
        }
    

    此控制器将处理 HTTP 请求,从负载中读取一个字符串并返回该字符串。

    您可以使用 HttpClient 通过传递 StringContent 的实例来调用它。 StringContent 将默认使用 text/plain 作为媒体类型。这正是您想要通过的。

        [Fact]
        public void PostAString()
        {
    
            var client = new HttpClient();
    
            var content = new StringContent("Some text");
            var response = client.PostAsync("http://oak:9999/api/text", content).Result;
    
            Assert.Equal("Some text",response.Content.ReadAsStringAsync().Result);
    
        }
    

    【讨论】:

    • +1 - 我喜欢 Web API。我今天刚刚了解了HttpResponseMessage,以及如何绕过内容协商和序列化手动创建响应。我想知道我是否可以为请求做类似的事情。瞧,它就在这里!
    • "接受你正在使用 HTTP 的事实" 喜欢它。 :)
    • 这是最好的答案,因为它不需要客户端对发布的字符串做任何奇怪的事情。
    【解决方案3】:

    对于 WebAPI,这里是无需通过特殊的 [FromBody] 绑定即可检索正文文本的代码。

    public class YourController : ApiController
    {
        [HttpPost]
        public HttpResponseMessage Post()
        {
            string bodyText = this.Request.Content.ReadAsStringAsync().Result;
            //more code here...
        }
    }
    

    【讨论】:

      【解决方案4】:

      我使用此代码发布 HttpRequest。

      /// <summary>
              /// Post this message.
              /// </summary>
              /// <param name="url">URL of the document.</param>
              /// <param name="bytes">The bytes.</param>
              public T Post<T>(string url, byte[] bytes)
          {
              T item;
              var request = WritePost(url, bytes);
      
              using (var response = request.GetResponse() as HttpWebResponse)
              {
                  item = DeserializeResponse<T>(response);
                  response.Close();
              }
      
              return item;
          }
      
          /// <summary>
          /// Writes the post.
          /// </summary>
          /// <param name="url">The URL.</param>
          /// <param name="bytes">The bytes.</param>
          /// <returns></returns>
          private static HttpWebRequest WritePost(string url, byte[] bytes)
          {
              ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
      
              HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
              Stream stream = null;
              try
              {
                  request.Headers.Clear();
                  request.PreAuthenticate = true;
                  request.Connection = null;
                  request.Expect = null;
                  request.KeepAlive = false;
                  request.ContentLength = bytes.Length;
                  request.Timeout = -1;
                  request.Method = "POST";
                  stream = request.GetRequestStream();
                  stream.Write(bytes, 0, bytes.Length);
              }
              catch (Exception e)
              {
                  GetErrorResponse(url, e);
              }
              finally
              {
                  if (stream != null)
                  {
                      stream.Flush();
                      stream.Close();
                  }
              }
              return request;
          }
      

      关于你的代码,试试不带 content.Type (request.ContentType = "application/x-www-form-urlencoded";)

      更新

      我认为问题在于您尝试检索值的方式。当您执行 POST 并通过 Stream 发送字节时,它们不会作为参数传递到操作中。您需要通过服务器上的流检索字节。

      在服务器上,尝试从流中获取字节。以下代码是我使用的。

           /// <summary> Gets the body. </summary>
           /// <returns> The body. </returns>
           protected byte[] GetBytes()
           {
             byte[] bytes;
              using (var binaryReader = new BinaryReader(Request.InputStream))
              {
                  bytes = binaryReader.ReadBytes(Request.ContentLength);
              }
      
               return bytes;
           }
      

      【讨论】:

      • 嗨查克,谢谢你的回答。我尝试过这个。但结果与我的代码相同。在服务器端执行“Post”方法,但值为 NULL。
      • 您在服务器端检索 InputStream 的建议对我也不起作用...我们是否还有其他方法可以仅检索字符串而不是 byte[] - 这就是我所需要的?
      • 我不确定还有什么建议。上面的代码,是我们用来完成你想要达到的目标的代码。
      【解决方案5】:

      Darrel 的回答当然是正确的。要添加的一件事是,尝试绑定到包含单个标记(如“hello”)的主体的原因。

      是它不是完全 URL 形式的编码数据。通过像这样在前面添加“=”:

      =hello
      

      它变成了单个键值对的 URL 形式编码,其名称为空,值为“hello”。

      不过,更好的解决方案是在上传字符串时使用application/json:

      POST /api/sample HTTP/1.1
      Content-Type: application/json; charset=utf-8
      Host: host:8080
      Content-Length: 7
      
      "Hello"
      

      使用 HttpClient 你可以这样做:

      HttpClient client = new HttpClient();
      HttpResponseMessage response = await client.PostAsJsonAsync(_baseAddress + "api/json", "Hello");
      string result = await response.Content.ReadAsStringAsync();
      Console.WriteLine(result);
      

      亨里克

      【讨论】:

      • 这对我不起作用,最终我的参数为空。不得不使用表单数据方法。感觉真的很hacky:/
      【解决方案6】:

      我遇到了这个问题,找到这篇文章。 http://www.jasonwatmore.com/post/2014/04/18/Post-a-simple-string-value-from-AngularJS-to-NET-Web-API.aspx

      我找到的解决方案是简单地将字符串值包装成双精度 在你的 js 帖子中引用

      像魅力一样工作!仅供参考

      【讨论】:

      • 这对我有用,但请查看文章的顶部评论以了解“为什么”。简而言之,如果您使用 AngularJS,JSON.stringify(value) 会更好。
      【解决方案7】:
      ([FromBody] IDictionary<string,object> data)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-14
        • 2017-08-16
        • 2018-11-09
        • 1970-01-01
        • 2019-01-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多