You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web API中如何验证ViewModel列表并在UI对应行显示错误信息

Solution for Validating Customer Emails in ActivateCustomers Method

Got it, let's break down how to implement row-specific validation for your ActivateCustomers method. Since your CustomerViewModel already has the [Required] attribute on the Email field, we can leverage .NET's built-in validation tools to check each entry and map errors back to the correct UI row.

Step 1: Define Helper Classes for Validation Feedback

First, create simple DTOs to carry validation errors along with their corresponding list index—this lets your UI easily match errors to the right row:

public class ItemValidationError
{
    public int RowIndex { get; set; } // Matches the position in the uploaded list
    public List<string> ErrorMessages { get; set; } = new List<string>();
}

public class ActivationValidationResult
{
    public bool IsValid { get; set; } => !ValidationErrors.Any();
    public List<ItemValidationError> ValidationErrors { get; set; } = new List<ItemValidationError>();
}

Step 2: Implement Per-Item Validation in ActivateCustomers

Now update your method to validate each CustomerViewModel in the list, collect errors, and return meaningful feedback:

public async Task<IHttpActionResult> ActivateCustomers(List<CustomerViewModel> customers)
{
    var validationResult = new ActivationValidationResult();

    // Loop through each customer in the list to validate individually
    for (int i = 0; i < customers.Count; i++)
    {
        var currentCustomer = customers[i];
        var validationContext = new ValidationContext(currentCustomer, serviceProvider: null, items: null);
        var validationErrors = new List<ValidationResult>();

        // Validate the current customer (checks all DataAnnotations like [Required])
        bool isCustomerValid = Validator.TryValidateObject(
            currentCustomer,
            validationContext,
            validationErrors,
            validateAllProperties: true // Ensures all attributes are checked, not just required
        );

        if (!isCustomerValid)
        {
            // Map validation errors to the row index
            validationResult.ValidationErrors.Add(new ItemValidationError
            {
                RowIndex = i,
                ErrorMessages = validationErrors.Select(err => err.ErrorMessage).ToList()
            });
        }
    }

    // If there are errors, return them to the UI
    if (!validationResult.IsValid)
    {
        return BadRequest(validationResult);
    }

    // Validation passed—proceed with activation logic
    await ProcessCustomerActivation(customers);

    return Ok("All valid customers have been activated successfully");
}

// Helper method to handle actual activation logic
private async Task ProcessCustomerActivation(List<CustomerViewModel> customers)
{
    // Convert ViewModels to domain entities if needed, then update database status
    // Example:
    // var domainCustomers = customers.Select(vm => new Customer { Email = vm.Email }).ToList();
    // await _customerRepository.ActivateCustomers(domainCustomers);
}

Key Notes for Implementation

  • Row Index Matching: The RowIndex property directly corresponds to the position of the customer in the uploaded list. Make sure your UI preserves the order of items when sending the request—this ensures errors show up on the correct row.
  • DataAnnotations Reuse: We're using the existing [Required] attribute on CustomerViewModel, so you don't have to duplicate validation logic. If you add more attributes later (like [EmailAddress]), this code will automatically check those too.
  • Clear Feedback: The returned ActivationValidationResult gives your UI a structured way to display errors—loop through the ValidationErrors list, find the row with the matching RowIndex, and show the ErrorMessages for that row.

内容的提问来源于stack exchange,提问作者VJOY

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:06:21