在WinForms中集中API CRUD调用

e37o9pze  于 2022-12-27  发布在  其他
关注(0)|答案(1)|浏览(138)

我需要在WinForms应用程序内部进行CRUD调用。为此,我想创建Post、Put、Delete和Get的助手方法。此方法需要将数据返回给我。
对于post和put,我可能会传递List、myobject、string、int、bool等。我的返回类型可能与我输入的模型不同。
为了实现上述目标,我正沿着以下思路思考......
类返回给调用方

public class ApiCallResult<X> where X: class 
{
    public HttpStatusCode StatusCode { get; set; }
    public string ReasonPhrase { get; set; }

    public bool IsError { get; set; }
    public bool IsException { get; set; }
    public string Message { get; set; }

    public X Response { get; set; } //deserialized object, could be List, int string or just a single object
}

然后沿着以下代码行创建方法

public static async Task<ApiCallResult<X>> Post<T, X>(T data, X returnModel, string apiUrl)
{
    var apiCallResult = new ApiCallResult<X> {IsError = true, Message = "No run"};
    try
    {
        //json string 
        var jsonString = JsonConvert.SerializeObject(data);
        using (var client = new HttpClient())
        {
            var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
            var response = await client.PostAsync(apiUrl, httpContent);
            var jsonResponseString = await response.Content.ReadAsStringAsync();
            
            //fill
            apiCallResult.StatusCode = response.StatusCode;
            apiCallResult.ReasonPhrase = response.ReasonPhrase;
            if (response.IsSuccessStatusCode)
            {
                //deserialize
                if (typeof(X).GetGenericTypeDefinition() == typeof(List<>))
                {
                    // X is a generic list
                    apiCallResult.Response = JsonConvert.DeserializeObject<List<X>>(jsonResponseString).ToList();
                }
                else
                {
                    //single object
                    apiCallResult.Message = JsonConvert.DeserializeObject<X>(jsonResponseString);
                }
                apiCallResult.IsError = false;
            }
            else
            {
                //error response
                apiCallResult.Message = jsonResponseString;
            }
        }
    }
    catch (Exception ex)
    {
        apiCallResult.IsException = true;
        apiCallResult.Message = ex.Message;
    }

    return apiCallResult;
}

但显然这有很多问题

  1. async可以返回一个Task,而我希望返回apiCallResult
  2. var apiCallResult = new ApiCallResult<T> {IsError = true, Message = "No run"};行将生成The type 'T' must be a reference type in order to use it as parameter 'T'
  3. apiCallResult.Response = JsonConvert.DeserializeObject<List<X>>(jsonResponseString).ToList();行将生成Cannot convert source type 'System.Collections.Generic.List<X>' to target type 'T'
    我怎样才能最好地完成这样的事情呢?我不想写多个方法来执行这个任务。

**更新1:**以下是工作示例代码及其用法,有一些粗糙的边缘,但工作...

CRUD方法

public static class ApiCrudCallHelper
{
    /// <summary>
    /// Performs Post and returns ApiCallResult
    /// </summary>
    /// <typeparam name="T">model to Post, could be null, T, List T</typeparam>
    /// <typeparam name="X">return model by API, could be X, List X, string </typeparam>
    /// <param name="data">data to post of type T, List T</param>
    /// <param name="apiUrl">api full URL like http://localhost:65152/API/Test if executing custom action, provide that as well at the end </param>
    /// <returns>
    /// ApiCallResult
    ///     StatusCode: status code returned by the API
    ///     ReasonPhrase: reason phrase returned by the API
    ///     IsError: true/false
    ///     IsException: true/false
    ///     Message: error message, exception message, or result of OK etc results by API
    ///     X ResponseObject: model returned by the API, it might not be available in all cases. Could be X, List X or string as provided by X above
    /// </returns>
    public static async Task<ApiCallResult<X>> Post<T, X>(T data, string apiUrl) where X : class
    {
        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
        try
        {
            //json string 
            var jsonString = JsonConvert.SerializeObject(data);
            using (var client = new HttpClient())
            {
                var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
                var response = await client.PostAsync(apiUrl, httpContent);
                var jsonResponseString = await response.Content.ReadAsStringAsync();

                //fill
                if (response.IsSuccessStatusCode)
                {
                    //deserialize
                    if (!typeof(X).Equals(typeof(string)))
                    {
                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
                    }
                    apiCallResult.IsError = false;
                }
                apiCallResult.StatusCode = response.StatusCode;
                apiCallResult.ReasonPhrase = response.ReasonPhrase;
                apiCallResult.Message = jsonResponseString;
            }
        }
        catch (Exception ex)
        {
            apiCallResult.IsException = true;
            apiCallResult.Message = ex.Message;
        }

        return apiCallResult;
    }

