BattlFrog
BattlFrog

Reputation: 3397

Is it ok to use generic method for web api?

I am getting data from a web api by making httpclient calls from various MVC controllers. Because I have to do it many times, I made a generic method that I can reuse by just passing in the api url and the model return type. It works fine, but I am concerned I am loosing the oppurtunity to have different methods, like GetPeople, GetPersonById, etc. Is there a downside to what I am doing?

Utilities.cs:

    public static T GetDataFromWebService<T>(T model, string svcEndPoint)
    {
        HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        var response = client.GetAsync(svcEndPoint).Result;
        var result = response.Content.ReadAsAsync<T>().Result;

        return result;
    }

Controller:

        string svc = appSettings.GetPeopleApiUrl;
        var model = new List<Person>();
        var people = Utilities.GetDataFromWebService <IEnumerable<Person>>(model, svc);

Upvotes: 3

Views: 3778

Answers (3)

Subhash Dike
Subhash Dike

Reputation: 1891

Well, there is definitely, better way of doing the overall implementation, but if I have to stick to the question, I would say any attempt of reducing coupling is a good step for future directions. In your situation, since you are abstracting away the responsibility of making service calls to a utility method, it would help you in the long run.

Though I would suggest that instead of having this stuffed together in Utility class you should make the connectivity it's own class, something like this

public delegate T ParseToObject<T>(string response);

public class ServiceConnector : IServiceConnector
{
    public string LogoffUrl { get; set; }
    public bool SupportRetry { get; set; }        

    private WebClient _client;

    public ServiceConnector()
    {
    }

    public T GetResponse<T>(string requestUrl, ParseToObject<T> parsingMethod)
    {
        string response = __getResponse(requestUrl);
        return parsingMethod(response);
    }     

    private string __getResponse(string requestUrl)
    {
        string serviceResponse = string.Empty;
        try
        {
            __initializeWebClient();
            Logger.Current.LogInfo(string.Format("Sending request with URL {0}", requestUrl));
            serviceResponse = _client.DownloadString(requestUrl);
        }
        catch (Exception ex)
        {
            if (ex.Message != null) 
            {
            Logger.Current.LogException(string.Format("Exception during OvidWS request {0} ", requestUrl), ex); 
                _client = null;
            }
          //Sample implementation only, you could throw the exception up based on your domain needs
        }
        return serviceResponse;
    }

    private void __initializeWebClient()
    {
        if (_client == null)
            _client = new WebClient();
    }
}

With this in place, tomorrow, let's say you want to add support to log off, support cookies, support credentials, support retries, this is the only place where you can be and comfortably make changes. Similarly if you want to use Webclient over something else, you can also do that better here.

Upvotes: 1

Dejan Ciev
Dejan Ciev

Reputation: 142

Try with that helper:

 public static class WebClientExtension
{
    public static T DownloadSerializedJsonData<T>(string url) where T : new()
    {
        var contentType = ConfigurationManager.AppSettings["ContentType"];//content type in app config or web config
        using (var webClient = new WebClient())
        {
            webClient.Headers.Add("Content-Type", contentType);
            var jsonData = string.Empty;
            try
            {
                jsonData = webClient.DownloadString(url);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return !string.IsNullOrEmpty(jsonData) ? JsonConvert.DeserializeObject<T>(jsonData) : new T();
        }
    }

    public static T AuthorizationContentSerializedJsonData<T>(string url) where T : new()
    {
        string jsonData = null;
        try
        {
            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            //ClientBase.AuthorizeRequest(httpRequest, Authorization.AccessToken);
            var response = httpRequest.GetResponse();
            Stream receiveStream = response.GetResponseStream();
            var readStream = new StreamReader(receiveStream, Encoding.UTF8);
            jsonData = readStream.ReadToEnd();
            response.Close();
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return !string.IsNullOrEmpty(jsonData) ? JsonConvert.DeserializeObject<T>(jsonData) : new T();
    }
}

App confing / Web config example for content type

<add key="ContentType" value="application/hal+json; charset=UTF-8" />

Upvotes: 0

usr
usr

Reputation: 171246

You can still have specialized methods such as GetPeople, GetPersonById by layering them on top:

PeopleModel GetPeople(...) {
 return GetDataFromWebService<PeopleModel>(...);
}

No downsides, it is good that you have all boilerplate code in a shared utility method.

Upvotes: 2

Related Questions