Async CTP pour un PostSubmitter avec Cancelltion Support (CancellationTokenSource) et rapport d'étape

StackOverflow https://stackoverflow.com/questions/7832991

Question

autres devs!

J'ai une classe pour l'affichage à l'aide d'un site POST ou GET et lire la réponse. Il est tout Async maintenant et ne provoque pas l'interface utilisateur pour accrocher.

Je dois le mettre à jour pour gérer maintenant l'annulation. Toutes les méthodes Async utilisées ne sont PAS accepter le jeton d'annulation. Je dois comprendre pourquoi et quelles sont mes alternatives. S'il est possible, que je devrais créer l'objet CancellationTokenSource dans la classe ou paramètrer il de l'interface utilisateur?

En second lieu, je dois exposer les progrès de la méthode PostData (). Comment puis-je faire?

La classe:

using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using System.Collections.Generic;
using RESTClient.Core.UploadFile;
using System.Threading;

namespace RESTClient.Core {

    /// <summary>
    /// Submits post data to a url.
    /// </summary>
    public class PostSubmitter {

        #region Backing Store
        private string _URL = string.Empty;
        private NameValueCollection _PostValues = new NameValueCollection();
        private PostTypeEnum _PostType = PostTypeEnum.GET;
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public PostSubmitter() {

        }

        /// <summary>
        /// Constructor that accepts a url as a parameter
        /// </summary>
        /// <param name="url">The url where the post will be submitted to.</param>
        public PostSubmitter(string url)
            : this() {
            _URL = url;
        }

