Web API controller POST attempts a file download on client - asp.net-mvc-4

I've created an Web API controller in my MVC project. Whenever I try to POST to this controller, it try's to initiate a File download, and I am not sure why. It happens if I navigate to the API controller via URL or if it's done via a Form post.
It's a very simple scenario where an File is to be uploaded via a post.
Here is the controller code. Any ideas?
public class UploadController : ApiController
{
public async Task<List<string>> Post()
{
// Verify that this is an HTML Form file upload request
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
// Create a stream provider for setting up output streams that saves the output under c:\tmp\uploads
// If you want full control over how the stream is saved then derive from MultipartFormDataStreamProvider
// and override what you need.
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider("c:\\tmp\\");
var task = await Request.Content.ReadAsMultipartAsync(streamProvider);
return new List<string>();
}
Here is the client code:
<!DOCTYPE HTML>
<html>
<head>
<title>HTML5 Multiple File Upload Sample</title>
</head>
<body>
<form action="http://localhost:8080/api/upload" enctype="multipart/form-data" method="POST">
What is your name?
<input name="submitter" size="40" type="text"><br>
What files are you uploading?
<input name="data" type=file multiple>
<br>
<input type="submit" />
</form>
</body>
</html>

I suspect that you are using Internet Explorer to test this example, aren't you? But this browser always prompts for download when the server sends JSON (yeah IE sucks like hell, we all know that, please use a real web browser when you are doing web development). Your controller action returns JSON that IE is incapable of displaying inline and it asks you to save it on the client. I don't know what else did you expect? Your controller action returns an empty array as JSON. You have an HTML form to upload a file and the result of this file upload is an empty JSON array that IE doesn't know how to handle.
If you use an HTML client form you probably wanna return HTML from this API controller action, right?

Related

Microsoft WebHelpers with NETCore.App (2.1)

I'm trying to get the below code to work, but I keep getting compatibility problems with Microsoft.Web.Helpers v 3.2.6 and my current SDK package of NETCore 2.1. Also, for the life of me, I can't get the simplest calls of IsPost and Request to be recognized. I'm sure it's an obvious fix, but I can't find it!
Thanks in Advance for any direction...
#using Microsoft.Web.Helpers;
#{
var fileName = "";
if (IsPost) {
var fileSavePath = "";
var uploadedFile = Request.Files[0];
fileName = Path.GetFileName(uploadedFile.FileName);
fileSavePath = Server.MapPath("~/App_Data/UploadedFiles/" +
fileName);
uploadedFile.SaveAs(fileSavePath);
}
}
<!DOCTYPE html>
<html>
<head>
<title>FileUpload - Single-File Example</title>
</head>
<body>
<h1>FileUpload - Single-File Example</h1>
#FileUpload.GetHtml(
initialNumberOfFiles:1,
allowMoreFilesToBeAdded:false,
includeFormTag:true,
uploadText:"Upload")
#if (IsPost) {
<span>File uploaded!</span><br/>
}
</body>
</html>
The WebHelpers library is not compatible with ASP.NET Core. It relies on System.Web, which .NET Core has been designed to move away from.
The replacement for the IsPost block is a handler method. By convention, a handler method named OnPost will be executed if the method used to request the page is POST (which is what the IsPost property used to check).
Personally, I never understood the point of the FileUpload helper unless you wanted to allow the user to add additional file uploads to the page (which you clearly don't in this case). An input type="file" is easier to add to a page.
File uploading in ASP.NET Core is completely different to Web Pages. Here's some guidance on it: https://www.learnrazorpages.com/razor-pages/forms/file-upload

Opening HDevelop examples from internal function description

We have an extensive internal Halcon library, which is used by more and more people. Now we have arrived at the point, where it would be very useful if we could directly access HDevelop examples written specifically for this function as it is already available for MVTec's functions.
This is not possible at the moment, and MVTec will not provide this in the future (their words).
I have made some progress but I'm not able to find a solution.
If a text is filled in one of the boxes in Procedures/Edit Interface/General Documentation/Suggestions it generates an HTML link. Is is possible to start HDevelop using Internet Explorer:
<html>
<head>
<script type="text/javascript">
function foo() {
var WshShell = new ActiveXObject("WScript.Shell");
var oExec = WshShell.Exec("hdevelop.exe");
var input = "";
while (!oExec.StdOut.AtEndOfStream) {
input += oExec.StdOut.ReadLine() + "<br />";
}
if (input)
document.getElementById("dummy").innerHTML = input;
}
</script>
</head>
<body onload="foo();">
<code id="dummy"></code>
</body>
</html>
Unfortunately if the reference to an HTML document is local, it is opened inside the HDevelop's browser, which does not support ActiveX objects. If the reference starts with http, it is opened in your default browser.
Does anyone else have another solution which wouldn't require local server to access local files and setting your default browser to IE?
It is possible to do it by generating a simple HTML file:
<html>
<body>
<TT>Example</TT>
</body>
</html>
and referencing it in any of the Suggestions fields without the .html extension:
There is going to be one step more when accessing the example, where you have to click on the hyperlink "Example" because automatic redirecting is not working.

Refreshing Html.Action

I have a partial view rendered with an Html.Action() that I want to refresh on a button click. I've tried AJAX requests, but the data I'm passing back and forth exceeds the maximum length for JSON.
The basic structure of the page looks like:
<html>
<head>
...
</head>
<body>
<div>
#Html.Action("DisplayBox")
</div>
<div>
<input type="button" id="RefreshButton" value="Refresh Box" />
</div>
</body>
</html>
The reason why I'm asking for a method other than an AJAX request is that the partial I'm rendering is a PDF object:
#model byte[]
#{
String base64EncodedPDF = System.Convert.ToBase64String(Model);
Layout = null;
}
<object data="data:application/pdf;base64,#base64EncodedPDF"
width="900" height="900" type="application/pdf"></object>
Thus, the data passed to the partial view for rendering is too big to put in an AJAX request. On button click, I want to be able to execute the controller action and have the results update the partial with new data. Is there any way of doing this?
You have to load the HTML with the link to the controller that generate the PDF or generate the file on the server side, host it and return the URL of this PDF, then, javascript can redirect user to that file.
I don't think that returning file trough AJAX is really not a good practice!

