Search code examples
c#.netasp.net-coreasp.net-core-mvcactionresult

How to return Generic response from Controller?


Net core application and calling other .Net Core Web API application. I am calling controller methods as below

public async Task<T> SendRequest<T, U>(U request, string methodName, HttpMethod reqtype, string token)
        {
            T res = default(T);
            try
            {
                string url = methodName;
                string contentJson = JsonConvert.SerializeObject(request);
                var content = new StringContent(contentJson, Encoding.UTF8, "application/json");
                var actualRequest = new HttpRequestMessage(reqtype, url);
                actualRequest.Content = content;
                _httpClient.DefaultRequestHeaders.Clear();
                _httpClient.DefaultRequestHeaders.Add("Authorization", token);
                var rawData = await _httpClient.SendAsync(actualRequest);
                var processedData = await rawData.Content.ReadAsStringAsync().ConfigureAwait(false);
                res = JsonConvert.DeserializeObject<T>(processedData);
            }
            catch (Exception ex)
            {
                _reqHandler.RaiseBusinessException(MethodBase.GetCurrentMethod()?.DeclaringType?.DeclaringType?.Name
                    , MethodBase.GetCurrentMethod().Name,
                    "InputType: " + typeof(U) + " || OutputType: " + typeof(T) + " || Error Message: " + ex.Message);
            }
            return res;
        }  

Below are the samples returning from API controller

return StatusCode(404, result.MapFileFileUploadSummary);
 return StatusCode((int)System.Net.HttpStatusCode.Unauthorized);

In the above code when I return some status code I want to bind it to genric type T. For example I am calling one method

 _Client.SendRequest<ObjectResult, requestModel>()

I am sending ObjectResult of type as return type. I am getting below response in processedData for example,

"{\"type\":\"https://tools.ietf.org/html/rfc7235#section-3.1\",\"title\":\"Unauthorized\",\"status\":401,\"traceId\":\"00-e517f28f9c0b0441a8634c1c703a1cae-f64589d11056ad45-00\"}"

But unable to bind it to

res = JsonConvert.DeserializeObject<T>(processedData); This returns nothing I am adding screenshot here

enter image description here here T is of type ObjectResult. I am looking for generic response type here so that I can always bind it to type T. Can some one help me with this implementation. Any help would be appreciated. Thank you


Solution

  • The issue you have is: In case of error the JSON will be totally different than if it succeed.

    For the sake of example I will hide most part about the HTTP request :

    public async Task<ActionResult<T>> SendRequest<T, U>(U request, string methodName, HttpMethod reqtype, string token);
    

    For me you should not return "T" but "ActionResult<T>". Your code still doesn't know what will be the actual content (T) but know it will return the ActionResult. It will ease the rest.

    var response = await _httpClient.SendAsync(actualRequest);
    if (!response.IsSuccessStatusCode) 
    {
       return new StatusCodeResult((int)response.StatusCode)
    }
    var responseData = JsonConvert.DeserializeObject<T>(await rawData.Content.ReadAsStringAsync());
    return new ObjectResult(responseData);
    

    In all cases you will have an ActionResult that can be directly return from your controller.

    In case of error the result will only be the status code returned by the other server.

    In case of success you'll have the data send by the other server.

    I think in case of error returning the status code to your user is enough but if to want to send the whole object you also can do it. You just need to create first a class that will represent the data sent in case of error then:

    var response = await _httpClient.SendAsync(actualRequest);
    
    object responseData;
    
    if (response.IsSuccessStatusCode) 
    {
        responseData = JsonConvert.DeserializeObject<T>(await rawData.Content.ReadAsStringAsync());
    }
    else 
    {
       responseData = JsonConvert.DeserializeObject<YourClassInCaseOfError>(await rawData.Content.ReadAsStringAsync());
    }
    
    return new ObjectResult(responseData) 
    {
       StatusCode = (int)response.StatusCode,  
    }
    

    And you also have to change the method signature to :

    public async Task<ActionResult> SendRequest<T, U>(U request, string methodName, HttpMethod reqtype, string token);
    

    Since the second example will potentially return 2 differents types (the data type or the error type) you have to use the ActionResult, not the ActionResult<>