#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. Комментарий Макса, в частности, был верен деньгам.