web-dev-qa-db-fra.com

Angular Publication sur une API Web .net

J'essaie d'exécuter un POST de mon application angular sur une instance d'API Web .net, mais le serveur renvoie null.

serveur  

    [HttpPost] 
    public string callBcknd([FromBody]string body)
    {
        try
        {
            Log.Info(string.Format("{0}", body));

        }
        catch(Exception ex)
        {
            return "error";
        }
    }
}

angular * Notez que j'utilise HttpClient au lieu de Http .. ne sais pas si c'est aussi le problème

callServer(){
    var test = { "name": "John" }
    let data = JSON.stringify(test);
    let headers = new HttpHeaders(); 
    headers.set('Content-Type', 'application/json');
    this.appService.http.post('http://localhost:3000/api/WebApI/callBcknd', 
                          test, 
                          {headers: headers})
  .subscribe(data => {console.log(data);}}}

config

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new {action = "GET", id = RouteParameter.Optional}
            );
        }
    }

Avec le réglage ci-dessus, je ne génère aucune erreur de serveur 404 côté client (en vérifiant la console de chrome), mais renvoie null en backend. Mais comme j'ai essayé avec Postman, il envoie des valeurs correctement avec la même URL. Si je n'inclue pas [FromBody] à l'intérieur de la méthode dans le backend, j'obtiens une erreur de serveur 404 du côté client. De plus, les messages indiquent "AUCUNE ressource HTTP correspondant à l'URI de la demande" n'a été trouvée. Une question similaire à celle-ci semble résoudre le problème en ayant [FromBody] mais je reçois toujours un null ... J'ai aussi suspecté que mon fichier de configuration Web (pas celui ci-dessus) devrait contenir des en-têtes, donc quand j'ai ajouté des en-têtes comme content tapez pour être json et etc alors je reçois une erreur de 500 serveur du côté client. À ce stade, je suis vraiment confus et je ne sais pas trop quoi faire. 

UPDATE1

Après le code serveur, le message est renvoyé mais le corps est toujours nul. Aucune erreur n'a été observée. 

[HttpPost]
        public IHttpActionResult Callbcknd([FromBody] string body)
        {
            try
            {
                Log.Info(string.Format("called with data {0}", body));

                return Ok(new { Message = "It worked!" });
            }

            catch(Exception ex)
            {
                return base.Content(HttpStatusCode.InternalServerError, ex.ToString());
            }
        }
2
Zlatko Loa

Je vois plusieurs raisons pour lesquelles vous obtiendriez des erreurs inattendues et des valeurs NULL dans votre code:

  1. (erreur) Votre méthode .net callBcknd ne devrait même pas être compilée car elle ne peut renvoyer quelque chose que s'il existe une exception.
  2. (erreur) Lors de l’envoi de données à votre contrôleur api, vous devez envoyer le message json et la méthode du contrôleur api doivent accepter un objet complexe et pas un type primitif comme string/int/bool etc.
  3. (avertissement) Votre service d'angle doit exposer les fonctionnalités et renvoyer des éléments observables ou des promesses auxquelles le composant peut ensuite s'abonner. N'exposez pas la HttpClient directement.
  4. (avertissement) Votre API Web doit renvoyer l'interface IHttpActionResult au lieu du type directement. Ensuite, vous pouvez utiliser les méthodes intégrées telles que Ok, Content et BadRequest pour renvoyer des informations d'état avec des données. Voir aussi Résultats de l'action dans Web API 2
  5. (suggestion) Utilisez Route et RoutePrefix en tant qu'attributs au lieu de vous fier à la configuration de route. Ceci est plus flexible et vous permettra également de spécifier les paramètres à inclure dans l'URL, ce qui permettra une conception plus RESTful. Voir aussi Routage d'attributs dans ASP.NET Web API 2
  6. (suggestion) Add CamelCasePropertyNamesContractResolver à résoudre entre boel Pascal et boîtier entre votre front end et backend. Voir aussi Sérialisation à l'aide de ContractResolver

