从 ASP.NET 核心 API 端点获取不受支持的媒体类型的 HttpCode 415 错误

Getting HttpCode 415 error of Unsupported media type from ASP.NET Core API endpoint

我正在尝试从反应代码调用 asp.net 核心 5 API,但我一直从服务器收到错误 415。

这是前端尝试调用的我的服务器端点

    public class OauthToken
    {
        public string TokenId;
    }

    [AllowAnonymous]
    [HttpPost("signin-google")]
    [Consumes("application/json")]
    public async Task<IActionResult> GoogleLogin(OauthToken userView)
    {
     ....
    }

前端代码如下:

   const googleResponse = (response) => {       
   
    const options = {
      method: 'POST',
      body: { TokenId: response.tokenId },
      mode: 'no-cors',  
      accepts: "application/json",         
      cache: 'default',
      contentType : "application/json",
    }

    fetch(config.GOOGLE_AUTH_CALLBACK_URL, options)
      .then(r => {
        console.log(r)      
    })
    .catch(e=>{
        console.log(e)
    })
      
  }

下面是我的startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddDatabaseDeveloperPageExceptionFilter();

        services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
            .AddRoles<ApplicationRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

        services.AddCors(opts =>
        {
            opts.AddPolicy("AllowAll", builder =>
            {
                builder.AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader();
                //.AllowCredentials();
            });
        });

        services.AddAuthentication()
            .AddIdentityServerJwt()
            .AddJwtBearer(cfg =>
            {
                cfg.RequireHttpsMetadata = false;
                cfg.SaveToken = true;

                cfg.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AppSettings:JwtSecret"])),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            })
            .AddGoogle(opt =>
            {
                opt.ClientId = "MY_CLIENT_ID";
                opt.ClientSecret = "MY_CLIENT_SECRET";
                opt.SignInScheme = IdentityConstants.ExternalScheme;
            })
            //.AddTwitter(twitterOptions => { })
            .AddFacebook(facebookOptions => {
                facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
                facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
            });
        services.AddControllersWithViews();
        services.AddRazorPages();

        // In production, the React files will be served from this directory
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/build";
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseMigrationsEndPoint();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseSpaStaticFiles();

        app.UseRouting();
        app.UseCors("AllowAll");
        app.UseAuthentication();
        app.UseIdentityServer();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "/api/v1/{controller}/{action=Index}/{id?}");
            endpoints.MapRazorPages();
        });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseReactDevelopmentServer(npmScript: "start");
            }
        });
    }

我不知道问题出在哪里。 请帮我解决这个问题。 谢谢。

根据您的评论,我已经测试并重现了您的问题,您可以在下面查看:

Reproduced Issue:

How do I call the endpoint to get the data get to it?:

如果你可以查看你的 client-side codeHTTP Verb 它应该在 FromBody 内发送请求但是你没有那样发送,所以你有两种方法可以实现那:

Way 1 : Set [FromBody] On Method:

[AllowAnonymous]
[HttpPost("signin-google")]
[Consumes("application/json")]
public async Task<IActionResult> GoogleLogin([FromBody] OauthToken userView)
 {
    return Ok();
 }

Output:

Way 2 : Set TokenId as string on Method:

[AllowAnonymous]
[HttpPost("signin-google")]
[Consumes("application/json")]
public async Task<IActionResult> GoogleLogin(string TokenId)
  {
     return Ok();
  }

Output:

Note: So you could try above steps to call your API endpoint accordingly. I noticed that problem was in API routing and method argurment additionally, I would suggest you to have a look on our offical docs for indepth insight here

希望它能按预期为您提供指导,帮助您解决问题。

经过以下更改后,我终于让代码正常工作了:

  1. 我注意到OathTokenclass的TokenId属性没有getter也没有setter。所以,我更新如下:

     public class OauthToken
     {
         //added {get; set;}
         public string TokenId { get; set; }
     }
    
  2. 将提取请求的主体从对象更改为 blob,如下所示:

    const tokenBlob = new Blob([JSON.stringify({ TokenId: response.tokenId }, null, 2)], { type: 'application/json' });
    
  3. 将请求的模式从“no-cors”更改为“cors”,因为 Cors 已经在项目startup.cs class 中声明

因此,更新后的工作提取请求如下:

        const googleResponse = (response) => {       
    
    const tokenBlob = new Blob([JSON.stringify({ TokenId: response.tokenId }, null, 2)], { type: 'application/json' });
    const options = {
      method: 'POST',
      body: tokenBlob,
      mode: 'cors',  
      accepts: "application/json",         
      cache: 'default',
      contentType : "application/json",
    }

   

    fetch(config.GOOGLE_AUTH_CALLBACK_URL, options)
      .then(r => {
        r.json().then(user => {
           
            console.log(user.tokenId);
          });
    })      
    .catch(e=>{
        console.log(e)
    })
      
  }

端点处的代码如下:

public class OauthToken
    {
        public string TokenId { get; set; }
    }

    [AllowAnonymous]
    [Consumes("application/json")]
    [HttpPost("signin-google")]
    public async Task<IActionResult> GoogleLogin([FromBody] OauthToken clientToken)
    {
        return Ok(clientToken);
    }

我想这可能会对面临此类问题的其他人有所帮助。 谢谢