【问题标题】:Get parameter custom mapping获取参数自定义映射
【发布时间】:2021-12-12 00:50:00
【问题描述】:

我想编写许多接收对象 ID 的 GET 处理程序,

site.com/controller/Action1/1234
site.com/controller/Action2/1234
site.com/controller/Action3/1234

我想编写一次从数据库中获取复杂对象的代码:

class ComplexObject
{
    public string str1 { get; set; }
    public string str2 { get; set; }
}

ComplexObject GetFromId(string id)
{
    ComplexObject x = Database.GetById(id);

    if (x == null)
    {
        return Http404();
    }

    return x;
}

然后直接使用对象:

[Route("/[controller]/[action]/{message}")]
[HttpGet]
public string Action1(ComplexObject message)
{
    return message.str1;
}

[Route("/[controller]/[action]/{message}")]
[HttpGet]
public string Action2(ComplexObject message)
{
    return message.str1;
}

[Route("/[controller]/[action]/{message}")]
[HttpGet]
public string Action3(ComplexObject message)
{
    return message.str1;
}

而且我所有的处理程序都只会获取对象,而不必检查 ID 是否正确等。

这怎么可能?

【问题讨论】:

  • 我不建议这样做,因为您会将大量工作委托给自定义模型活页夹,这对我来说真的很奇怪。
  • 只是想明白,你想写一个endpoint来通过ID和对象名返回任意对象?
  • 顺便说一句,所有这些都记录在官方文档中...docs.microsoft.com/en-us/aspnet/core/mvc/advanced/…
  • @Marius 嗨,我想编写许多端点来做不同的事情,但都将使用该对象。想象一下控制汽车的东西。您将有“驾驶”、“停止”和“转弯”,都收到汽车的 ID

标签: c# asp.net-core asp.net-core-mvc model-binding


【解决方案1】:

official Microsoft Docs 准确描述了如何使用自定义模型绑定器将路由参数绑定到数据库中的复杂对象。

这是他们的示例模型绑定器:

public class AuthorEntityBinder : IModelBinder
{
    private readonly AuthorContext _context;

    public AuthorEntityBinder(AuthorContext context)
    {
        _context = context;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var modelName = bindingContext.ModelName;

        // Try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var value = valueProviderResult.FirstValue;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        if (!int.TryParse(value, out var id))
        {
            // Non-integer arguments result in model state errors
            bindingContext.ModelState.TryAddModelError(
                modelName, "Author Id must be an integer.");

            return Task.CompletedTask;
        }

        // Model will be null if not found, including for
        // out of range id values (0, -3, etc.)
        var model = _context.Authors.Find(id);
        bindingContext.Result = ModelBindingResult.Success(model);
        return Task.CompletedTask;
    }
}

然后有多种方法可以使用这种新的模型活页夹。一种是在模型本身上添加一个属性:

[ModelBinder(BinderType = typeof(AuthorEntityBinder))]
public class Author
{
    // snip
}

另一个是在动作参数上使用一个属性:

[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
    // snip
}

【讨论】:

    【解决方案2】:

    我不确定为什么要按照您的建议去做,但它不必要地使事情复杂化并导致对模型绑定器的依赖。

    这是我将如何实现的:

    有一个类来管理您的复杂对象并将其隐藏在接口后面,将其注入控制器:

    public interface IComplexObjectManager 
    {
        ComplexObject GetFromId(string id);
    }
    
    public class ComplexObjectManager : IComplexObjectManager 
    {
        private readonly Database _database;
        
        public ComplexObjectManager(Database database)
        {
            _database = database;
        }
    
        public ComplexObject GetFromId(string id)
        {
            ComplexObject x = _database.GetById(id);            
            return x;
        }
    }
    
    [ApiController]
    public class ComplexObjectController
    {
        public ComplexObjectController(IComplexObjectManager complexObjectManager)
        {
            ObjectManager = complexObjectManager;
        }
    
        public IComplexObjectManager ObjectManager { get; }
    }
    

    然后在您的方法中使用它,将返回类型更改为操作结果:

    [Route("/[controller]/[action]/{id}")]
    [HttpGet]
    public IActionResult Action1(string id)
    {
        var obj = ObjectManager.GetFromId(id);
        if(obj != null)
            return Ok(obj.str1);
        else
            return NotFound();
    }
    

    确保相应地处理响应。

    这种方法将事物解耦(可以为Database 添加进一步的抽象),并允许注入和单元测试。

    请检查代码的一致性。匆匆写下。

    【讨论】:

      【解决方案3】:

      我没有按照您的要求做,但我认为它可以帮助您。首先,我使用BaseController,因为您可以在执行之前过滤所有操作。

      public class BaseController : Controller
      {
          #region /*IoC*/
          public BaseViewModel baseViewModel;
          public IUnitOfWork<Product> unitOfWorkProductForCart;
          #endregion
      
          #region /*ctor*/
          public BaseController(IUnitOfWork<Product> unitOfWorkProductForCart)
          {
              this.unitOfWorkProduct = unitOfWorkProduct;
          }
          #endregion
      
          public override void OnActionExecuting(ActionExecutingContext filterContext)
          {
              string controllerName = filterContext.ActionDescriptor.RouteValues["controller"];
              string actionName = filterContext.ActionDescriptor.RouteValues["action"];
              if (actionName == "ProductDetails")
              {
                  var urlParameters = filterContext.ActionArguments;
                  if (urlParameters.Count != 0)
                  {
                      var isThatSlug = urlParameters.ElementAt(0).Key;
                      if (isThatSlug == "slug")
                      {
                          var slugCondition = urlParameters.ElementAt(0).Value;
                          var isThatProductExist = unitOfWorkProduct.RepositoryProduct.GetProductBySlugForChecking(slugCondition.ToString());
                          if (isThatProductExist.Count == 0)
                          {
                              filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
                                  {
                                      {"controller","Account"},
                                      {"action","NotFound"}
                                  });
                          }
                      }
                  }
              }
          }
      }
      

      在该示例中,我正在控制参数。如果这是我不想要的,它会将您重定向到NotFound 页面。 我希望它能给你一个想法

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-17
        • 2013-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多