Welcome Guest Search | Active Topics | Sign In | Register

Net Core 6 MVC - How to post a view model when performing a Url to PDF Request Options
OJB1
Posted: Friday, October 21, 2022 1:06:26 PM
Rank: Newbie
Groups: Member

Joined: 10/6/2022
Posts: 9
Hello,

I'm trialing your software and one of the features I'm looking to achieve is being able to post a view model with a url to pdf request. The method below gives an example of a current method used for posting the view model to a page that uses JavaScript to render a chart.

Quote:

[HttpPost, Route("basicareachart/")]
public async Task<IActionResult> BasicAreaChartAsync([FromBody] Chart chart)
{
try
{
var qwwqd = chart;
switch (chart.DataSourceType)
{
case "logEvents":
// code block
chart.ChartDataDatasets = await _appInsightsQueryRepo.GetChartsFromAppInsightsWithQuery(chart);
break;
}

return View(chart);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}


I need to achieve a simialr result when requesting a url to pdf, in order that I can re-produce the same chart using your headless browser.

Please could you advise the best approach for this? I'm not sure whether it would be easier to work with your option for "Calling HtmlToPdfOptions.AddPostData" OR use MVC to PDF, many thanks
eo_support
Posted: Friday, October 21, 2022 3:51:38 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Hi,

MVCToPDF should be the easiest way to do this. This requires only minimim code changes. You can follow these steps:

1. Follow steps here to initialize MVCToPDF in your application:

https://www.essentialobjects.com/doc/pdf/web/mvc.html#begin

2. Add EO.Pdf.Mvc.RenderAsPDF attribute to your action. So the code would become this:

Code: C#
[HttpPost, Route("basicareachart/")]
[EO.Pdf.Mvc.RenderAsPDF]
public async Task<IActionResult> BasicAreaChartAsync([FromBody] Chart chart)
{
    ....
}


This will automatically "redirect" the output of your action to PDF. So whatever that triggers this action would get PDF result instead of the original HTML result. For example, if the action is triggered by a browser, then the browser would get the PDF file instead of the original HTML.

3. Once step 2 is sucessful, you can change this "always PDF" logic into conditional if needed. For example, you may only want to return PDF if a specific query string parammeter exists. To do so you would need to add AutoConvert=false to your attribute and then call RenderAsPDF conditionally in your action:

Code: C#
[HttpPost, Route("basicareachart/")]
[EO.Pdf.Mvc.RenderAsPDF(AutoConvert=false)]
public async Task<IActionResult> BasicAreaChartAsync([FromBody] Chart chart)
{
    if (some_condition_is_met())
        MVCToPDF.RenderAsPDF();
    ....
}


Hope this helps. Please feel free to let us know if you run into any problems or still have any questions.

Thanks!
OJB1
Posted: Friday, October 21, 2022 6:03:45 PM
Rank: Newbie
Groups: Member

Joined: 10/6/2022
Posts: 9
Hi, many thanks for coming back.

I did some more testing using MvcToPdf but I couldnt get it to work for my particular environment.
Instead I have reverted to using EO.Pdf.HtmlToPdf.ConvertUrl() and this seems to work OK for me now.

Using the HtmlToPdf option means I achieve more flexibility and control as to how and under what scenario I can generate the PDF, my requirements was being able to do either of the following:
- Export to PDF on a user request by posting to a controller api method.
- Export to PDF from a process triggered from a background service such as a cron job.

I'm able to call the ConvertUrl method from anywhere within the app, I'm not tied to using MVC and controller methods alone so I beleive I have a solution now.

Code: C#
[HttpGet, Route("basicareachart/")]
public IActionResult BasicAreaChart()
{
	return View();
}
// I tested this by calling the POST method from Swagger UI.
// The results returns a download link
[HttpPost, Route("downloadchartreport/")]
public async Task<ActionResult> DownloadChartReportPdf()
{
	ViewData["Message"] = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";

	var scheme = this.Request.Scheme;
	var host = this.Request.Host;
	var pathBase = this.Request.PathBase;
	var url = "http://" + host + "/chartexport/basicareachart";

	// create memory stream to save PDF
	using var pdfStream = new MemoryStream();

	EO.Pdf.HtmlToPdf.ConvertUrl(url, pdfStream);

	// Reset stream position
	pdfStream.Seek(0, SeekOrigin.Begin);

	return File(pdfStream.ToArray(), "application/pdf", "Test Report");
}

eo_support
Posted: Saturday, October 22, 2022 9:13:26 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Yes. Thanks for sharing the code. That will work too.

Please feel free to let us know if you have any more questions.
OJB1
Posted: Monday, October 24, 2022 3:56:50 PM
Rank: Newbie
Groups: Member

Joined: 10/6/2022
Posts: 9
Hi, one thing I'm still not sure how to do is sending the EO.Pdf.HtmlToPdf.ConvertUrl as a post request rather than a get request. Ideally I'd like to send it as a post and then pass in a model of properties as the [FromBody] request, is this possible? thanks
eo_support
Posted: Monday, October 24, 2022 5:15:15 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Hi,

Yes. This is possible. Depending on the complexity of your request, you can use either one of the following methods. For simple request, you can use this method:

https://www.essentialobjects.com/doc/pdf/htmltopdf/post.html

For more complex request, you can use an HttpToPdfSession object, you can find sample code here:

https://www.essentialobjects.com/doc/eo.pdf.htmltopdfsession.runwebviewcallback_overload_1.html

The key to the second method is you would call the underlying WebView object's LoadRequest method to load the request directly into the WebView.

Please let us know if you still have any questions.

Thanks!
OJB1
Posted: Monday, October 24, 2022 5:37:54 PM
Rank: Newbie
Groups: Member

Joined: 10/6/2022
Posts: 9
Hi, I did review the documentaiton for "https://www.essentialobjects.com/doc/pdf/htmltopdf/post.html" but I wasnt able to add a model of properties without calling .ToString() method, I also tried to serialize the model object but for some reason it kept faling for me. I'll have another look.

I kept getting the following text in the output pdf:
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.13","title":"Unsupported Media Type","status":415,"traceId":"00-
2c374705c4a632b80b330707c3bf3362-a5f88e6b79c7132e-00"}

Below is my sample code when trying to post the data:

Code: C#
private string? ChartRenderUrl { get; set; }

[BindProperty(SupportsGet = true)]
private ChartRenderViewModel? ChartRenderViewModel { get; set; }

[HttpPost, Route("basicareachart/")]
public IActionResult BasicAreaChartAsync([FromBody] ChartRenderViewModel chartRenderViewModel)
{
	if (chartRenderViewModel == null) { return BadRequest("ChartRenderViewModel is missing!"); }

	ChartRenderViewModel = new ChartRenderViewModel();
	//ChartRenderViewModel = JsonSerializer.Deserialize&lt;ChartRenderViewModel&gt;(chartRenderViewModel);
	ChartRenderViewModel = chartRenderViewModel;

	return View(ChartRenderViewModel);
}

[HttpGet, Route("downloadchartreportpdf/")]
public async Task<ActionResult> DownloadChartReportPdfAsync(string id)
{
	try
	{
		var chart = await _cosmosDbService.GetItemAsync<Chart>(
			id, _cosmosDbName_reportConfig,
			_cosmosDbContainerName_charts,
			_cosmosDbPartitionKey_charts, id);

		if (chart == null) { return NotFound(); }

		var chartRenderViewModel = new ChartRenderViewModel();
		chartRenderViewModel!.Chart = chart;

		switch (chart.DataSourceType)
		{
			case "logEvents":
				// code block
				chartRenderViewModel.ChartData = await _appInsightsQueryRepo.GetChartsFromAppInsightsWithQuery(chart);
				break;
		}

		// Fetch the chart report header html  from the repo.
		chartRenderViewModel.ChartReportHeaderHtml = await _chartExportRepo.GetChartReportHeaderHtml(chart);

		// Get the localhost url from the client request.
		var scheme = this.Request.Scheme;
		var host = this.Request.Host;
		var pathBase = this.Request.PathBase;

		switch (chart.ChartType)
		{
			case "basicAreaChart":
				// code block
				ChartRenderUrl = "http://" + host + "/chartexport/" + chart.ChartType + "?id=" + id;
				break;
			default:
				// code block
				return BadRequest("Chart Type not recognized!");
		}

		switch (chart.PdfExportOptions!.PaperSize)
		{
			case "A3":
				// code block
				switch (chart.PdfExportOptions!.PaperOrientation)
				{
					case "landscape":
						HtmlToPdf.Options.PageSize = new SizeF(EO.Pdf.PdfPageSizes.A3.Height, EO.Pdf.PdfPageSizes.A3.Width);
						// Left / Top / Width / Height
						// A3 Paper Size = 11.7 x 16.5 inches OR 297 x 420 mm (width x height)
						EO.Pdf.HtmlToPdf.Options.OutputArea = new RectangleF(0.5f, 0.5f, 15.5f, 10.7f);
						break;
					case "portrait":
						HtmlToPdf.Options.PageSize = new SizeF(EO.Pdf.PdfPageSizes.A3.Width, EO.Pdf.PdfPageSizes.A3.Height);
						// Left / Top / Width / Height
						// A3 Paper Size = 11.7 x 16.5 inches OR 297 x 420 mm (width x height)
						EO.Pdf.HtmlToPdf.Options.OutputArea = new RectangleF(0.5f, 0.5f, 10.7f, 15.5f);
						break;
				}
				break;
			case "A4":
				// code block
				switch (chart.PdfExportOptions!.PaperOrientation)
				{
					case "landscape":
						HtmlToPdf.Options.PageSize = new SizeF(EO.Pdf.PdfPageSizes.A4.Height, EO.Pdf.PdfPageSizes.A4.Width);
						// Left / Top / Width / Height
						// A4 Paper Size = 8-1/4 x 11-3/4 inches OR 210 x 297 mm (width x height)
						EO.Pdf.HtmlToPdf.Options.OutputArea = new RectangleF(0.5f, 0.5f, 10.75f, 7.25f);
						break;
					case "portrait":
						HtmlToPdf.Options.PageSize = new SizeF(EO.Pdf.PdfPageSizes.A4.Width, EO.Pdf.PdfPageSizes.A4.Height);
						// Left / Top / Width / Height
						// A4 Paper Size = 8-1/4 x 11-3/4 inches OR 210 x 297 mm (width x height)
						EO.Pdf.HtmlToPdf.Options.OutputArea = new RectangleF(0.5f, 0.5f, 7.25f, 10.75f);
						break;
				}
				break;
		}

		// create memory stream to save PDF
		using var pdfStream = new MemoryStream();

		EO.Pdf.HtmlToPdf.Options.AddPostData("chartRenderViewModel", JsonSerializer.Serialize(chartRenderViewModel));
		//EO.Pdf.HtmlToPdf.Options.AddPostData("chartRenderViewModel", chartRenderViewModel.ToString());
		EO.Pdf.HtmlToPdf.Options.AdditionalHeaders = new string[]
		{
			"Media-Type: application/json"
		};
		EO.Pdf.HtmlToPdf.ConvertUrl(ChartRenderUrl, pdfStream);

		// Reset stream position
		pdfStream.Seek(0, SeekOrigin.Begin);

		return File(pdfStream.ToArray(), "application/pdf", chart.Name);

	}
	catch (Exception ex)
	{
		return BadRequest(ex.Message);
	}
}
eo_support
Posted: Monday, October 24, 2022 9:54:11 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,217
Hi,

AddPostData won't work in your case. You will need to use Request object this way:

Code: C#
string json = JsonSerializer.Serialize(chartRenderViewModel);
byte[] data = Encoding.UTF8.GetBytes(json);
Request request = new Request(url);
request.PostData.Add(new PostDataItem(data));
request.ContentType = "application/json";
request.Method = "POST";
webView.LoadRequest(request);

The key difference with the above code is your JSON string is the entire request body. This is what the FromBody attribute wants for your BasicAreaChartAsync method.

Where as if you use AddPostData, then the request body will be:

Code:
chartRenderViewModel=your_json_string

This is not the format your BasicAreaChartAsync is expecting because of the FromBody attribute.

Alternatively, you could change your BasicAreaChartAsync to take multiple simple arguments such as:

Code: C#
public IActionResult BasicAreaChartAsync(string type, string title, int status, string traceId)
{
    .....
}

Then you can call AddPostData for each argument. This is the "classic" way of parameters binding and it should always work.

Please let us know if this resolves the issue for you.

Thanks!


You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.