C'est un bon exemple de la façon d'appeler une API Web et de structurer votre code.

Notez que ces exemples de code ne montrent que les parties pertinentes ajoutées ou modifiées}

WebApiConfig.cs

public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {
        // add this to ensure that casing is converted between camel case (front end) and Pascal case (c#/backend)
        var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        config.MapHttpAttributeRoutes();
    }
}

ApiModel.cs

public class ApiModel {
    public string Content {get;set;}
}

WebApIController.cs

[RoutePrefix("api/WebApI")]
public class WebApIController : ApiController {

    [HttpPost] 
    [Route("callBcknd")]
    public IHttpActionResult CallBcknd([FromBody] ApiModel body)
    {
        try
        {
            Log.Info(string.Format("{0}", body.Content));
            return Ok(new {Message = "It worked!"});
        }
        catch(Exception ex)
        {
            // example of how to return error with content. I would not recommend actually returning the exception details to the client in a production setting
            return base.Content(HttpStatusCode.InternalServerError, ex.ToString());
        }
    }
}

application.service.ts

constructor(private httpClient: HttpClient){}

callServer(data: {content: string}) : Observable<any> {
    return this.httpClient.post('http://localhost:3000/api/WebApI/callBcknd', data);
}

application.component.ts

constructor(private myService: MyService){}

onDoSomething(){
    this.myService.callServer({content: 'This is what I have sent'})
        .subscribe(data => console.log("Succeeded, result = " + data), (err)=> console.error("Failed! " + err));
}

Notez ce qui suit:

  1. ApiModel représente l'objet entrant dans la demande. L'appel angulaire envoie ensuite {content: 'This is what I have sent'} qui reflète ce type.
  2. IHttpActionResult est le type de réponse pour votre méthode API Web
  3. Vous pouvez renvoyer différents types avec des informations de statut dans la méthode CallBcknd
  4. Route et RoutePrefix ont été ajoutés pour donner plus de contrôle sur le chemin uri.
  5. Le composant angulaire et le service ont été scindés en 2 méthodes, le service renvoie un observable et le composant appelle les méthodes de service et souscrit à l'observateur renvoyant. Lorsque vous développez cet exemple, vous souhaitez remplacer any par les résultats attendus définis à l'aide d'interfaces. Il en va de même pour tous les paramètres entrants que vous souhaitez envoyer.
3
Igor

Un appel typique à une API de "Angular"

update(data: string): Observable<IResponse> {
    console.log(data);
    let url = '...';
    let headers = new Headers({
        'Content-Type': 'application/json; charset=utf-8',
    });
    let options = new RequestOptions({ headers: headers })

    return this._http.post(url, data, options)
        .map((res: any) => {
            return res.json();
        })
        .catch(this.handleError);

}

Le code dans l'API

[HttpPost] 
public string callBcknd([FromBody]string body)
{
   try
   {
      Log.Info(string.Format("{0}", body));
      //You must return something
      return "Post Realized";
   }
   catch(Exception ex)
   {
      return "error";
   }
}

//I like call async


[HttpPost] 

public async Task<IActionResult>callBcknd([FromBody]string body)
{
   try
   {
      Log.Info(string.Format("{0}", body));
      //await "some action"
      //You can return OK("success") or an object
      return Ok(new { success = true, description = "callback sucesfully" });;
   }
   catch(Exception ex)
   {
            //You can return OK("error") or an object
      return Ok(new { success = false, description = ex.InnerException });;
   }
}
1
Eliseo

Eh bien, ce que vous publiez ressemblerait à quelque chose comme

{"body":{// something here }}

Attendu que votre contrôleur attend:

"valuehere" (qui est un json valide pour la chaîne).

Vous devez changer le code c # pour avoir un modèle pour votre DTO:

public class PostedObject{
 public object Data {get;set;}
}

  [HttpPost] 
    public string callBcknd([FromBody]PostedObject body)
    {
    // do things

}
0
zaitsman