    /// <summary>
    /// Performs Put and returns ApiCallResult
    /// </summary>
    /// <typeparam name="T">model to Post, could be null, T, List T</typeparam>
    /// <typeparam name="X">return model by API, could be X, List X, string </typeparam>
    /// <param name="data">data to post of type T, List T</param>
    /// <param name="apiUrl">api full URL including the Id like http://localhost:65152/API/Test/12345 if executing custom action, provide that as well </param>
    /// <returns>
    /// ApiCallResult
    ///     HttpStatusCode StatusCode: status code returned by the API
    ///     string ReasonPhrase: reason phrase returned by the API
    ///     bool IsError: true/false
    ///     bool IsException: true/false
    ///     string Message: error message, exception message, or result of OK etc results by API
    ///     X ResponseObject: model returned by the API, it might not be available in all cases. Could be X, List X or string as provided by X above
    /// </returns>
    public static async Task<ApiCallResult<X>> Put<T, X>(T data, string apiUrl) where X : class
    {
        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
        try
        {
            //json string 
            var jsonString = JsonConvert.SerializeObject(data);
            using (var client = new HttpClient())
            {
                var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
                var response = await client.PutAsync(apiUrl, httpContent);
                var jsonResponseString = await response.Content.ReadAsStringAsync();

                //fill
                if (response.IsSuccessStatusCode)
                {
                    //deserialize
                    if (!typeof(X).Equals(typeof(string)))
                    {
                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
                    }
                    apiCallResult.IsError = false;
                }
                apiCallResult.StatusCode = response.StatusCode;
                apiCallResult.ReasonPhrase = response.ReasonPhrase;
                apiCallResult.Message = jsonResponseString;
            }
        }
        catch (Exception ex)
        {
            apiCallResult.IsException = true;
            apiCallResult.Message = ex.Message;
        }

        return apiCallResult;
    }

    /// <summary>
    /// Performs Delete and returns ApiCallResult
    /// </summary>
    /// <typeparam name="X">return model by API, could be X, List X, string. Usually you'll only get Ok result etc for delete, so specify string  </typeparam>
    /// <param name="apiUrl">api full URL including the Id like http://localhost:65152/API/Test/12345 if executing custom action, provide that as well </param>
    /// <returns>
    /// ApiCallResult
    ///     HttpStatusCode StatusCode: status code returned by the API
    ///     string ReasonPhrase: reason phrase returned by the API
    ///     bool IsError: true/false
    ///     bool IsException: true/false
    ///     string Message: error message, exception message, or result of OK etc results by API
    ///     X ResponseObject: will only be available if api is returning a model (should not), in most cases it will not be available. Could be X, List X or string as provided by X above
    /// </returns>
    public static async Task<ApiCallResult<X>> Delete<X>(string apiUrl) where X : class
    {
        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
        try
        {
            using (var client = new HttpClient())
            {
                var response = await client.DeleteAsync(apiUrl);
                var jsonResponseString = await response.Content.ReadAsStringAsync();

                //fill
                if (response.IsSuccessStatusCode)
                {
                    //deserialize
                    if (!typeof(X).Equals(typeof(string)))
                    {
                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
                    }
                    apiCallResult.IsError = false;
                }
                apiCallResult.StatusCode = response.StatusCode;
                apiCallResult.ReasonPhrase = response.ReasonPhrase;
                apiCallResult.Message = jsonResponseString;
            }
        }
        catch (Exception ex)
        {
            apiCallResult.IsException = true;
            apiCallResult.Message = ex.Message;
        }

        return apiCallResult;
    }

    /// <summary>
    /// Performs Get and returns ApiCallResult
    /// </summary>
    /// <typeparam name="X">return model by API, could be X, List X, string. </typeparam>
    /// <param name="apiUrl">api full URL </param>
    /// <returns>
    /// ApiCallResult
    ///     HttpStatusCode StatusCode: status code returned by the API
    ///     string ReasonPhrase: reason phrase returned by the API
    ///     bool IsError: true/false
    ///     bool IsException: true/false
    ///     string Message: error message, exception message, or result of OK etc results by API
    ///     X ResponseObject: Could be X, List X or string as provided by X above
    /// </returns>
    public static async Task<ApiCallResult<X>> Get<X>(string apiUrl) where X : class
    {
        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
        try
        {
            using (var client = new HttpClient())
            {
                var response = await client.GetAsync(apiUrl);
                var jsonResponseString = await response.Content.ReadAsStringAsync();

                //fill
                if (response.IsSuccessStatusCode)
                {
                    //deserialize
                    if (!typeof(X).Equals(typeof(string)))
                    {
                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);
                    }
                    apiCallResult.IsError = false;
                }
                apiCallResult.StatusCode = response.StatusCode;
                apiCallResult.ReasonPhrase = response.ReasonPhrase;
                apiCallResult.Message = jsonResponseString;
            }
        }
        catch (Exception ex)
        {
            apiCallResult.IsException = true;
            apiCallResult.Message = ex.Message;
        }

