0

I'm using C# 6.0, ASP.NET 4.5 MVC 4.

I'm developing an API that is essentially a wrapper for another API that generates PDFs. A separate server will be implementing it directly, and all other applications will send their data to this server for conversion. The underlying PDF conversion software has specific system requirements so this will free us from the limitation of what machines our applications can run on. It's also somewhat brittle so isolating it is desireable.

To accomplish this I've set up two separate MVC applications, one with the conversion implementation, the other as a simple application that generates data to be converted, which implements the API I'm developing. They're set up to exchange data using POST.

The problem I've run into is that the PDF server isn't receiving the data to be converted. It runs, but its parameter only contains null. I set it up so that it will return a PDF containing the error if this happens. It comes through successfully, containing the resulting error message it generated so that part of it is functioning properly.

Here's the code running on the PDF server:

[HttpPost]
public FileResult MakePdf(string html)
{
    byte[] pdf = null;
    var converter = new HtmlToPdfConverter();
    try
    {
        pdf = converter.GeneratePdf(html);
    }
    catch (Exception e)
    {
        Debug.WriteLine(e.Message);
        var errorHtml = errorTop + new Regex("\\s").Replace(e.Message, " ") + errorBottom;
        pdf = converter.GeneratePdf(errorHtml);
    }
    return File(pdf, "application/pdf");
}

Here's the code that's sending the HTML there to be converted:

public byte[] Fetch() {
    var webRequest = (HttpWebRequest)WebRequest.Create("http://localhost:60272/PdfServer/MakePdf");
    webRequest.Method = "POST";
    var encoder = new UTF8Encoding();
    byte[] data = encoder.GetBytes(Resource);   // Resource contains valid HTML output by ASP.NET
    webRequest.ContentLength = data.Length;
    webRequest.ContentType = "text/html";
    using (var stream = webRequest.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
        stream.Flush();
    }
    using (var webResponse = webRequest.GetResponse())
    {
        using (Stream responseStream = webResponse.GetResponseStream())
        {
            using (var memoryStream = new MemoryStream())
            {
                int bufferLength = 1024;
                data = new byte[bufferLength];
                int responseLength = 0;
                do
                {
                    responseLength = responseStream.Read(data, 0, bufferLength);
                    memoryStream.Write(data, 0, responseLength);
                } while (responseLength != 0);
                data = memoryStream.ToArray();
            }
        }
    }
    return data;
}

I haven't tried sending data to an ASP.NET MVC controller method from a separate application before. The code I wrote here is based on examples I've found of how it's done.

Any ideas about what I'm doing wrong?

buchWyrm
  • 127
  • 10

1 Answers1

1

Try to form encode it: "application/x-www-form-urlencoded" and name the string-data html. So it would look something like:

var s = "html=" + Resource;

And then send s, instead of sending Resource. And of course set the content type to "application/x-www-form-urlencoded". This should help MVC map the data to the html parameter.

That's the only thing I can think of.

On a side note, I think you also should Close() your stream when you're done, rather than flushing it.

===

A final idea would be to try to change your encoding from text/html to text/plain. I know you're thinking it's HTML, but your method is taking in a string. So to MVC it's expecting a string, not HTML, the fact that it's actually HTML is incidental to the MVC deserializer.

Ayo I
  • 7,722
  • 5
  • 30
  • 40
  • When I try it this way the server returns a (501) Not Implemented error when webRequest.GetResponse() is called. When I leave off the "html=" it works the same as it did before. I've also tried putting quotation marks or parentheses around the Resource variable to offset it from the "html=" to see if that would help, but it generated the same error. Any other thoughts? – buchWyrm Apr 05 '16 at 17:28
  • It could be the characters in your html. What happens when you try with a basic safe string, like "hello"? Does the string get received? I don't see much wrong with the code, except to post as form data. – Ayo I Apr 05 '16 at 17:54
  • It does work with just sending "hello" so it does look like it's something to do with the content I'm sending it. I tried using an escaped url: `string urlEncoded = "html=" + Uri.EscapeDataString(Resource);` `byte[] data = encoder.GetBytes(urlEncoded);` That's crashing at the same spot though so there's something else going on too . . . – buchWyrm Apr 05 '16 at 19:14
  • Try looking at this stackoverflow answer: http://stackoverflow.com/a/5665663/497745. After encoding are there any & symbols or spaces? That will be your main problem. – Ayo I Apr 05 '16 at 19:22
  • It turns out that POST wouldn't let the PDF server accept HTML code. I was able to compensate by adding a space after each "<" and then removing the space after it was received. – buchWyrm Apr 05 '16 at 21:39
  • Thanks for your help @user497745! I really appreciate it! – buchWyrm Apr 05 '16 at 21:40
  • The problem with sending the PDF server HTML was with the input validation for controller methods. One thing that the input validation does is screen for HTML. This was why it worked okay when I just sent "Hello" but threw an exception without even starting the method when I sent it HTML. I found that this could be overcome by using the `[ValidateInput(false)]` attribute on this method so that I don't have to modify the HTML to get it to accept it. Because this server is accessible only internally to our other servers the potential security risk is much lower. – buchWyrm Apr 06 '16 at 15:57