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());
}
}
Je vois plusieurs raisons pour lesquelles vous obtiendriez des erreurs inattendues et des valeurs NULL dans votre code:
callBcknd
ne devrait même pas être compilée car elle ne peut renvoyer quelque chose que s'il existe une exception.string
/int
/bool
etc.HttpClient
directement.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 2Route
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 2C'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:
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.IHttpActionResult
est le type de réponse pour votre méthode API WebCallBcknd
Route
et RoutePrefix
ont été ajoutés pour donner plus de contrôle sur le chemin uri.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.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 });;
}
}
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
}