获取参数自定义映射

Get parameter custom mapping

我想编写许多接收对象 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 是否正确等。

这怎么可能?

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

以下是我的实现方式:

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

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; }
}

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

[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 添加进一步的抽象),并允许注入和单元测试。

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

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
}

我并没有完全按照您的要求做事,但我认为它可以帮助您。首先,我为此使用 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 页面。 我希望它能给你一个想法