I have two controllers, 1 is an api controller, the other is a view controller, what I am trying to do is generate the PDF from the api controller from a view in the view controller...is this possible and how would I do it?
API Controller - This is where I want to generate the PDF
public byte[] getReportsPDF(string community, string procedure)
{
byte[] pdfBytes = new byte[] { };
System.Web.Mvc.ControllerContext context = new System.Web.Mvc.ControllerContext();
if(procedure == "GetProductionTasks")
{
var actionPDF = new Rotativa.ActionAsPdf("RedBluePDF", new { community = community, procedure = procedure })
{
PageSize = Size.A4,
PageOrientation = Rotativa.Options.Orientation.Landscape,
PageMargins = { Left = 1, Right = 1 }
};
pdfBytes = actionPDF.BuildFile(context);
}
return pdfBytes;
}
View Controller - This is the view I want to generate.
public ActionResult RedBluePDF(string community, string procedure)
{
return View();
}
After trying for a very long time, finally I managed to solve by this:
var controller = DependencyResolver.Current.GetService<ViewController>();
RouteData route = new RouteData();
route.Values.Add("action", "RedBluePDF");
route.Values.Add("controller", "ApiController");
ControllerContext newContext = new ControllerContext(new HttpContextWrapper(System.Web.HttpContext.Current), route, controller);
controller.ControllerContext = newContext;
controller.RedBluePDF(community, procedure);
I have been trying:
var controller = DependencyResolver.Current.GetService<ControllerB>();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);
But this will give me the wrong pdf, because it will use your current controller (API Controller in this case) to make the call for ActionAsPdf()
Credit to this answer
Related
I want to use stimulsoft in a project written in ASP.NET MVC .Net6.
StimulSoft version is 2022.1.1 and I installed NuGet
Stimulsoft.Reports.Web.NetCore
in my project.
Controller:
...
public IActionResult PrintPage()
{
return View();
}
public IActionResult GetReport()
{
StiReport report = new StiReport();
report.Load(StiNetCoreHelper.MapPath(this, "wwwroot/Reports/sample1.mrt"));
var list = _unitOfWork.RefereeTypeRepos.GetAll(); //Get information for print.
report.RegData("DT", list);
return StiNetCoreViewer.GetReportResult(this, report);
}
public IActionResult ViewerEvent()
{
return StiNetCoreViewer.ViewerEventResult(this);
}
PrintPage.cshtml:
#using Stimulsoft.Report.Mvc
#using Stimulsoft.Report.Web
#Html.StiNetCoreViewer(new StiNetCoreViewerOptions()
{
Actions =
{
GetReport = "GetReport",
ViewerEvent = "ViewerEvent"
}
})
When the page loads, I get this Error:
I don't know what I should do, and which version or NuGet is proper for .Net6?
I appreciate somebody answers.
It works for me:
public IActionResult GetReport()
{
var person = new { Name = "samplename", Family = "samplefamily" };
StiReport report = new StiReport();
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
report.Load(path + "\\Reports\\Ticket.mrt");
var service = new Stimulsoft.Report.Export.StiPdfExportService();
report.RegData("PersonDetail", person);
report.Render(false);
StiPdfExportSettings st = new StiPdfExportSettings();
st.ImageQuality = 1f;
st.EmbeddedFonts = true;
st.ImageResolution = 300;
st.ImageFormat = StiImageFormat.Color;
st.AllowEditable = StiPdfAllowEditable.No;
var stream = new MemoryStream();
// Export PDF using MemoryStream.
service.ExportTo(report, stream, st);
Response.Headers.Add("Content-Disposition", "attachment;filename=card.pdf");
return File(stream.ToArray(), "application/pdf");
}
It creates a pdf file and I use Nuget: stimulsoft.reports.engine.netcore
and there is no need for ViewerEvent() and PrintPage.cshtml
Please keep in mind am fairly new to mocking and testing. In my test method I have some code like so which mocks the controller:
var azureReportControllerUnderTest = new AzureReportsController(mockIlogger.Object, mockPrismRepository.Object, mockReportRepository.Object);
azureReportControllerUnderTest.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext(),
};
azureReportControllerUnderTest.ControllerContext.HttpContext.Request.Headers.Add(HeaderNames.Cookie, selectedTenantCookie);
azureReportControllerUnderTest.HttpContext.Session.Set(handle, memoryStream.ToArray());
azureReportControllerUnderTest.HttpContext.Session.Get(handle);
This is my action method where I create a guid and a byte array as a key value pair to store a file in session.
[HttpGet]
public IActionResult ResourcesCustomReports(DateTime? startingDate, DateTime? endingDate)
{
var selectedTenant = this.GetCurrentTenant();
var fileName = $"resources_report_tenant_id_{selectedTenant}.xlsx";
var workbook = this.reportsRepository.GetCustomDateResources(selectedTenant, startingDate, endingDate);
string handle = Guid.NewGuid().ToString();
using (MemoryStream memoryStream = new MemoryStream())
{
workbook?.SaveAs(memoryStream);
memoryStream.Position = 0;
this.HttpContext.Session.Set(handle, memoryStream.ToArray());
}
return this.Json(new { FileGuid = handle, FileName = fileName });
}
What I need is simply to be able to mock the session so I can pass the test. Currently though I am getting an error like this:
This is my assertion that I need to pass through:
var result=azureReportControllerUnderTest.ResourcesCustomReports(to, from);
Assert.IsNotNull(result);
I am currently running unit tests on a controller that utilizes Url.Action to get the absolute path of two different routes. I am using Moq to set up the mock Action. The test is currently failing with the message 'Object reference not set to an instance of an object.'
[Edit - Clarifying the question]
When I debug the test, the Url shows to as a Mock<IUrlHelper> with an ActionContext property, but on the line where it calls the Url.Action, the Action property shows as null. What am I missing when setting up the Mock<IUrlHelper> that way the Url.Action doesn't come back as null?
I've checked multiple options for setting up Mock Url Actions, but nothing has seemed to work for me.
Here is the setup for the test
[Fact]
public async void SendGroup_PassesDetailsToNotificationService()
{
UrlActionContext actualContext = null;
var criteria = new NotificationCriteria { Section = 1, Subsection = 2 };
userSvc.GetNotificationGroupReturnValue = new List<NotificationRecipient>
{
new NotificationRecipient{ SmsAuth = true }
};
var actionContext = new ActionContext
{
ActionDescriptor = new ActionDescriptor(),
RouteData = new RouteData(),
};
var urlHelper = new Mock<IUrlHelper>();
urlHelper.SetupGet(h => h.ActionContext).Returns(actionContext);
urlHelper.Setup(h => h.Action(It.IsAny<UrlActionContext>()))
.Callback((UrlActionContext context) => actualContext = context);
controller.Url = urlHelper.Object;
var dummy = await controller.SendGroup(criteria);
Assert.Same(criteria, notificationSvc.SendGroupNotificationDetailUsed);
}
This is the code for the controller
[HttpPost]
public async Task<IActionResult> SendGroup([FromBody]NotificationCriteria criteria)
{
List<NotificationRecipient> recipients = (await userSvc.GetNotificationGroup(
criteria.Section, criteria.Subsection)).ToList();
if (!recipients.Any())
{
return BadRequest();
}
var uro = Url;
var sectionUrl = Url.Action("Index", "ReportHome", new {sectionId = criteria.Section}, Request.Scheme);
var profileUrl = Url.Action("Index", "Profile", null, Request.Scheme);
var detail = new NotificationDetail
{
SectionUrl = sectionUrl,
ProfileUrl = profileUrl,
Recipients = recipients
};
try
{
await notificationSvc.SendGroupNotification(detail);
}
catch
{
return StatusCode(500);
}
return Ok();
}
I need server side page render (without request) outside controller(I want to render view outside controller).
To do that, i need a fake HttpRequest to create ControllerContext so i can render view with razor.Render() and return
stringWriter.ToString() to client using signalr hub.
So i used this code to render partialview as string;
public class FakeController : Controller { }
HttpRequest httpRequest = new HttpRequest("", new UriBuilder("http", "localhost", 10654, "/" + "SomeController/SomeAction").Uri.ToString(), "");
var routeData = new RouteData();
routeData.Values.Add("Controller", "SomeController");
routeData.Values.Add("Action", "SomeAction");
StringWriter stringWriter = new StringWriter();
HttpResponse httpResponse = new HttpResponse(stringWriter);
HttpContext httpContextMock = new HttpContext(httpRequest, httpResponse);
var _contextWrapper = new HttpContextWrapper(httpContextMock);
var controllerContext = new ControllerContext(new RequestContext(_contextWrapper , routeData), new FakeController());
string filePath = "~/Areas/Site/Views/Shared/SomePartialView.cshtml";
var razor = new RazorView(controllerContext, filePath, null, false, null);
razor.Render(new ViewContext(controllerContext, razor, new ViewDataDictionary(model), new TempDataDictionary(), stringWriter), stringWriter);
return stringWriter.ToString();
Here when code tries to render #Html.HiddenFor(m => m.property)
I get error;
source : System.Web.WebPages
error : Object reference not set to an instance of an object
But the weird thing is that i can get #Model.property value(i mean it is not null) and also the other model properties(And it seems that only #Html.HiddenFor or #Html.Hidden is the problem).
Current Solution
So I have something very similar to
[HttpPost]
public ActionResult Upload()
{
var length = Request.ContentLength;
var bytes = new byte[length];
if (Request.Files != null )
{
if (Request.Files.Count > 0)
{
var successJson1 = new {success = true};
return Json(successJson1, "text/html");
}
}
...
return Json(successJson2,"text/html");
}
Unit testable solution?
I want something like this:
[HttpPost]
public ActionResult Upload(HttpRequestBase request)
{
var length = request.ContentLength;
var bytes = new byte[length];
if (request.Files != null )
{
if (request.Files.Count > 0)
{
var successJson1 = new {success = true};
return Json(successJson1);
}
}
return Json(failJson1);
}
However this fails, which is annoying as I could make a Mock from the base class and use it.
Notes
I am aware this is not a good way to parse a form/upload and would
like to say other things are going on here (namely that this upload
can be a form or an xmlhttprequest - the action does not know which).
Other ways to make "Request" unit testable would also be awesome.
You already have a Request property on your controller => you don't need to pass it as action argument.
[HttpPost]
public ActionResult Upload()
{
var length = Request.ContentLength;
var bytes = new byte[length];
if (Request.Files != null)
{
if (Request.Files.Count > 0)
{
var successJson1 = new { success = true };
return Json(successJson1);
}
}
return Json(failJson1);
}
Now you can mock the Request in your unit test and more specifically the HttpContext which has a Request property:
// arrange
var sut = new SomeController();
HttpContextBase httpContextMock = ... mock the HttpContext and more specifically the Request property which is used in the controller action
ControllerContext controllerContext = new ControllerContext(httpContextMock, new RouteData(), sut);
sut.ControllerContext = controllerContext;
// act
var actual = sut.Upload();
// assert
... assert that actual is JsonResult and that it contains the expected Data