4
votes

HttpGet renvoie une erreur 500 sur vuejs mais fonctionne avec postman

J'ai un client qui consomme mon API. J'ai un point de terminaison sur l'API comme ceci:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   app.UseRouting();
   app.UseCors("AllowAll");
   app.UseAuthentication();
   app.UseAuthorization();
   app.UseEndpoints(endpoints =>
   {
       endpoints.MapControllers();
       endpoints.MapDefaultControllerRoute();
   });
}

Mon application client Vue.js appelle cette action comme ceci:

async getAccount(): Promise<Account> {
  const result = await this.axios.get(`${this.apiUrl}/account`);
  return this.jsonConvert.deserializeObject(result.data, Account);
}

Chaque fois que cela s'appelle, j'obtiens une erreur 500. J'ai mis un point d'arrêt sur la ligne de départ de l'action, mais il n'est pas appelé. Mais quand j'appelle cette même action via Postman, cela fonctionne. Il n'y a rien de différent dans les 2 demandes.

Cependant si je change le [HttpGet] en [HttpGet("test") et change le client vue.js pour await this.axios.get( $ {this.apiUrl} / account / test ); cela fonctionne et atteint le point d'arrêt.

Quelqu'un peut-il expliquer ce que je fais de mal ici?

ÉDITER:

Voici ce qui se trouve dans l'inspecteur de mon navigateur, comme vous pouvez le voir, il n'y a aucune information utile ici. Il dit simplement 500 erreur interne sans réponse:

entrez la description de l'image ici

Voici la méthode de configuration de mon fichier Startup.cs:

[Route("api/account")]
[ApiController]
public class AccountController : BaseController
{
    private readonly IIdentityProvider _identityProvider;

    private string UserId 
    { 
        get
        {
           return User?.FindFirst("AspNetUserId")?.Value;
        }
    }

    public AccountController(IIdentityProvider identityProvider)
    {
        _identityProvider = identityProvider;
    }

    [HttpGet]
    public IActionResult GetAccount()
    {
       var user = _identityProvider.GetByAspNetUserId(UserId);
       var account = new Account
       {
          UserName = user.UserName,
          Email = user.Email,
          EmailConfirmed = user.EmailConfirmed,
          PhoneNumber = user.PhoneNumber,
          PhoneNumberConfirmed = user.PhoneNumberConfirmed,
          TwoFactorEnabled = user.TwoFactorEnabled,
          SmsTwoFactorEnabled = user.SmsTwoFactorEnabled
       };

       return Ok(account);
     }
}


26 commentaires

500 signifie que le serveur plante sur quelque chose. Avez-vous regardé dans la console de développement de votre navigateur pour voir ce qui se passe sur la demande réseau? Le serveur devrait renvoyer les informations du crash s'il s'agit d'un développement local


la meilleure estimation est que votre application vue n'est pas authentifiée, mais vous avez configuré l'authentification dans postman ....


@KeithNicholas J'ai jeté un œil à la console et il n'y a pas d'erreurs. J'ai également oublié de mentionner qu'il n'y a pas de middleware d'authentification ou quelque sorte. À des fins de test, je peux appeler cela sans avoir besoin d'auth


pas la console, le réseau


L'onglet «Réponse» de l'inspecteur du navigateur contient-il des informations utiles? Habituellement, si vous êtes en débogage, il affichera l'exception pendant 500 secondes.


Il dit simplement "Impossible de charger les données de réponse" @BryanLewis


Pour aider à déterminer s'il s'agit d'une sorte de problème de routage étrange ou d'un bogue de code, pouvez-vous changer la méthode GetAccount () pour retourner simplement ok et rien d'autre ("return Ok ();") et voir si cela fonctionne. Sinon, il y a un étrange problème d'itinéraire par défaut.


J'aurais dû le mentionner, j'ai supprimé tout le code de la méthode et je viens de retourner Ok () mais cela lève toujours une exception 500 sans explication @BryanLewis


Puisque vous utilisez [ApiController], je suppose que vous utilisez .Net Core. Quelle version utilisez-vous? À quoi ressemble la méthode Configure dans votre Startup.cs? Utilise-t-il le "app.UseEndpoints" par défaut?


pouvez-vous essayer async getAccount (): Promise <Account> {const result = wait this.axios.get ( ${this.apiUrl}/account/GetAccount ); return this.jsonConvert.deserializeObject (result.data, compte); }


@BryanLewis J'utilise .NET Core 3.1 et j'ai ajouté la méthode configure dans la question.


@Nonik Cela ne fonctionne pas car l'API renvoie un 404


Je vois que vous héritez de "BaseController" et non de "ControllerBase" (qui est la base par défaut de l'API .Net Core 3.1). Utilisez-vous un contrôleur de base personnalisé? Si oui, peut-être avez-vous quelque chose là-dedans qui dérange? Pouvez-vous essayer de supprimer cela ou d'hériter de la ControllerBase par défaut? Ou est-ce un projet MVC?


Ce projet a-t-il été migré depuis une ancienne version de .Net Core ou s'agit-il d'un nouveau projet 3.1?


Quelle est l'URL de demande complète que vous utilisez dans Postman?


@BryanLewis Le BaseController hérite de ControllerBase, le BaseController a juste des méthodes utiles que je peux utiliser sur tous les contrôleurs qui en héritent


@BryanLewis Le projet a été créé à l'aide de .NET Core 3.1 et n'a pas été migré à partir d'une ancienne version


@jslowik L'URL de la demande est localhost: 65384 / api / account dans Postman. Ce que j'ai remarqué, c'est que si je clique sur la demande dans l'onglet réseau de la console pour la renvoyer, cela fonctionne et cela atteint les points d'arrêt dans l'action mais ne fonctionne pas via le client lorsque le client fait la demande


Essayez un appel et codez en dur ' localhost: 65384 / api / account ' au lieu de $ this.apiUrl juste pour vous assurer qu'il n'y a pas de bogue dans le code. Je testerais également une fois en commentant le code dans le constructeur et en descendant directement de la classe Controller.


Comment allez-vous obtenir une réponse avec un code incomplet? Où est la définition de UserId dans votre contrôleur? Je parierais la maison que cette ligne var user = _identityProvider.GetByAspNetUserId(UserId); lève l'exception, mais hélas, vous n'avez pas non plus publié le code dans cette méthode. C'est là que réside votre problème. Si seulement vous saviez comment définir des points d'arrêt et déboguer votre backend, vous auriez résolu cela sans prime.


Si cette ligne lançait l'exception, pourquoi n'a-t-elle pas atteint le point d'arrêt avant cette ligne juste au moment où l'action est appelée? Je parierais que si UserId n'existait pas, il y aurait des erreurs de compilation. Mais allez-y, vous semblez savoir quel est le problème, alors je publierai la propriété UserId :) @Andy


Si cela vous aide à mieux comprendre le problème .. J'ai supprimé tout le code de l'action et JUSTE retourner Ok (); comme mentionné précédemment dans ces commentaires et cela ne fonctionne toujours pas. Maintenant ... pouvons-nous contourner le transfert de propriété de cette maison sur laquelle vous vouliez parier @Andy


@RossBush J'ai essayé cela en supprimant apiUrl et cela ne fonctionne pas, ce qui ne m'étonne pas car la valeur était toujours rendue


Mais qu'en est-il des journaux de serveur? C'est votre propre API, l'erreur 500 devrait produire une exception ou quelque chose dans les journaux du serveur


Si vous pouvez déboguer l'application, je suggérerais d'activer toutes les exceptions CLR ( docs.microsoft.com/en-us/visualstudio/debugger/… ), jetez également un œil à la fenêtre de sortie pour des journaux détaillés sur tous les threads. Quelle différence voyez-vous dans les en-têtes de demande pour les deux demandes? Mon intuition dit que cela a à voir avec l'en-tête d'authentification dans la demande.


@KTOV Je viens de lancer une application WebApi dans VS, en utilisant votre classe AccountController et la méthode Configure , mais je ne peux pas reproduire le problème que vous avez mentionné (c'est-à-dire qu'il renvoie le résultat correct et aucune erreur ne se produit). Pouvez-vous fournir un lien GitHub vers un projet qui reproduit le problème?


4 Réponses :


3
votes

Ce n'est pas un échec de la requête GET mais de la requête OPTIONS. VueJS effectue un contrôle en amont pour les demandes. Donc, si votre point de terminaison a un verbe GET, lorsqu'il est appelé avec celui OPTIONS, il générera une erreur.

Vous avez 2 solutions:

  1. Solution simple, autorisez le verbe OPTIONS pour votre endpoin, vérifiez-le et renvoyez un 200.
  2. Vous pouvez vérifier la réponse à cette question et faire une solution similaire: problème cors avec vue et dotnet et implémenter une réponse automatique pour toutes les requêtes OPTIONS.

Pour une référence, vous pouvez lire https://docs.microsoft.com/it-it/aspnet/core/security/cors?view=aspnetcore-5.0#preflight-requests


14 commentaires

Cela montre dans la question que ce n'est pas une erreur CORS mais une erreur HTTP 500.


Sa demande fait une OPTIONS sur un point de terminaison GET. C'est un 500.


Si les options échouent, pourquoi y a-t-il une deuxième demande? Ça se serait arrêté après le premier, n'est-ce pas?


L'image montre une erreur 500 à la demande d'options. Sous l'info-bulle se trouve le libellé "OPTIONS". La chose étrange est qu'il semble que la demande d'options soit la deuxième ...


Vous êtes peut-être dans la bonne direction ici, je viens de comparer la méthode de requête à d'autres requêtes du client .. une autre requête get dans l'application client montre en fait que la requête est effectuée comme GET tandis que la requête de compte est OPTIONS


meh - si cela s'avère être une chose CORS, alors tout cela était vraiment une perte de temps car Chrome est très verbal à propos de ces erreurs.


@Et j'ai déjà rencontré ce genre d'erreurs, Chrome n'a jamais aidé plus de 500 sur les options ... Habituellement, Chrome se plaint des origines ou d'une autre erreur d'en-tête. J'ai utilisé divers framework FE et que les options échouées sont généralement dues au serveur se plaignant du verbe.


Ce que je ne comprends pas, c'est pourquoi la modification de l'action en [HttpGet("test")] et l'appel du point de terminaison fonctionnent-ils en conséquence? Pourquoi cela ne fonctionne-t-il pas sans itinéraire sur l'action?


@KTOV Je ne pense pas que le problème soit la route, mais le fait que vous forcez axios à faire directement une requête GET. Comme vous pouvez également le lire sur MS doc, vous pouvez créer un point de terminaison d'options pour correspondre à chaque demande en supprimant la route.


Mais j'ai forcé axios à faire une requête GET dans d'autres requêtes et cela fonctionne toujours @Michael


@KTOV, ok j'ai mal compris, eh bien ... en effet c'est un peu bizarre, pouvez-vous poster une autre demande de travail et son contrôleur sans l'itinéraire?


@KTOV oh ... est le AccountController que vous avez posté le contrôleur complet? ... ou y a-t-il d'autres terminaux à l'intérieur?


@Michael Oui, il y a plus de points de terminaison à l'intérieur. Il y a beaucoup d'actions dans le contrôleur, donc je ne voudrais pas inonder la question avec des choses qui peuvent être irellevantes, mais sur les 14 actions, 1 est un GET qui est celui avec lequel j'ai des problèmes, 1 est un PUT et le reste sont POST. Aucun d'entre eux n'est en conflit les uns avec les autres en termes de routage


@KTOV bien, sans plus d'informations, je ne peux pas dire la raison exacte pour laquelle donner à cette action une route résout le problème avec une requête GET ... Je peux vous donner une suggestion, importer et configurer swashbuckle pour être fanfaron côté développement, vous pouvez avoir une vue sur tous les terminaux et les essayer sans facteur. Peut-être que vous pouvez recevoir une erreur plus détaillée.



1
votes

Ce comportement peut être causé par un problème de routage qui peut être résolu en ajoutant une route vide sur votre méthode pour permettre une demande d'obtention faite à Route ("api / account").

    [Route("")]
    [HttpGet]
    public IActionResult GetAccount()
    {
       var user = _identityProvider.GetByAspNetUserId(UserId);
       var account = new Account
       {
          UserName = user.UserName,
          Email = user.Email,
          EmailConfirmed = user.EmailConfirmed,
          PhoneNumber = user.PhoneNumber,
          PhoneNumberConfirmed = user.PhoneNumberConfirmed,
          TwoFactorEnabled = user.TwoFactorEnabled,
          SmsTwoFactorEnabled = user.SmsTwoFactorEnabled
       };

       return Ok(account);
     }

Lorsque je débogue localement et que je tente d'obtenir un point de terminaison sans route vide sur une méthode, j'obtiens une erreur 404 sans que le point d'arrêt de la méthode ne frappe. L'ajout de l'itinéraire vide provoque l'atteinte du point d'arrêt.

En ce qui concerne le 500, votre application pourrait très bien avoir quelque chose causant un 500 au lieu d'un 404, comme une référence nulle dans un gestionnaire d'erreur personnalisé, ou votre classe de contrôleur de base ou une sorte d'erreur dans un filtre comme un filtre d'autorisation.

Votre débogueur devrait vous donner plus d'informations sur la raison exacte de l'erreur 500. Si vous hébergez dans IIS, vous pouvez activer le suivi des demandes pour les codes d'erreur supérieurs à 500.


0 commentaires

0
votes

Je vois que vous utilisez DI, avez-vous également enregistré l'interface IdentityProvider sur startup.cs . pour les problèmes de cors, vous devez d'abord ajouter les cors sur ConfigureServices comme ceci

app.UseRouting();
app.UseCors("CorsPolicy");

dans la méthode configure, ajoutez les cors avec cette politique comme ceci

services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowAnyOrigin());
        });


0 commentaires

0
votes

Je pense qu'il s'agit du routage de votre contrôleur. La route de votre contrôleur ( [Route("api/account")] ) signifie que lorsque vous souhaitez appeler ses actions, vous ne devez pas spécifier Action dans votre URL. AccountController peut une action pour chaque verbe HTTP (vous ne pouvez pas avoir plus d'un HttpGet dans AccountController). Si vous avez plus d'un HttpGet dans votre AccountController, modifiez son routage comme ceci:

const result = await this.axios.get(`${this.apiUrl}/account/getaccount`);

puis ajoutez le nom de l'action à l'URL dans la partie frontale:

[Route("api/[Controller]/[Action]")]


0 commentaires