web-dev-qa-db-fra.com

POST) chaîne vers l'application ASP.NET Web Api - renvoie la valeur null

J'essaie de transmettre une chaîne du client à l'application ASP.NET MVC4.

Mais je ne peux pas recevoir la chaîne, elle est nulle ou la méthode post est introuvable (erreur 404)

Code client pour transmettre la chaîne (application console):

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();

Contrôleur d’API Web ASP.NET:

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

Dans ce cas, je peux appeler la méthode "Post", mais "valeur" est NULL. Si je modifie la signature de la méthode en (valeur de chaîne), elle ne sera jamais appelée.

Même "sans" le réglage [Autoriser], il a le même comportement étrange. -> Donc, cela n'a rien à voir avec l'authentification de l'utilisateur.

Des idées que je fais mal? Je suis reconnaissant pour toute aide.

40
user1011394

Vous semblez avoir utilisé un attribut [Authorize] Dans votre action de contrôleur API Web et je ne vois pas en quoi cela est pertinent pour votre question.

Alors, passons à la pratique. Voici à quoi pourrait ressembler un contrôleur API Web trivial:

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

et un consommateur d'ailleurs:

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);
        }
    }
}

Vous remarquerez sans aucun doute la décoration de [FromBody] de l'attribut du contrôleur API Web ainsi que le préfixe = De POST data om Je vous recommande de lire à propos de comment l’API Web lie-t-elle les paramètres de liaison pour mieux comprendre les concepts.

En ce qui concerne l'attribut [Authorize], Il pourrait être utilisé pour empêcher que certaines actions sur votre serveur ne soient accessibles qu'aux utilisateurs authentifiés. En fait, ce que vous essayez de faire ici n’est pas très clair. Vous auriez dû préciser cela plus clairement dans votre question. Êtes-vous en train d'essayer de comprendre le fonctionnement de la liaison de paramètres dans l'API Web ASP.NET (veuillez lire l'article sur lequel je suis lié si tel est votre objectif) ou tentez-vous d'effectuer une authentification et/ou une autorisation? Si le second cas est votre cas, le following post que j'ai écrit sur ce sujet est intéressant pour vous aider à démarrer.

Et si, après avoir lu les documents auxquels je me suis associé, vous êtes comme moi et vous vous dites, homme de WTF, tout ce que je dois faire est POST une chaîne vers un noeud final côté serveur et j'ai besoin de Pour faire tout cela? Pas moyen, puis payer ServiceStack . Vous aurez une bonne base de comparaison avec l'API Web. Je ne sais pas à quoi les gars de Microsoft pensaient lors de la conception de l'API Web. , mais bon, sérieusement, nous devrions avoir des contrôleurs de base séparés pour nos trucs HTML (pensez Razor) et REST? Cela ne peut pas être grave.

57
Darin Dimitrov

La Web API fonctionne très bien si vous acceptez le fait que vous utilisez HTTP. C'est lorsque vous commencez à prétendre que vous envoyez des objets par le fil que cela commence à devenir désordonné.

 public class TextController : ApiController
    {
        public HttpResponseMessage Post(HttpRequestMessage request) {

            var someText = request.Content.ReadAsStringAsync().Result;
            return new HttpResponseMessage() {Content = new StringContent(someText)};

        }

    }

Ce contrôleur gérera une requête HTTP, lira une chaîne dans la charge utile et renverra cette chaîne.

Vous pouvez utiliser HttpClient pour l'appeler en transmettant une instance de StringContent. StringContent utilisera par défaut text/plain comme type de support. Ce qui est exactement ce que vous essayez de passer.

    [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);

    }
35
Darrel Miller

Darrel a bien sûr raison dans sa réponse. Une chose à ajouter est que la raison pour laquelle tenter de se lier à un corps contenant un seul jeton, tel que "hello".

est-ce que ce n'est pas tout à fait les données encodées sous forme d'URL En ajoutant “=” devant comme ceci:

=hello

il devient un codage de forme d'URL d'une paire clé-valeur unique avec un nom vide et la valeur "hello".

Cependant, une meilleure solution consiste à utiliser application/json lors du téléchargement d'une chaîne:

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

"Hello"

En utilisant HttpClient, vous pouvez le faire comme suit:

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

Henrik

3

J'utilise ce code pour publier HttpRequests.

/// <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;
    }

En ce qui concerne votre code, essayez-le sans le content.Type (request.ContentType = "application/x-www-form-urlencoded";)

mise à jour

Je crois que le problème réside dans la façon dont vous essayez de récupérer la valeur. Lorsque vous effectuez un POST et envoyez des octets via le flux, ils ne sont pas transmis à l'action en tant que paramètre. Vous devez récupérer les octets via le flux sur le serveur.

Sur le serveur, essayez d’obtenir les octets du flux. Le code suivant est ce que j'utilise.

     /// <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;
     }
3
Chuck Conway

Pour WebAPI, voici le code permettant de récupérer le corps du texte sans passer par leur liaison spéciale [FromBody].

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

je rencontre ce problème et trouve cet article. http://www.jasonwatmore.com/post/2014/04/18/Post-a-simple-string-value-from-AngularJS-to-NET-Web-API.aspx

La solution que j'ai trouvée consistait simplement à envelopper la valeur de chaîne entre guillemets dans votre message js

fonctionne comme un charme! FYI

2
Bill.Zhuang