Kzryzstof
Kzryzstof

Reputation: 8402

How to handle different models returned by a single Web API call?

I am consuming an API that provides this operation:

[HttpGet]
[SwaggerOperation(OperationId = nameof(GetUsers))]
[SwaggerResponse(StatusCodes.Status200OK, "Result", typeof(List<UserModel>))]
[ProducesResponseType(typeof(List<UserModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetUsers(string tenantId, int departmentId)
{
    // Magically queries the users and returns OK or detect something is wrong and returns BadRequest
    ...
}

This call can return a list of UserModel if everything is fine or an ErrorModel if the request is wrong.

Using swagger and autorest, I get an autogenerated client that has this method returning an object:

 public async Task<HttpOperationResponse<object>> GetUsersWithHttpMessagesAsync(string tenantId, int departmentId, string commitReference = default(string), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
{
   ...
}

In that method, the algorithm checks for the status code. If the status code is 200, then it builds a list of UserModel:

    // Deserialize Response
    if ((int)_statusCode == 200)
    {
        _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

        try
        {
            _result.Body = SafeJsonConvert.DeserializeObject<List<UserModel>>(_responseContent, DeserializationSettings);
        }
        catch (JsonException ex)
        {
            _httpRequest.Dispose();
            if (_httpResponse != null)
            {
                _httpResponse.Dispose();
            }
            throw new SerializationException("Unable to deserialize the response.", _responseContent, ex);
        }
   }

However, if the status code is 400, then an instance of ErrorModel is being built:

if ((int)_statusCode == 404)
{
    _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
    try
    {
        _result.Body = SafeJsonConvert.DeserializeObject<ErrorModel>(_responseContent, DeserializationSettings);
    }
    catch (JsonException ex)
    {
        _httpRequest.Dispose();
        if (_httpResponse != null)
        {
            _httpResponse.Dispose();
        }
        throw new SerializationException("Unable to deserialize the response.", _responseContent, ex);
    }
}

So anytime I am using this method, I have to check for the type of the object returned to find out if I have a list of UserModel or an ErrorModel.

I have to do something like this when I call the autogenerated client:

object result = await client.GetUserAsync(tenantId, departmentId);

switch (result)
{
    case List<UserModel> userModels:
        // Handle the users.
    case ErrorModel errorModel:
        // Handle the error.
}

This pushes any type safety checks at the runtime instead of compile-time which in turn can lead to bugs.

Question

How can I handle this situation in C# without having to rely on runtime type checks?

Upvotes: 1

Views: 1118

Answers (0)

Related Questions