Recommand Rotativa
as well, feel it is very convenient to use. I made a demo, which you can have a reference.
1.Download the Rotativa.AspNetCore
from nuget.
2.we need to add a new Folder in wwwroot with name “Rotativa” and inside this folder, then add
wkhtmltopdf.exe
, wkhtmltoimage.exe files
.

You can get the two files from GitHub sample demo
https://github.com/webgio/Rotativa.AspNetCore/tree/master/Rotativa.AspNetCore.Demo/wwwroot/Rotativa
3.Configure it in your Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
var hostingEnvironment = app.ApplicationServices.GetService<Microsoft.AspNetCore.Hosting.IHostingEnvironment>();
RotativaConfiguration.Setup(hostingEnvironment);
4.In your controller action, Just change the returnType to ViewAsPdf
, then it will display the view as pdf.
public IActionResult Index()
{
var users = new List<User>()
{
new User{ Id = 1, Name = "AA", Address = "Address1"},
new User{ Id = 2, Name = "BB", Address = "Address2"},
new User{ Id = 3, Name = "CC", Address = "Address3"},
new User{ Id = 4, Name = "DD", Address = "Address4"},
};
return new ViewAsPdf(users);
}
The Index view:
@model IEnumerable<User>
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Id)
</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Address)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Result:

If you want to download it, you can simply give it a FileName
return new ViewAsPdf(users)
{
FileName="MyPdf.pdf"
};
Update:
Action:
public IActionResult DemoViewAsPDF()
{
var users = new List<User>()
{
new User{ Id = 1, Name = "AA", Address = "Address1"},
new User{ Id = 2, Name = "BB", Address = "Address2"},
new User{ Id = 3, Name = "CC", Address = "Address3"},
new User{ Id = 4, Name = "DD", Address = "Address4"},
};
ViewData["Data"] = "ViewDataValue";
ViewBag.Data2 = "ViewBagValue";
return new ViewAsPdf(users, ViewData);
}
View:
@model IEnumerable<User>
@{
ViewData["Title"] = "Index";
}
<h1>@ViewData["Data"]</h1>
<h1>@ViewBag.Data2</h1>
<h1>@ViewData["Title"]</h1>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Id)
</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Address)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Result:
