API 控制器的通用方法 [英] generic methods for API controller

查看:30
本文介绍了API 控制器的通用方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的游戏编写 API,我开始意识到 GET、POST 和 PUT API 方法的数量真的可以加起来.

I'm writing an API for my game and I'm starting to realize that the amount of GET, POST, and PUT API methods can really add up.

所以现在,我正在尝试使它更通用,这样我就不必编写单独的方法,例如 GetMonsterList、GetTreasureList、GetPlayerInfo 等.

So right now, I'm trying to make it more generic so that I don't have to write a separate method like GetMonsterList, GetTreasureList, GetPlayerInfo, etc.

但我不太确定如何去做.

But I'm not quite sure how to go about doing that.

这是我目前拥有的非通用 PUT 方法.

Here is a non-generic PUT method that I currently have.

    // PUT: api/MonsterLists/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutMonsterList(string id, MonsterList monsterList)
    {
        if (id != monsterList.MonsterId)
        {
            return BadRequest();
        }

        _context.Entry(monsterList).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MonsterListExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return NoContent();
    }

这是我尝试概述通用方法的尝试:

And here is my attempt at outlining a generic method:

    // PUT: api/AnyLists/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutAnyList(string id, AnyList anyList)
    {
        if (id != anyList.AnyId)
        {
            return BadRequest();
        }

        _context.Entry(anyList).State = EntityState.Modified;

        return NoContent();
    }

我不明白的问题是,如何将模型传递给这样的通用控件?就像我有 MonsterList、TreasureList、PlayerInfo、WeaponList 等的模型.

My problem that I don't understand is, how do I pass in a model to a generic control like this? Like if I have a model for MonsterList, TreasureList, PlayerInfo, WeaponList, etc.

我怎样才能对所有这些都使用一种通用方法?

How could I use one generic method for all of them?

我确实在这里找到了一个类似的问题,Generic Web Api controller to支持任何模型,但答案似乎暗示这不是一个好主意.

I did find one similiar question here, Generic Web Api controller to support any model , but the answer seemed to imply that this isn't a good idea.

这可能吗?

谢谢!

推荐答案

在我们创建通用控制器之前,值得一提的是,实体的结构模型对于轻松或难以构建通用控制器非常重要.

Before we create the generic controller, it is worth to mention that the structure model of your entities is so important to easily or hardly build the generic controller.

>

例如,您可能有一些带有 int id 的模型,而另一些带有 string id 的模型,因此我们需要为这两种类型建立一个共同的基础.

For example you could have some models with int id and others with string id, so we need to have a common base for both types.

首先为 Id 属性创建通用接口来处理通用接口中的 int 或 string Id:

Start by creating the common interface for Id property to handle int or string Ids in the generic interface:

public interface IHasId<TKey> 
    where TKey : IEquatable<TKey>
{
    TKey Id { get; set; }
}

另一件要考虑的事情是对实体进行排序,当查询实体列表时,我们需要对它们进行排序以获得正确的分页实体.因此,我们可以创建另一个接口来指定排序属性,例如姓名.

Another thing to consider is ordering the entities, when querying for a list of entities we need to sort them to get the right paged entities. So, we can create another interface to specify the sorting property e.g. Name.

public interface IOrdered
{
    string Name { get; set; }
}

我们的对象必须实现如下的通用接口:

Our objects must implement the common interfaces like below:

public class Player : IHasId<string>, IOrdered
{
    public string Id { get; set; }
    public string Name { get; set; }
    ...
}

public class Treasure : IHasId<int>, IOrdered
{
    public int Id { get; set; }
    public string Name { get; set; }
    ...
}

现在创建一个通用的基本 api 控制器,确保将方法标记为虚拟方法,以便我们可以在必要时在继承的 api 控制器中覆盖它们.

Now create a generic base api controller, make sure to mark the methods as virtual so we can override them in the inherited api controllers if necessary.

[Route("api/[controller]")]
[ApiController]
public class GenericBaseController<T, TKey> : ControllerBase
    where T : class, IHasId<TKey>, IOrdered
    where TKey : IEquatable<TKey>
{
    private readonly ApplicationDbContext _context;

    public GenericBaseController(ApplicationDbContext context)
    {
        _context = context;
    }

    // make methods as virtual, 
    // so they can be overridden in inherited api controllers
    [HttpGet("{id}")]
    public virtual T Get(TKey id)
    {
        return _context.Set<T>().Find(id);
    }

    [HttpPost]
    public virtual bool Post([FromBody] T value)
    {
        _context.Set<T>().Add(value);
        return _context.SaveChanges() > 0;
    }

    [HttpPut("{id}")]
    public virtual bool Put(TKey id)
    {
        var entity = _context.Set<T>().AsNoTracking().SingleOrDefault(x => x.Id.Equals(id));
        if (entity != null)
        {
            _context.Entry<T>(value).State = EntityState.Modified;
            return _context.SaveChanges() > 0;
        }

        return false;
    }

    [HttpDelete("{id}")]
    public virtual bool Delete(TKey id)
    {
        var entity = _context.Set<T>().Find(id);
        if (entity != null)
        {
            _context.Entry<T>(entity).State = EntityState.Deleted;
            return _context.SaveChanges() > 0;
        }

        return false;
    }

    [HttpGet("list/{pageNo}-{pageSize}")]
    public virtual (IEnumerable<T>, int) Get(int pageNo, int pageSize)
    {
        var query = _context.Set<T>();

        var totalRecords = query.Count();
        var items = query.OrderBy(x => x.Name)
            .Skip((pageNo - 1) * pageSize)
            .Take(pageSize)
            .AsEnumerable();

        return (items, totalRecords);
    }
}

剩下的很简单,只需创建继承自基本通用控制器的 api 控制器:

The rest is easy, just create api controllers that inherits from the base generic controller:

玩家控制器:

[Route("api/[controller]")]
[ApiController]
public class PlayersController : GenericBaseController<Player, string>
{
    public PlayersController(ApplicationDbContext context) : base(context)
    {

    }
}

宝藏控制器:

[Route("api/[controller]")]
[ApiController]
public class TreasuresController : GenericBaseController<Treasure, int>
{
    public TreasuresController(ApplicationDbContext context) : base(context)
    {

    }
}

您不必创建任何方法,但您仍然可以覆盖基本方法,因为我们将它们标记为虚拟,例如:

you don't have to create any methods, but you are still able to override the base methods since we marked them as virtual e.g.:

[Route("api/[controller]")]
[ApiController]
public class TreasuresController : GenericBaseController<Treasure, int>
{
    public TreasuresController(ApplicationDbContext context) : base(context)
    {
        public ovedrride Treasure Get(int id)
        {
            // custom logic ….

            return base.Get(id);
        }
    }
}

您可以从 GitHub 下载示例项目:https://github.com/LazZiya/GenericApiSample

You can download a sample project from GitHub: https://github.com/LazZiya/GenericApiSample

这篇关于API 控制器的通用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