c#类库中的WebApi提供程序IHttpActionResult

时间:2016-08-22 14:33:09

标签: c# asp.net-web-api

所以我创建了一个提供程序来处理我的所有代码。 最初它看起来像这样:

public class AnswerProvider : ApiController
{

    private readonly IUnitOfWork _unitOfWork;
    private readonly AnswerService _answerService;

    private QuestionService _questionService;
    public QuestionService QuestionService => _questionService ?? (_questionService = new QuestionService(this._unitOfWork));

    public AnswerProvider(IUnitOfWork unitOfWork)
    {
        this._unitOfWork = unitOfWork;
        this._answerService = new AnswerService(unitOfWork);
    }

    public async Task<IHttpActionResult> CreateAsync(AnswerRequestModel model)
    {
        try
        {

            // Validate our answer count
            await ValidateAnswerCountAsync(model.QuestionId);

            // Create our model
            var answer = ModelFactory.Create(model);

            // Add our images to our answer
            answer.Images = model.Images;

            // Save our model
            this._answerService.Create(answer);

            // Save the database changes
            await this._unitOfWork.SaveChangesAsync();

            // Return our updated model
            return Ok(ModelFactory.Create(answer));

            // If there is an error
        }
        catch (Exception ex)
        {

            // Return our error
            return BadRequest(ex.Message.ToString());
        }
    }

    /// <summary>
    /// Validates the answers based on the question type
    /// </summary>
    /// <param name="id">The id of the question</param>
    /// <returns></returns>
    private async Task ValidateAnswerCountAsync(int id)
    {

        // Get our question
        var question = await this.QuestionService.GetAsync(id, "Answers");

        // If we have 3 answers or more
        if (question.Answers.Count >= 3 && question.Type == QuestionType.Boolean)
        {

            // Throw an error
            throw new InvalidOperationException("A Boolean question can only have 3 answers");
        }
    }
}

我继承了ApiController,因为我希望能够访问 Ok BadRequest 和其他此类方法,这是唯一的原因。 当我尝试运行该代码时,即使它编译我也会收到此错误:

  

HttpControllerContext.Configuration不能为null

我认为这是因为我试图继承 ApiController 而我不应该这样做。 有没有其他方法可以访问 Ok 和其他类似的方法而不继承 ApiController 。 请记住,我将有多个提供商。

1 个答案:

答案 0 :(得分:0)

不要从ApiController继承,因为它是由请求管道中的工厂实例化的。您应该只为实际的api控制器实例继承它,而不是为了方便某些现有方法。最好的解决方案是在您的Provider / Service /中抛出自定义异常,并在控制器中捕获它们并返回正确的HttpStatus或让异常通过,这将导致500状态。

尽管如此我已经在ApiController周围创建了一个小包装器,您可以在基于接口的Provider / Service / etc中重用它(因此很容易抽象出来并且易于测试)。

// demo of controller calling your Provider
public class SomeController : ApiController
{
    public async Task<IHttpActionResult> Get()
    {
        var wrapper = this.ActionWrapper();
        var answerProvider = new AnswerProvider(wrapper);
        var result = await answerProvider.CreateAsync(model);
    }
}

// a simple extension on the ApiController
public static class WrapperExtension
{
    public static IActionWrapper ActionWrapper(this ApiController controller)
    {
        return new ApiActionWrapperContext(controller);
    }
}
// wrapped in interface so its easy to unit test the Provider
public interface IActionWrapper
{
    OkResult Ok();
    BadRequestResult BadRequest();
    BadRequestErrorMessageResult BadRequest(string message);
    OkNegotiatedContentResult<T> Ok<T>(T content);
}

// the implementation, this takes the current Controller and uses it as the context to return the same result types
// only implemented Ok and BadRequest as a demo, you can extend it as needed
public class ApiActionWrapperContext : IActionWrapper
{
    private ApiController _controller;

    public ApiActionWrapperContext(ApiController controller)
    {
        _controller = controller;
    }

    public BadRequestResult BadRequest()
    {
        return new BadRequestResult(_controller);
    }
    public BadRequestErrorMessageResult BadRequest(string message)
    {
        return new BadRequestErrorMessageResult(message, _controller);
    }
    public OkResult Ok()
    {
        return new OkResult(_controller);
    }
    public OkNegotiatedContentResult<T> Ok<T>(T content)
    {
        return new OkNegotiatedContentResult<T>(content, _controller);
    }
}

// provider shortered with just some relevant code to demo
// notice constructor, the new private field, and the use of it
public class AnswerProvider
{
    private IActionWrapper _actionWrapper;
    public AnswerProvider(IActionWrapper actionWrapper)
    {
        if(actionWrapper == null)
            throw new ArgumentNullException("actionWrapper");
        _actionWrapper = actionWrapper;
    }

    public async Task<IHttpActionResult> CreateAsync(AnswerRequestModel model)
    {
        try
        {
            // Validate our answer count
            await ValidateAnswerCountAsync(model.QuestionId);

            // Create our model
            var answer = ModelFactory.Create(model);

            // Add our images to our answer
            answer.Images = model.Images;

            // Save our model
            this._answerService.Create(answer);

            // Save the database changes
            await this._unitOfWork.SaveChangesAsync();

            // Return our updated model
            return this._actionWrapper.Ok(ModelFactory.Create(answer));

            // If there is an error
        }
        catch (Exception ex)
        {

            // Return our error
            return this._actionWrapper.BadRequest(ex.Message.ToString());
        }
    }
}