ASP.NET Core 3.1 Метод публикации веб-API, вызывающий ошибку 405 «Метод не разрешен»

#c# #visual-studio #asp.net-core #asp.net-web-api

#c# #visual-studio #asp.net-core #asp.net-web-api

Вопрос:

Новичок здесь пытается создать ASP.NET Ядро 3.1 Веб-API впервые.

Мой GET метод работает нормально, но всякий раз, когда я пробую этот POST метод, я получаю ошибку 405 «Метод не разрешен». Я искал и искал, и большинство решений, похоже, сосредоточены на отключении WebDAV — web.config файла или использовании правильного пути маршрута.

Сейчас я работаю локально, пытаясь протестировать непосредственно из Visual Studio 2019, поэтому его не web.config существует. Я отправляю POST запрос с помощью Postman на URL http://localhost:58322/api/test/ -адрес, где порт 58322 является назначенным портом при запуске проекта.

Может кто-нибудь, пожалуйста, скажите мне, что я могу делать неправильно?

Вот мой код для TestController.cs :

 using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SimpleAPI.Models;

namespace SimpleAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly appDBContext _context;

        public TestController(appDBContext context)
        {
            _context = context;
        }

        // GET: api/Test
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Test>>> GetTest()
        {
            return await _context.Test.ToListAsync();
        }

        // GET: api/Test/5
        [HttpGet("{id}")]
        public async Task<ActionResult<Test>> GetTest(int id)
        {
            var test = await _context.Test.FindAsync(id);

            if (test == null)
            {
                return NotFound();
            }

            return test;
        }

        // PUT: api/Test/5
        // To protect from overposting attacks, enable the specific properties you want to bind to, for
        // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
        [HttpPut("{id}")]
        public async Task<IActionResult> PutTest(int id, Test test)
        {
            if (id != test.DataId)
            {
                return BadRequest();
            }

            _context.Entry(test).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!TestExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // POST: api/Test
        // To protect from overposting attacks, enable the specific properties you want to bind to, for
        // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
        [HttpPost]
        public async Task<ActionResult<Test>> PostTest(Test test)
        {
            _context.Test.Add(test);
            await _context.SaveChangesAsync();

            return CreatedAtAction(nameof(GetTest), new { id = test.DataId }, test);
        }

        // DELETE: api/Test/5
        [HttpDelete("{id}")]
        public async Task<ActionResult<Test>> DeleteTest(int id)
        {
            var test = await _context.Test.FindAsync(id);
            if (test == null)
            {
                return NotFound();
            }

            _context.Test.Remove(test);
            await _context.SaveChangesAsync();

            return test;
        }

        private bool TestExists(int id)
        {
            return _context.Test.Any(e => e.DataId == id);
        }
    }
}
  

Класс модели с именем test.cs:

 using System;
using System.Collections.Generic;

namespace SimpleAPI.Models
{
    public partial class Test
    {
        public int DataId { get; set; }
        public string Data { get; set; }
    }
}
  

Мой Startup.cs:

 using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SimpleAPI.Models;

namespace SimpleAPI
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            var connection = Configuration.GetConnectionString("myDatabase");
            services.AddDbContext<appDBContext>(options => options.UseSqlServer(connection));

            services.AddControllersWithViews();
        }

        // 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();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Test}/{action=Index}/{id?}");
                //endpoints.MapControllers();
            });
        }
    }
}
  

И, наконец, мой appsettings.json:

 {
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "myDatabase": "Server=server;Database=dbname;User ID=user;Password=***;"
  }
}
  

Комментарии:

1. Я не знаю наверняка вашу проблему, но я подозреваю, что проблема может быть в токене защиты от подделки. Поместите атрибут [IgnoreAntiforgeryToken(Order = 1001)] в свой метод POST и попробуйте это

2. Вероятно, это связано с CORS. Прочитайте CORS и как включить в startup.cs. Кроме того, Postman может поначалу вводить в заблуждение, убедитесь, что ваш запрос содержит «тестовую» полезную нагрузку в виде json в теле.

3. Если вас это утешит, я не смог воспроизвести вашу жалобу; просто создал новый проект API, поместил в него свой код, сократил его до простых Ok() ответов, и сообщение работало нормально

4. Спасибо за комментарии. Я попытался добавить токен подделки anit, включил CORS при запуске и добавил атрибут [FromBody]. Ни один из них, похоже, не помог. Затем я сократил метод post до простого public void Post(), и, похоже, это нравится. Так что, может быть, проблема с тем, как я отправляю через Postman? Я меняю его на метод POST с путем localhost:58322/api/test , а в теле я использую необработанный формат с JSON как { «data»: «testing» }

5. Понял! Это был формат, который я использовал в Postman. Я отправлял в формате raw, но не заметил выпадающего списка, в котором по умолчанию используется значение «Текст». Переключил его на «JSON», и он работал, как ожидалось. Спасибо всем за помощь! Понял, что это что-то в коде. Благодаря вашему замечательному руководству удалось сузить его.

Ответ №1:

В большинстве случаев вы получаете MethodNotAllowed, когда веб-приложение не может найти метод.

Измените свой post на это и посмотрите, работает ли это:

  [HttpPost]
    public async Task<ActionResult<Test>> PostTest([FromBody] Test test)
    {
        _context.Test.Add(test);
        await _context.SaveChangesAsync();

        return CreatedAtAction(nameof(GetTest), new { id = test.DataId }, test);
    }
  

Комментарии:

1. Как атрибут [FromBody] связан с обнаружением маршрута?

2. @YegorAndrosov При работе над моими проектами .NET Core он иногда выдавал мне 405, когда я не добавлял атрибут [FromBody] . Мой ответ — это всего лишь предложение, а не гарантированное решение.

Ответ №2:

Это была скорее проблема с Postman, чем с кодом. Отличные комментарии помогли мне взглянуть на то, как данные отправлялись с помощью Postman. Комментарий Макса, в частности, был верен деньгам.