Há alguns dias atrás me deparei com um problema, eu precisava simular um post HTTP de uma pagina HTML. A princípio parecia ser uma tarefa fácil, porém, a página tinha um elemento input, o qual permitia ao usuário selecionar um arquivo e isso acabou complicando um pouco as coisas. Nesse artigo vou mostrar a solução que encontrei, para quem tenha o mesmo problema possa usar como referência.

Após muitas buscar na Internet descobri que um elemento do tipo input do HTML gera uma parâmetro diferente no post, passando o nome e o conteúdo do arquivo. A partir disso utilizei um sniffer para capturar todos os dados do post, e com essas informações desenvolvi a classe HttpPost, como mostra a Listagem 1.

Listagem 1: Classe HttpPost


public class HttpPost
{
    private String _url = String.Empty;
 
    private List< HttpPostParameter> _parameters = new List< HttpPostParameter>();           
 
    public HttpPost(String url)
    {
        this._url = url;
    }
 
    private void AddParameter(HttpPostParameter parameter)
    {
        this._parameters.Add(parameter);
    }
 
    public void AddKey(String name, String value)
    {
        HttpPostParameter parameter = new HttpPostParameter
        {
            Name = name,
            Value = value,
            Kind = HttpPostParameterKind.Key
        };
        this.AddParameter(parameter);
    }
 
    public void AddFile(String name, String value)
    {
        HttpPostParameter parameter = new HttpPostParameter
        {
            Name = name,
            Value = value,
            Kind = HttpPostParameterKind.File
        };
        this.AddParameter(parameter);
    }
 
    private List< HttpPostParameter> GetKeys()
    {
        return _parameters.FindAll(p => p.Kind == HttpPostParameterKind.Key);
    }
 
    public List< HttpPostParameter> GetFiles()
    {
        return _parameters.FindAll(p => p.Kind == HttpPostParameterKind.File);
    } 
 
    public String Submit()
    {
        String boundary = "----------------------------" +
        DateTime.Now.Ticks.ToString("x");
 
 
        HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(this._url);
        httpWebRequest.ContentType = "multipart/form-data; boundary=" +
        boundary;
        httpWebRequest.Method = "POST";
        httpWebRequest.KeepAlive = true;
        httpWebRequest.Credentials =
        System.Net.CredentialCache.DefaultCredentials;
 
        Stream memStream = new System.IO.MemoryStream();
 
        Byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
        boundary + "\r\n");
 
        String formdataTemplate = "\r\n--" + boundary +
        "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";
 
        foreach (HttpPostParameter key in this.GetKeys())
        {
            String formitem = String.Format(formdataTemplate, key.Name, key.Value);
            Byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
            memStream.Write(formitembytes, 0, formitembytes.Length);
        }
 
        memStream.Write(boundarybytes, 0, boundarybytes.Length);
 
        String headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n Content-Type: plain/text\r\n\r\n";
 
        foreach (HttpPostParameter file in this.GetFiles())
        {
            String header = String.Format(headerTemplate, file.Name, DateTime.Now.ToString("ddMMyyyyTHHmmss"));
 
            Byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
 
            memStream.Write(headerbytes, 0, headerbytes.Length);
                
            MemoryStream memoryStream = new MemoryStream(Encoding.Default.GetBytes(file.Value));
            Byte[] buffer = new Byte[1024];
 
            int bytesRead = 0;
 
            while ((bytesRead = memoryStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                memStream.Write(buffer, 0, bytesRead);
            }
 
            memStream.Write(boundarybytes, 0, boundarybytes.Length);
 
            memoryStream.Close();
        }
 
        httpWebRequest.ContentLength = memStream.Length;
 
        Stream requestStream = httpWebRequest.GetRequestStream();
 
        memStream.Position = 0;
        byte[] tempBuffer = new byte[memStream.Length];
        memStream.Read(tempBuffer, 0, tempBuffer.Length);
        memStream.Close();
        requestStream.Write(tempBuffer, 0, tempBuffer.Length);
        requestStream.Close();
 
        WebResponse webResponse = httpWebRequest.GetResponse();
 
        Stream stream = webResponse.GetResponseStream();
        StreamReader reader = new StreamReader(stream, Encoding.Default);
        String result = reader.ReadToEnd();
 
        webResponse.Close();
        httpWebRequest = null;
        webResponse = null;
 
        return result;
    }
}
 

Criei também a classe HttpPostParameter (Listagem 2) para representar um parâmetro do post, e o enum HttpPostParameterKind (Listagem 3) que define se o parâmetro é um parâmetro normal ou um arquivo.

Listagem 2: Classe HttpPostParameter


public class HttpPostParameter
{
    public String Name;
    public String Value;
    public HttpPostParameterKind Kind;
}

Listagem 3: Enum HttpPostParameterKind


public enum HttpPostParameterKind
{
    Key,
    File
}

Não estarei entrando em detalhes na implementação da classe HttpPost, mas vou mostrar como utilizar esta classe. Como podemos ver na listagem 4, a primeira coisa que precisamos fazer é instanciar uma classe, e nisso informar qual a URL que será utilizada para o post. Depois de criado o nosso objeto, podemos adicionar parâmetros através dos métodos AddKey(String, String) para parâmetros normais, e AddFile(String, String) para parâmetros do tipo arquivo. Após determinarmos os parâmetros devemos utilizar o método Submit() para fazer a simulação do post, esse método retorna uma String que nada mais é que o HTML gerado como resposta pelo post, ou seja, o HTML que seria desenhado no browser. Vejamos então na listagem 4 a utilização da classe HttpPost.

Listagem 4: Utilização da classe HttpPost


public static class Main
{
    public static void Main(String [] args)
    {
        HttpPost httpPost = new HttpPost("//www.devmedia.com.br/someservice")
        httpPost.AddKey("user", "devmain");
        httpPost.AddKey("password", "1234");
        httpPost.AddFile("fileName.txt", "content");
 
        String htmlResponse = httpPost.Submit();
    }
}

E é isso pessoal, espero que esse post possa ajudá-los, estou à disposição para qualquer duvida. Um abraço e até a próxima.