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

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

问题描述

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

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

但是我不太确定该怎么做.

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

 //PUT:api/MonsterLists/5[HttpPut("{id}")]公共异步Task< IActionResult>PutMonsterList(字符串ID,MonsterList monsterList){如果(id!= monsterList.MonsterId){返回BadRequest();}_context.Entry(monsterList).State = EntityState.Modified;尝试{等待_context.SaveChangesAsync();}捕获(DbUpdateConcurrencyException){如果(!MonsterListExists(id)){返回NotFound();}别的{扔;}}返回NoContent();} 

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

 //PUT:api/AnyLists/5[HttpPut("{id}")]公共异步Task< IActionResult>PutAnyList(字符串ID,AnyList anyList){如果(id!= anyList.AnyId){返回BadRequest();}_context.Entry(anyList).State = EntityState.Modified;返回NoContent();} 

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

如何为所有这些人使用一种通用方法?

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

有可能吗?

谢谢!

解决方案

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

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

首先创建Id属性的通用接口以处理通用接口中的int或字符串Id:

 公共接口IHasId< TKey>其中TKey:IEquatable< TKey>{TKey ID {放;}} 

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

 公共接口IOrdered{字符串名称{get;放;}} 

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

 公共类Player:IhasId< string>,IOrdered{公共字符串ID {get;放;}公共字符串名称{get;放;}...}公共类宝藏:IHasId< int>,IOrdered{public int ID {get;放;}公共字符串名称{get;放;}...} 

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

 <代码> [Route("api/[controller]")]][ApiController]公共类GenericBaseController< T,TKey>:ControllerBase其中T:class,IHasId< TKey> ;, IOrdered其中TKey:IEquatable< TKey>{私有只读ApplicationDbContext _context;公共GenericBaseController(ApplicationDbContext上下文){_context =上下文;}//将方法设为虚拟,//因此可以在继承的api控制器中覆盖它们[HttpGet("{id}")]公共虚拟T Get(TKey id){返回_context.Set< T>().Find(id);}[HttpPost]公共虚拟布尔帖子([FromBody] T值){_context.Set< T>().Add(value);返回_context.SaveChanges()>0;}[HttpPut("{id}")]公共虚拟布尔Put(TKey id){var实体= _context.Set< T>().AsNoTracking().SingleOrDefault(x => x.Id.Equals(id));如果(实体!=空){_context.Entry< T>(值).State = EntityState.Modified;返回_context.SaveChanges()>0;}返回false;}[HttpDelete("{id}")]公共虚拟布尔删除(TKey id){var entity = _context.Set< T>().Find(id);如果(实体!=空){_context.Entry< T>(实体).State = EntityState.Deleted;返回_context.SaveChanges()>0;}返回false;}[HttpGet(列表/{pageNo}-{pageSize}")]公共虚拟(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();返回(项目,totalRecords);}} 

其余的操作很简单,只需创建从基本通用控制器继承的api控制器即可:

PlayersController:

 <代码> [Route("api/[controller]")]][ApiController]公共类PlayersController:GenericBaseController< Player,string>{公共PlayersController(ApplicationDbContext上下文):base(上下文){}} 

TreasuresController:

 <代码> [Route("api/[controller]")]][ApiController]公共类TreasuresController:GenericBaseController< Treasure,int>{公共TreasuresController(ApplicationDbContext上下文):base(上下文){}} 

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

 <代码> [Route("api/[controller]")]][ApiController]公共类TreasuresController:GenericBaseController< Treasure,int>{公共TreasuresController(ApplicationDbContext上下文):base(上下文){公共ovedrride宝藏Get(int id){//自定义逻辑….返回base.Get(id);}}} 

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

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.

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.

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

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?

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.

Is that possible?

Thanks!

解决方案

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.

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.

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

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

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

PlayersController :

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

    }
}

TreasuresController :

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

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

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

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