c# WebApi provider IHttpActionResult in Class Libr

2019-07-25 16:04发布

So I have created a provider which will handle all my code. Originally it looked like this:

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

I inherited ApiController because I want to gain access to the Ok, BadRequest and other such methods, that is the only reason. When I try to run that code, even though it compiles I get this error:

HttpControllerContext.Configuration must not be null

I assume that is because I am trying to inherit the ApiController and I shouldn't be doing that. Is there another way I can get access the the Ok and other similar methods without inheriting the ApiController. Please bare in mind that I will have more than one provider.

1条回答
看我几分像从前
2楼-- · 2019-07-25 16:38

Do not inherit from ApiController as this is instantiated by a factory in the request pipeline. You should only inherit it for actual api controller instances, not for convenience of some of the existing methods. The best solution would be to throw custom exceptions in your Provider/Service/ whatever and catch them in your controller and return the correct HttpStatus OR let the exception pass through and it would result in a 500 status.

As requested though I have created a small wrapper around the ApiController that you could reuse in your Provider/Service/etc based on an interface (so its easy to abstract this AND easy to test).

// 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());
        }
    }
}
查看更多
登录 后发表回答