        /// <summary>
        /// Constructor allowing the setting of the url and items to post.
        /// </summary>
        /// <param name="url">the url for the post.</param>
        /// <param name="values">The values for the post.</param>
        public PostSubmitter(string url, NameValueCollection values)
            : this(url) {
            _PostValues = values;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the url to submit the post to.
        /// </summary>
        public string Url {
            get {
                return _URL;
            }
            set {
                _URL = value;
            }
        }

        /// <summary>
        /// Gets or sets the name value collection of items to post.
        /// </summary>
        public NameValueCollection PostItems {
            get {
                return _PostValues;
            }
            set {
                _PostValues = value;
            }
        }

        /// <summary>
        /// Gets or sets the type of action to perform against the url.
        /// </summary>
        public PostTypeEnum Type {
            get {
                return _PostType;
            }
            set {
                _PostType = value;
            }
        }
        #endregion

        /// <summary>
        /// Posts the supplied data to specified url.
        /// </summary>
        /// <returns>a string containing the result of the post.</returns>
        public async Task<String> Post() {
            StringBuilder parameters = new StringBuilder();
            for (int i = 0; i < _PostValues.Count; i++) {
                EncodeAndAddItem(ref parameters, _PostValues.GetKey(i), _PostValues[i]);
            }
            string result = await PostData(_URL, parameters.ToString());
            return result;
        }

        /// <summary>
        /// Posts the supplied data to specified url.
        /// </summary>
        /// <param name="url">The url to post to.</param>
        /// <returns>a string containing the result of the post.</returns>
        public async Task<String> Post(string url) {
            _URL = url;
            return await this.Post();
        }

        /// <summary>
        /// Posts the supplied data to specified url.
        /// </summary>
        /// <param name="url">The url to post to.</param>
        /// <param name="values">The values to post.</param>
        /// <returns>a string containing the result of the post.</returns>
        public async Task<String> Post(string url, NameValueCollection values) {
            _PostValues = values;
            return await this.Post(url);
        }

        /// <summary>
        /// Posts data to a specified url. Note that this assumes that you have already url encoded the post data.
        /// </summary>
        /// <param name="postData">The data to post.</param>
        /// <param name="url">the url to post to.</param>
        /// <returns>Returns the result of the post.</returns>
        private async Task<String> PostData(string url, string postData) {
            HttpWebRequest request = null;

            if (_PostType == PostTypeEnum.POST) {
                Uri uri = new Uri(url);
                request = WebRequest.Create(uri) as HttpWebRequest;
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postData.Length;

                using (Stream writeStream = await request.GetRequestStreamAsync()) {
                    UTF8Encoding encoding = new UTF8Encoding();
                    byte[] bytes = encoding.GetBytes(postData);
                    writeStream.Write(bytes, 0, bytes.Length);
                }
            }
            else {
                Uri uri = new Uri(url + "?" + postData);
                request = WebRequest.Create(uri) as HttpWebRequest;
                request.Method = "GET";
            }

            using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()) {
                using (Stream responseStream = response.GetResponseStream()) {
                    using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8)) {
                        return await readStream.ReadToEndAsync();
                    }
                }
            }
        }

        /// <summary>
        /// Encodes an item and ads it to the string.
        /// </summary>
        /// <param name="baseRequest">The previously encoded data.</param>
        /// <param name="dataItem">The data to encode.</param>
        /// <returns>A string containing the old data and the previously encoded data.</returns>
        private void EncodeAndAddItem(ref StringBuilder baseRequest, string key, string dataItem) {
            if (baseRequest == null) {
                baseRequest = new StringBuilder();
            }
            if (baseRequest.Length != 0) {
                baseRequest.Append("&");
            }
            baseRequest.Append(key);
            baseRequest.Append("=");
            baseRequest.Append(HttpUtility.UrlEncode(dataItem));
        }

        public async void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) {
            //log.Debug(string.Format("Uploading {0} to {1}", file, url));
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

            HttpWebRequest wr = WebRequest.Create(url) as HttpWebRequest;
            wr.ContentType = "multipart/form-data; boundary=" + boundary;
            wr.Method = "POST";
            wr.KeepAlive = true;
            wr.Credentials = CredentialCache.DefaultCredentials;

            Stream rs = await wr.GetRequestStreamAsync();

            string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
            foreach (string key in nvc.Keys) {
                await rs.WriteAsync(boundarybytes, 0, boundarybytes.Length);
                string formitem = string.Format(formdataTemplate, key, nvc[key]);
                byte[] formitembytes = Encoding.UTF8.GetBytes(formitem);
                await rs.WriteAsync(formitembytes, 0, formitembytes.Length);
            }
            await rs.WriteAsync(boundarybytes, 0, boundarybytes.Length);

            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
            string header = string.Format(headerTemplate, paramName, file, contentType);
            byte[] headerbytes = Encoding.UTF8.GetBytes(header);
            rs.WriteAsync(headerbytes, 0, headerbytes.Length);

            FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
            byte[] buffer = new byte[4096];
            int bytesRead = 0;
            while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0) {
                await rs.WriteAsync(buffer, 0, bytesRead);
            }
            fileStream.Close();

            byte[] trailer = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
            await rs.WriteAsync(trailer, 0, trailer.Length);
            rs.Close();

            WebResponse wresp = null;
            try {
                wresp = await wr.GetResponseAsync();
                Stream stream2 = wresp.GetResponseStream();
                StreamReader reader2 = new StreamReader(stream2);
                //log.Debug(string.Format("File uploaded, server response is: {0}", reader2.ReadToEnd()));
            }
            catch (Exception ex) {
                //log.Error("Error uploading file", ex);
                if (wresp != null) {
                    wresp.Close();
                    wresp = null;
                }
            }
            finally {
                wr = null;
            }

            /**
            NameValueCollection nvc = new NameValueCollection();
            nvc.Add("id", "TTR");
            nvc.Add("btn-submit-photo", "Upload");
            HttpUploadFile("http://your.server.com/upload",          @"C:\test\test.jpg", "file", "image/jpeg", nvc);        
            **/
        }

        public async Task<String> ExecutePostRequest(Uri url, Dictionary<string, string> postData, FileInfo fileToUpload, string fileMimeType, string fileFormKey) {
            HttpWebRequest request = WebRequest.Create(url.AbsoluteUri) as HttpWebRequest;
            request.Method = "POST";
            request.KeepAlive = true;

            String boundary = Utility.CreateFormDataBoundary();
            request.ContentType = "multipart/form-data; boundary=" + boundary;

            Stream requestStream = await request.GetRequestStreamAsync();
            postData.WriteMultipartFormData(requestStream, boundary);

            if (fileToUpload != null) {
                //TODO: Need async here...
                fileToUpload.WriteMultipartFormData(requestStream, boundary, fileMimeType, fileFormKey);
            }

            byte[] endBytes = Encoding.UTF8.GetBytes("--" + boundary + "--");

            await requestStream.WriteAsync(endBytes, 0, endBytes.Length);
            requestStream.Close();

            using (WebResponse response = await request.GetResponseAsync()) {
                using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
                    return await reader.ReadToEndAsync();
                }
            }
        }

    }

}

Note: Il y a trois méthode à la fin qui sont pour le téléchargement de fichiers. Je dois encore la figure alors et avant que je fais, je dois comprendre les rapports d'annulation et de progrès.

Async CTP pour un PostSubmitter

Toute aide serait très apprécié.

Était-ce utile?

La solution

soutenir les progrès et l'annulation en prenant les paramètres de IProgress<T> et CancellationToken.

Pour l'annulation, vérifier périodiquement si l'annulation a été demandée en appelant CancellationToken.ThrowIfCancellationRequested. Pour plus d'informations, consultez Annulation sur MSDN .

Pour que le progrès, vous devez d'abord décider quel genre de « progrès » est logique. Par exemple, si le « progrès » est juste un nombre d'octets transférés, alors vous pouvez utiliser IProgress<int>. Une fois que vous avez décidé de votre type de progression, puis appelez IProgress<T>.Report pour signaler les progrès. Il y a deux choses à connaître pour IProgress<T>:

  1. Le paramètre IProgress<T> peut être null.
  2. IProgress<T>.Report fonctionne de façon asynchrone. Cela signifie que vous devez soit: a) d'utiliser un type de valeur pour T dans IProgress<T>; B) effectuer une copie en profondeur de chaque objet T passé à IProgress<T>.Report; ou C) créer un nouvel objet T chaque fois que vous appelez IProgress<T>.Report.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top