Cloudinary jQuery Direct Upload issue

I am implementing Cloudinary Jquery Upload. From my file upload webpage, if I surf to another website ( google.com, or any external website), and then click on the back button on the browser into this same file upload page, the upload fails.
The error message I gotten back is (from Firebug):
400 Bad Request
{"error":{"message":"Upload preset Must specify upload preset when using unsigned upload”}}
I did not enable unsigned upload on the Cloudinary management console
because my intention is a signed upload
This is the JSON data that is created at the backend for data-form-data:
{"timestamp":1409146953,"callback":"http://newappsure.herokuapp.com/vendor/cloudinary/cloudinary_cors.html","signature":"19071a3e822eed51238454e359589f52cccca042","api_key":"224456847515364”}
Below is the javascript and input HTML:
<script type="text/javascript”>
$.cloudinary.config({cloud_name:'dashy', api_key:’XXXXXXXXXXXXXXX'});
</script>
<input name="file" type="file" id="uploadinput" class="cloudinary-fileupload" data-cloudinary-field="image_upload"
data-form-data="" ></input>
<script>
$.ajax({
url: '/filer',
type: 'POST',
success: function(response){
$('#uploadinput').attr('data-form-data', response);
}
});
</script>
This is the Ruby backend that generates JSON:
post '/filer' do
ts = Time.now.getutc.to_time.to_i.to_s
secret="XXXXXXXXXXXXXXXXXXXXXX"
altogether="callback=http://newappsure.herokuapp.com/vendor/cloudinary/cloudinary_cors.html&timestamp="+ts+secret
sig=Digest::SHA1.hexdigest altogether
ts = Time.now.getutc.to_time.to_i
{:timestamp => ts, :callback => "http://newappsure.herokuapp.com/vendor/cloudinary/cloudinary_cors.html", :signature => sig, :api_key =>"XXXXXXXXXXXXXXXX"}.to_json
end
Please help me understand what did I do wrong?
While your solution may work, the more optimal way is to update the upload parameters to call $(...).fileupload({formData: data}) where data is the parameters hash (not JSON serialized).
For more information:
http://support.cloudinary.com/entries/24950218-Why-is-updating-a-cloudinary-fileupload-field-dynamically-not-working-
Got it working by forcing the page to reload with the following snippets (ref: https://stackoverflow.com/a/9217531/3781343 and http://www.webdeveloper.com/forum/showthread.php?137518-How-to-refresh-page-after-clicking-quot-Back-quot-button)
<input type="hidden" id="refreshed" value="no">
<script type="text/javascript">
onload=function(){
var e=document.getElementById("refreshed");
if(e.value=="no")e.value="yes";
else{e.value="no";location.reload();}
}
</script>

AngularJS authentication - Incompatibility between directives?

I'm really new on Angular JS development and I'm trying to implement the following authentication module : https://github.com/witoldsz/angular-http-auth (more info at http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application), to my project .
THE MODULE
The module has been thought to allow the following scenario :
user asks for: something.com/secured/formXyz,
server sends a login form,
user logs in, fills a long and complicated form, but they are doing it so long that theirs session expires,
user submits a form, but since the session is not valid anymore, login screen appears,
once user logs in, server can process the submitted form, **no need to re-enter everything again**.
The solution to do such a thing is :
server side behavior :
for every /resources/* call, if user is not authorized, response a 401 status. Otherwise, when user is authorized or when not a /resources/* request, send what client asked for.
client side behavior :
capture 401 response,
save the request parameters, so in the future we can reconstruct original request,
create and return new object representing server’s future answer (instead of returning the original failed response),
broadcast that login is required, so application can react, in particular login form can appear,
listen to login successful events, so we can gather all the saved request parameters, resend them again and trigger all the ‘future’ objects (returned previously).
MY PROJECT
My project is a basic one, which uses the $route service. When I try to add the directive which catch the events :
scripts/directives/login-directive.js
angular.module('myApp', ['http-auth-interceptor','content-mocks'])
/**
* This directive will find itself inside HTML as a class,
* It is responsible for showing/hiding login form.
*/
.directive('authDirective', function() {
return {
restrict: 'C',
link: function(scope, elem, attrs) {
var login = elem.find('#login');
var main = elem.find('#main');
login.hide();
main.show();
scope.$on('event:auth-loginRequired', function() {
login.show();
main.hide();
});
scope.$on('event:auth-loginConfirmed', function() {
main.show();
login.hide();
});
}
}
});
to my index.html :
<body ng-app='myApp' class='auth-directive'>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an outdated browser. Upgrade your browser today or install Google Chrome Frame to better experience this site.</p>
<![endif]-->
<!--[if lt IE 9]>
<script src="components/es5-shim/es5-shim.js"></script>
<script src="components/json3/lib/json3.min.js"></script>
<![endif]-->
<div id="login" name="login">
<p>login</p>
</div>
<div id="main" name="main">
<p>main</p>
<div class="container" ng-view></div>
</div>
<script src="components/angular/angular.js"></script>
<script src="components/angular-resource/angular-resource.js"></script>
<script src="components/angular-cookies/angular-cookies.js"></script>
<script src="components/angular-sanitize/angular-sanitize.js"></script>
<script src="components/angular-http-auth/angular-mocks-1.0.1.js" type="text/javascript"></script>
<script src="components/angular-http-auth/http-auth-interceptor.js" type="text/javascript"></script>
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
<script src="scripts/directives/login-directive.js"></script>
<script src="scripts/mocks/content-mocks.js" type="text/javascript"></script>
</body>
The directive ngView doesn't work anymore. Nothing appears on console log. Is there any incompatibility between the two directives ?
By the look of your login-directive files it seems that you are (improperly) reinitialising you Angular app by passing the dependencies array as a second parameter to your angular.module call:
angular.module('myApp', ['http-auth-interceptor','content-mocks'])
Dependencies should be defines only once, when app is initialised (probably in app.js), and when you want to reference your application in another file you should call the angular.module without the second parameter:
angular.module('myApp')
.directive('authDirective', function() { ...