Rest API, file generation on client side - asp.net-mvc

I have an asp.net core website with a controller that return a View with a viewmodel.
This view generate a pdf on javascript using jsPdf. I can't perform the pdf generation on server side because i'm using a client side componnent (fabric.js)
I would like to expose an API to be able to download this pdf.
When i hit my end point with a browser, the pdf file is downloaded correctly.
When i'm using postman or a WebClient in c#, i can't get the pdf...
I guess i'm missing something. The Test method does not work, i have a pdf but i can't open it, it's only 15ko :(
My view :
<script src="/Scripts/fabric.js"></script>
<script src="/Scripts/jspdf.debug.js"></script>
<script type="text/javascript">
var canvas = new fabric.Canvas('canvas', {});
canvas.loadFromJSON(#Html.Raw(Model.Json),
() => {
canvas.setWidth(#Model.Width);
canvas.setHeight(#Model.Height);
var imgData = new Image();
imgData.crossOrigin = "Anonymous";
imgData.src = canvas.toDataURL('png');
var pdf = new jsPDF('p', 'pt', [#Model.Width, #Model.Height]);
pdf.addImage(imgData, 'PNG', 1, 1, #Model.Width, #Model.Height, 'Mockup', 'NONE', 0);
pdf.save('myMockup.pdf');
}
);
</script>
My controller :
public IActionResult Generate(int id)
{
var mockup = DynamicValueAppService.ParseMockup(id);
var mockupVM = new MockupGeneratorViewModel()
{
Width = mockup.Width,
Height = mockup.Height,
Json = JObject.Parse(mockup.Json)
};
Response.Headers.Add("Content-Type","application/octet-stream");
Response.Headers.Add("Content-Disposition", "attachment");
return View(mockupVM);
}
public IActionResult Test()
{
var client = new WebClient();
client.DownloadFile("http://localhost:21021/Mockup/generate/1", #"C:\mum.pdf");
return new EmptyResult();
}

Related

GeoJSON .NET with Google Maps in ASP.NET MVC

I am trying to create and load geoJSON data into Google Maps using the GeoJSON .NET library using ASP.NET MVC5 though I am doing something wrong somewhere.
Using the example code posted on the GitHub site my controller looks like this:
public ActionResult GetGeoJson()
{
var point = new GeoJSON.Net.Geometry.Point(
new GeoJSON.Net.Geometry.GeographicPosition(45.79012, 15.94107));
var featureProperties = new Dictionary<string, object> { {"Name", "Foo"} };
var model = new GeoJSON.Net.Feature.Feature(point, featureProperties);
var serializedData = JsonConvert.SerializeObject(model, Formatting.Indented,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
});
return Json(serializedData, JsonRequestBehavior.AllowGet);
}
And my view is as follows:
#{
ViewBag.Title = "Map";
}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=foobar"></script>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
function initialize() {
var centerPoint = new google.maps.LatLng(53.710921, -1.626776);
var mapOptions = {
center: centerPoint,
zoom: 12
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
map.data.loadGeoJson('/Home/GetGeoJson');
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<div id="map-canvas">
</div>
When I load the page and check the console in chrome I have the following error:
Uncaught InvalidValueError: not a Feature or FeatureCollection
If I go to my Action within the brower it outputs the following:
"{\r\n \"geometry\":
{\r\n \"coordinates\": [\r\n 15.94107,\r\n 45.79012\r\n ],
\r\n \"type\": \"Point\"\r\n },
\r\n \"properties\": {\r\n \"name\": \"Foo\"\r\n },
\r\n \"type\": \"Feature\"\r\n}"
A related SO question gave me the answer. I needed to change the return in my controller from
return Json(serializedData, JsonRequestBehavior.AllowGet);
to
return Content(serializedData, "application/json");
as the JsonResult was also serializing the serialized data.

How to show MVC logged in username with AngularJs

I am using MVC 5 / WebApi 2 and AngularJs. I want to display the Logged in username in my view. I know how to display that information using razor but how can I do it with Angular? So basically I need to do this with Angular.
<span >Logged In As: #Html.ActionLink(User.Identity.GetUserName(), "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage", #style = "color:white;float:right" })</span>
apiUserController
public class apiUserController : ApiController
{
// GET api/<controller>
public List<ApplicationUser> Get()
{
using (var context = new ApplicationDbContext())
{
List<ApplicationUser> users = new List<ApplicationUser>();
users = context.ApplicationUsers
.ToList();
return users;
}
}
}
Updated
public IHttpActionResult Get()
{
using (var context = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
var user = context.FindById(User.Identity.GetUserId());
var loggedInUser = user.UserName;
return Ok(loggedInUser);
}
}
you'll need to create a service that returns your user information
angular.module('app').factory('Authentication', function ($resource) {
var resource = $resource('/user', {}, {
query: {
method: 'GET',
cache: true
}
});
return resource.get().$promise;
});
* note that you'll need to create and endpoint that will send you the user data as json using web api
once you got it done you'll be able to use it in any controller (let's assume you have a homecontroller, it could be a headercontroller or any other)
angular.module('app').controller('HomeController', ['$scope', 'Authentication', function ($scope, Authentication) {
$scope.authentication = Authentication;
}]);
then use it in your view like:
<span >Logged In As: {{authentication.user.username}} </span>
EDIT:
your api controller as you suggested could be like
public HttpResponseMessage Get()
{
var userId = getCurrentUserId(); //something like that
using (var context = new ApplicationDbContext())
{
ApplicationUser user = new ApplicationUser();
user = context.ApplicationUsers.SingleOrDefault(x=>x.id==userId);
return user;
}
}
try to read http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
for routing try to read this article (I guess you are using web api 2)
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
If you want to cheat a little, you can do this in <head> in your _Layout:
<script type="text/javascript">
(function(myApp) {
myApp.username = "#User.Identity.GetUserName()";
//optional
myApp.otherStuff = "#moreMvcStuff";
})(window.myApp = window.myApp || {});
</script>
Then start your angular app like this:
(function (myApp) {
"use strict";
//var app = set up your angular app
app.run(["$rootScope",
function ($rootScope) {
$rootScope.appSettings = {
username: myApp.username
};
}
]);
})(window.myApp = window.myApp || {});
What you are doing is creating a single value on the window called myApp (or name it whatever you like) and passing it into your IIFE. This gives you access to it inside your angular script, bot only in that on block. So if you want it to stick around, you need to put it in a service or your rootScope.
In the app.run block, you can stick it in your rootScope or wherever you want it.
Now in your views you can display it with {{appSettings.username}}.
I call this "cheating" because it's specifically for MVC or webforms and it's not the "angular way". If you ever migrated to a fully agnostic html/js client (no asp.net mvc) and web APIs, you'd need to do what is in the currently-accepted answer.

MVC json vs. ActionLink

new to programming. I am trying to print a PDF from MVC, it works well if I use Action Link, here is my code:
<%= Html.ActionLink("Print","GeneratePdf","Home", new { fc="Test" },null) %>
public ActionResult GeneratePdf(string fc)
{
Document document = new Document();
MemoryStream workStream = new MemoryStream();
PdfWriter.GetInstance(document, workStream);
document.Open();
document.Add(new iTextSharp.text.Paragraph("\n\n"));
// need to add the user name
iTextSharp.text.Paragraph p = new iTextSharp.text.Paragraph("Name: " + fc);
p.Alignment = 1;
document.Add(p);
document.Close();
byte[] byteInfo = workStream.ToArray();
SendPdfToBrowser(byteInfo);
return null;
}
public void SendPdfToBrowser(byte[] buf)
{
string filename = "Certificate.pdf";
// Prepare the headers.
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
// Write the PDF data.
Response.BinaryWrite(buf);
// Flush the buffer to the browser.
Response.End();
Response.Flush();
Response.Clear();
}
I need to use Json, here is the code:
function PrintChart(fc) {
var fc = "Test";
var url = '<%= Url.Content("~/Home/GeneratePdf") %>';
$.post(url, { fc: fc },
function (content) {
if (content != null) { 5 }
}, "json");
<input type="button" onclick="PrintChart();" value="Print" />
I don't get any errors but it does not generate the PDF file. Thanks in advance.
You cannot use Ajax to download file. The jQuery $.post() will expect the response from the server is text.
To download file in a Ajax way, a general approach is to use a hidden iframe and set the src of the iframe to the URL of the file
<iframe id="hiddenFrame" src="" style="display:none; visibility:hidden;"></iframe>
In PrintChart() create URL including data as query-string and set the src of the iframe:
function PrintChart(fc) {
var fc = "Test";
var url = '<%= Url.Content("~/Home/GeneratePdf") %>';
url += "?fc="+fc;
$('#hiddenFrame').attr('src', url);
}

return PDF in ajax request

I have got an ajax request to my Server where i am creating an PDF File. Now i want to display this file in a new window/tab or just download it. how can i do that?
my request
$.ajax({
url: '/Document/CreatePDF',
type: 'POST',
data: {
docid: documentId,
dataId: array
},
traditional: true,
success: function (data) {
}
});
[HttpPost]
public FileStreamResult CreatePDF(long docid, List<long> dataId)
{
var document = _rep.LoadDocument(docid.ToString(), Server.MapPath("~/Documents/") + docid + ".xml");
var exporter = new PDFExporter(document);
MemoryStream fileStream = exporter.CreatePDF();
byte[] PdfByte = fileStream.GetBuffer();
fileStream.Flush();
fileStream.Close();
HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");
return new FileStreamResult(fileStream, "application/pdf");
}
You cannot use AJAX to download files. The reason for that is because javascript doesn't allow you to save the downloaded content on the client computer, nor to prompt for a Save As dialog. You should use a simple HTML <form> or an anchor:
#using (Html.BeginForm("CreatePDF", "Document", FormMethod.Post, new { id = "myform" }))
{
<button type="submit">Download</button>
}
If you need to pass arguments to this controller action that are known only at the client you could subscribe to the .submit event of this form and then dynamically inject hidden fields into it with the corresponding values and then leave the default action execute. And if the values are known at the server side you should simply use HTML helpers to generate those hidden fields.

How do I get MVC to return a Context type

I have the following code I wrote in Asp.NET and I am trying to convert it to MVC, but not sure how I do this within an Action
HttpContext context;
context.Response.ContentType = "application/pdf";
context.Response.AppendHeader("Content-Disposition", String.Format("attachment;filename={0}", filename));
context.Response.WriteFile(filename);
context.Response.Flush();
context.Response.SuppressContent = true;
public ActionResult Download()
{
var cd = new ContentDisposition
{
Inline = false,
FileName = filename
};
Response.SuppressContent = true;
Response.AppendHeader("Content-Disposition", cd.ToString());
return this.File(filename, MediaTypeNames.Application.Pdf);
}
UPDATE:
So you have a server side script (PDF.axd) which generates the PDF file. You don't have the pdf file stored on your file system. In this case you will need to first fetch the pdf and then stream it to the client:
public ActionResult Download()
{
byte[] pdfBuffer = null;
using (var client = new WebClient())
{
var url = string.Format("PDF.axd?file={0}.pdf", voucherDetail.Guid);
pdfBuffer = client.DownloadData(url);
}
var cd = new ContentDisposition
{
Inline = false,
FileName = "file.pdf"
};
Response.SuppressContent = true;
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(pdfBuffer, MediaTypeNames.Application.Pdf);
}
The usefulness of this controller action is doubtful as you already have a script that does the job.
You meant 'Content' and not 'Context' type, yes?
Maybe this SO post will help:
ASP.NET MVC and text/xml content type

Resources