簡體   English   中英

API中的POST上的模型綁定問題

[英]Issue with Model Binding on POST in API

我無法理解Asp.Net核心2中的模型綁定過程。我有一個非常簡單的API,它有一個模型。 它有一些基本的驗證。 每當用戶發布不正確的模型時,我都會嘗試從模型狀態返回422不可處理的實體以及錯誤消息。

我試圖理解的兩個問題如下:

  • 如果我發布沒有ID的請求,則會創建默認ID 0以繞過所需屬性。 我假設這是C#功能,為字段提供默認值。 有沒有辦法規避這個?

  • 另一個問題是,如果我在post post中放置一個斷點並發送一個錯誤的請求,它甚至不會進入該方法。 它使用驗證屬性發回400錯誤請求。 這是如何運作的? 一旦嘗試將綁定建模為無效屬性(即名稱長度> 10),請求是否會暫停? 我需要它做的是發送一個422不可處理的實體與相同的錯誤消息而不是400。

如果模型狀態驗證基於驗證屬性失敗,ASP.NET是否甚至不會進入該方法? 什么是解決此問題以返回422錯誤代碼的更好方法?

下面是我的各種類的代碼(我在創建項目時使用了API模板):

Startup.cs - 我在這里添加的只是我的內存上下文的單例實例

public void ConfigureServices(IServiceCollection services)
{
    //services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddMvc();
    services.AddSingleton<IItemRepository, ItemRepository>();
}

IItemRepository.cs - 我的DI界面

public interface IItemRepository
{
    List<ItemModel> Items { get; set; }
    void AddValue(ItemModel itemModel);
}

ItemRepository.cs - 具體實現

public class ItemRepository : IItemRepository
{
    public List<ItemModel> Items { get; set; } = new List<ItemModel>();

    public ItemRepository()
    {
        Items.AddRange(
            new List<ItemModel> {
                new ItemModel {Id = 1, Name = "Test1" },
                new ItemModel {Id = 2, Name = "Test2" }
             }
        );
    }

    public void AddValue(ItemModel itemModel)
    {
        Items.Add(itemModel);
    }
}

ItemModel.cs - 我的用戶輸入模型類

public class ItemModel
{
    [Required]
    public int Id { get; set; }
    [MaxLength(10)]
    public string Name { get; set; }
}

ValuesController.cs

[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
    private IItemRepository _context;

    public ValuesController(IItemRepository context)
    {
        _context = context;
    }

    // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return Ok(_context.Items);
    }

    // GET api/values/5
    [HttpGet("{id}", Name = "GetSingle")]
    public ActionResult<string> Get(int id)
    {
        return Ok(_context.Items.Where(x => x.Id == id));
    }

    // Problem here - placing a breakpoint in below method does not do anytthing as it will return a 400 bad request instead of 422
    [HttpPost]
    public ActionResult Post([FromBody] ItemModel itemModel)
    {
        if (!ModelState.IsValid)
        {
            return new UnprocessableEntityObjectResult(ModelState);
        }

        ItemModel addNew = new ItemModel { Id = itemModel.Id, Name = itemModel.Name };
        _context.AddValue(addNew);
        return Ok(addNew);
    }
}

如果我發布沒有ID的請求,則會創建默認ID 0以繞過所需屬性。 我假設這是C#功能,為字段提供默認值。 有沒有辦法規避這個?

正如@StephenMuecke 在這里回答的那樣,您需要將模型更改為

public class ItemModel
{
    [Required]
    public int? Id { get; set; }

    [MaxLength(10)]
    public string Name { get; set; }
}

另一個問題是,如果我在post post中放置一個斷點並發送一個錯誤的請求,它甚至不會進入該方法。 它使用驗證屬性發回400錯誤請求。 這是如何運作的? 一旦嘗試將綁定建模為無效屬性(即名稱長度> 10),請求是否會暫停? 我需要它做的是發送一個422不可處理的實體與相同的錯誤消息而不是400。

這是因為您將ApiControllerAttribute應用於Controller。 從文檔

驗證錯誤會自動觸發HTTP 400響應。 您的操作中不再需要以下代碼:

 if (!ModelState.IsValid) { return BadRequest(ModelState); } 

您可以刪除該屬性,或者,如同相同的鏈接所解釋的,將其添加到啟動配置:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
})

您的第一個問題可以通過使屬性可以為空來解決。 由Stepen Muecke評論。

另外看看這里 ,也許BindRequired屬性可以提供幫助。 本文還介紹了如何調整行為。

對於您的第二個問題,這是Asp.Net Core 2.1的 (破壞)行為。 新的是自動400響應 這就解釋了為什么你的斷點沒有被擊中。 您可以按如下方式禁止此操作:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});

對於第一個問題,如果您不想使屬性可為空,您還可以放置范圍屬性[Range(1,int.MaxValue)],但在這種情況下0將不是有效值。

對於第二個問題,如果您仍希望從ApiControllerAttribute進行自動模型驗證但需要422響應代碼而不是400,則可以使用InvalidModelStateResponseFactory配置選項。

services.Configure<ApiBehaviorOptions>(options => 
{
  options.InvalidModelStateResponseFactory = ctx => 
     new UnprocessableEntityObjectResult(ctx.ModelState);
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM