是否可以在ServiceStack中使用一个通用/抽象服务?

时间:2013-01-29 09:39:46

标签: api rest servicestack

我正在使用ServiceStack开发一个(希望)RESTful API。

我注意到我的大部分服务看起来都是一样的,例如,GET方法会看起来像这样

        try
        {
            Validate();
            GetData(); 
            return Response();
        }
        catch (Exception)
        {
            //TODO: Log the exception
            throw; //rethrow
        }

假设我有20个资源,20个请求DTO,所以我或多或少地获得了大约20个相同模板的服务......

我试图创建一个通用或抽象的服务,所以我可以创建只实现相关行为的继承服务,但是我被卡住了,因为请求DTO不是序列化所需的。

有什么办法吗?

修改

我尝试做的一个例子:

public abstract class MyService<TResponse,TRequest> : Service
{
    protected abstract TResponse InnerGet();
    protected abstract void InnerDelete();
    public TResponse Get(TRequest request)
    {
        //General Code Here.
        TResponse response = InnerGet();
        //General Code Here.
        return response;
    }

    public void Delete(TRequest request)
    {
        //General Code Here.
        InnerDelete();
        //General Code Here.
    }
}

public class AccountService : MyService<Accounts, Account>
{

    protected override Accounts InnerGet()
    {
        throw new NotImplementedException();//Get the data from BL
    }

    protected override void InnerDelete()
    {
        throw new NotImplementedException();
    }
}

2 个答案:

答案 0 :(得分:4)

为了在New API中执行此操作,我们引入了IServiceRunner的概念,该服务将服务的执行与其实现分离。

要添加自己的Service Hook,您只需要从其默认实现中覆盖AppHost中的默认Service Runner:

public virtual IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{           
    return new ServiceRunner<TRequest>(this, actionContext); //Cached per Service Action
}

用你自己的:

public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{           
    return new MyServiceRunner<TRequest>(this, actionContext); //Cached per Service Action
}

MyServiceRunner只是一个实现您感兴趣的自定义钩子的自定义类,例如:

public class MyServiceRunner<T> : ServiceRunner<T> {
    public override void OnBeforeExecute(IRequestContext requestContext, TRequest request) {
      // Called just before any Action is executed
    }

    public override object OnAfterExecute(IRequestContext requestContext, object response) {
      // Called just after any Action is executed, you can modify the response returned here as well
    }

    public override object HandleException(IRequestContext requestContext, TRequest request, Exception ex) {
      // Called whenever an exception is thrown in your Services Action
    }
}

另外,对于更细粒度的错误处理选项,请查看Error Handling wiki page

答案 1 :(得分:0)

我的解决方案是添加一个附加层,我可以在其中处理每个实体的逻辑:

基本逻辑示例:

class User(AbstractUser):
    ... # fields
    objects = MedicalUserManager()

class MedicalUserManager(models.UserManager):
    def create_user(self, username, email=None, password=None, **extra_fields):
        # pop all fields you want to use for `Doctor` or `Patient` from `extra_fields`
        doctor_pin = extra_fields.pop('doctor_pin', None)
        pid = extra_fields.pop('pid', None)
        user = super().create_user(username, email, password, **extra_fields)
        if doctor_pin:
            doctor = Doctor.objects.create(user=user, upin=doctor_pin)
            user.is_doctor = True  # not needed if `is_doctor` is in `extra_fields` because it would have been saved when creating the `User`
            user.save()
        elif pid:
            patient = Patient.objects.create(user=user, pid=pid)
            user.is_patient = True
            user.save()
        return user

然后我们可以使用钩子:

public interface IEntity
{
    long Id { get; set; }
}

public interface IReadOnlyLogic<Entity> where Entity : class, IEntity
{
    List<Entity> GetAll();
    Entity GetById(long Id);
}
public abstract class ReadOnlyLogic<Entity> : IReadOnlyLogic<Entity> where Entity : class, IEntity, new()
{
    public IDbConnection Db { get; set; }

    #region HOOKS
    protected SqlExpression<Entity> OnGetList(SqlExpression<Entity> query) { return query; }
    protected SqlExpression<Entity> OnGetSingle(SqlExpression<Entity> query) { return OnGetList(query); }
    #endregion

    public List<Entity> GetAll()
    {
        var query = OnGetList(Db.From<Entity>());
        return Db.Select(query);
    }

    public Entity GetById(long id)
    {
        var query = OnGetSingle(Db.From<Entity>())
                .Where(e => e.Id == id);

        var entity = Db.Single(query);

        return entity;
    }
}

最后,我们的服务仅调用我们的逻辑:

public interface IHello : IReadOnlyLogic<Hello> { }

public class HelloLogic : ReadOnlyLogic<Hello>, IHello
{
    protected override SqlExpression<Hello> OnGetList(SqlExpression<Hello> query)
    {
        return query.Where(h => h.Name == "Something");
    }
}