        return apiCallResult;
    }
}

并调用(WinForms)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void btnClear_Click(object sender, EventArgs e)
    {
        txtResults.Text = "";
    }

    //standard post
    private void btnTestApiPost_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoPost();
    }

    private async void DoPost()
    {
        var dp = new DummyPost { Id = 12345, Name = "XYZ" };
        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>
        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, DummyPost>(dp, ApiUrls.TestApiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //post to custom action
    private void btnTestApiPostExtra_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoPostExtra();
    }

    private async void DoPostExtra()
    {
        var dp = new DummyPost { Id = 12345, Name = "XYZ" };
        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>
        var apiUrl = string.Format("{0}/ExtraPost", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, DummyPost>(dp, apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //post to custom action and getting ok result back 
    private void btnTestApiPostExtraOkResult_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoPostExtraOkResult();
    }

    private async void DoPostExtraOkResult()
    {
        var dp = new DummyPost { Id = 12345, Name = "XYZ" };
        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>
        var apiUrl = string.Format("{0}/ExtraPostOk", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, string>(dp, apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //post with multiline model return
    private void btnTestApiPostExtraMultiLine_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoPostExtraMultiLineReturn();
    }

    private async void DoPostExtraMultiLineReturn()
    {
        var dp = new DummyPost { Id = 12345, Name = "XYZ" };
        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>
        var apiUrl = string.Format("{0}/ExtraPostMultiLineReturn", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, List<DummyPost>>(dp, apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //standard put
    private void btnTestApiPut_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoPut();
    }

    private async void DoPut()
    {
        var dp = new DummyPost { Id = 12345, Name = "XYZ" };
        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>
        //since this is a PUT call, append the id to the url as well
        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Put<DummyPost, DummyPost>(dp, apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //standard delete
    private void btnTestApiDelete_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoDelete();
    }

    private async void DoDelete()
    {
        //not posting any model, should get back a string responce but we can get a model as well (why?)
        //since this is a DELETE call, append the id to the url as well
        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Delete<string>(apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //standard get
    private void btnTestApiGet_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoGet();
    }

    private async void DoGet()
    {
        var apiUrl = ApiUrls.TestApiUrl;
        var apiCallResult = await ApiCrudCallHelper.Get<string>(apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //get by id
    private void btnTestApiGetId_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);
        DoGetId();
    }

    private async void DoGetId()
    {
        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Get<string>(apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }

    //custom get action
    private void btnTestApiGetAll_Click(object sender, EventArgs e)
    {
        btnClear.PerformClick();
        DoGetAll();
    }

    private async void DoGetAll()
    {
        var apiUrl = string.Format("{0}/All", ApiUrls.TestApiUrl);
        var apiCallResult = await ApiCrudCallHelper.Get<List<DummyPost>>(apiUrl);
        txtResults.AppendText(apiCallResult.Message);
    }
    
}
wgx48brx

wgx48brx1#

像这样?

public static async Task<ApiCallResult<X>> Post<T, X>(T data, X returnModel, string apiUrl) where X: class
{
    var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };
    try
    {
        //json string 
        var jsonString = JsonConvert.SerializeObject(data);
        using (var client = new HttpClient())
        {
            var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
            var response = await client.PostAsync(apiUrl, httpContent);
            var jsonResponseString = await response.Content.ReadAsStringAsync();

            //fill
            apiCallResult.StatusCode = response.StatusCode;
            apiCallResult.ReasonPhrase = response.ReasonPhrase;
            if (response.IsSuccessStatusCode)
            {
                //deserialize
                if (typeof(X).GetGenericTypeDefinition() == typeof(List<>))
                {
                    // X is a generic list
                    apiCallResult.Response = JsonConvert.DeserializeObject<X>(jsonResponseString);
                }
                else
                {
                    //single object
                    apiCallResult.Message = JsonConvert.DeserializeObject<X>(jsonResponseString).ToString();
                }
                apiCallResult.IsError = false;
            }
            else
            {
                //error response
                apiCallResult.Message = jsonResponseString;
            }
        }
    }
    catch (Exception ex)
    {
         apiCallResult.IsException = true;
         apiCallResult.Message = ex.Message;
    }

    return apiCallResult;
}

相关问题