problems with http file upload to wcf-rest service - wcf

I would like to upload a file using a form like this:
<form action="http://127.0.0.1:8090/MyService/"
method="post" enctype="multipart/form-data">
<p>Select File<br>
<input name="Datei" type="file" size="50">
<input type="submit" value="Submit">
</p>
</form>
And have a wcf service like this:
[ServiceContract]
public interface IDocumentConverter
{
[OperationContract, WebInvoke(UriTemplate = "/", Method = "POST")]
void UploadFile(Stream fileContents);
}
I have two problems with this. When I write the stream back into a file it does not only contain the file data but also some http stuff. This seems odd. Is there nice way to get the raw file data?:
-----------------------------67832811011758
Content-Disposition: form-data; name="Datei"; filename="haha.jpg"
Content-Type: image/jpeg
ÿØÿà...
The original file looks like this:
ÿØÿà...
Secondly I would like to have the filename in wcf. Ideally like this:
[ServiceContract]
public interface IDocumentConverter
{
[OperationContract, WebInvoke(UriTemplate = "/{filename}", Method = "POST")]
void UploadFile(string filename, Stream fileContents);
}
But this is not working, because I don't know how to integrate the filename into the url using the form/input stuff.

Related

getting 405 http error when reading form data in asp.net core web api controller

Since I am new to webapi my project fornt-end is react js and backend is Asp.net core 3.1 webapi
so i am redirecting to page given below with query string format username|redirecturl
username:Identify user
redirecturl:controller url where i will receive posted form data
--Request Url----
https://example.com/anonymouspage.aspx?req=username|https://localhost:5001/api/ControllerName/PostTodoItem
above page will redirect back to controller url which i have share in query string with following given response
---Response---
<form name='ecom' id='test' action='Returm URL' method='post'>
<input type='hidden' name='txtUserName' value='Process Completed Successfully'/>
<input type='hidden' name='givenUserName' value='abc'/>
</form>
---Controller----
[AllowAnonymous]
[HttpPost]
public async Task<string> PostTodoItem()
{
NameValueCollection nvc = Request.Form;
userName = nvc["givenUserName"];
status = nvc["txtUserName"];
}
but i am getting 405 http error i need help on this
i am redirecting to page given below with query string format username|redirecturl
https://example.com/anonymouspage.aspx?req=username|https://localhost:5001/api/ControllerName/PostTodoItem
above page will redirect back to controller url
If you do redirection via HttpResponse.Redirect Method in your .aspx code behind, it would make a GET request to your API action, but you applied [HttpPost] attribute to your API action, which cause the issue.
If you'd like to make a POST request to you API action PostTodoItem, you can refer to the following code snippet.
using (HttpClient httpClient = new HttpClient())
{
// in your code logic
// you may need to generate following contnet
// based on the retrieved from data
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("givenUserName", "abc"),
new KeyValuePair<string, string>("txtUserName", "Process Completed Successfully")
});
var result = httpClient.PostAsync("https://localhost:5001/api/ControllerName/PostTodoItem", content).Result;
//code logic here
//...
Test Result

How do I get the Web Root Path and the Content Root Path in ASP.NET Core?

I am trying to add a root path as a parameter in a View, so I can pass it as a parameter to a PayPal button.
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post" target="_top">
... snip ...
<input type="hidden" name="return" value="#Model.UrlRoot/Manage/Subscription?userId=#Model.User.Id">
... snip ...
</form>
I was sifting through the answers at
How can I get my webapp's base URL in ASP.NET MVC? (20 answers)
and
ASP.NET MVC 6 application's virtual application root path
Since ASP.NET Core is quite different, the Request class no longer contains a .Url property, so most of those answers don't work.
You can inject the IHostingEnvironment into the Controller like this:
public class HomeController : Controller
{
protected readonly IHostingEnvironment _hostingEnvironment;
public HomeController(IHostingEnvironment hostingEnvironment)
{
}
}
In your _ViewImports.cshtml add:
#using Microsoft.AspNetCore.Hosting
#inject IHostingEnvironment HostingEnvironment
Now you can use can use HostingEnvironment and all its properties in your form.
For example HostingEnvironment.WebRootPath or HostingEnvironment.ContentRootPath
I came across Marius Schulz's post. (If you are Marius, please add your answer, contact me and I'll remove mine.)
https://blog.mariusschulz.com/2016/05/22/getting-the-web-root-path-and-the-content-root-path-in-asp-net-core
For some reason my Controllers don't have the IHostingEnvironment injected in the constructor, but they do have the Request object.
In my Controller, I've declared
var urlRoot = $"{Request.Scheme}://{Request.Host}{Url.Content("~")}";
and passed it to MyViewModel
var model = new MyViewModel { UrlRoot = urlRoot };
return View(model);
This takes into account http vs. https, port numbers, and hopefully, the site root if the web site is not rooted at /. Since my site is at / I cannot confirm that Url.Content("~") gets the site root.
With .NET core 2.1 the context is automatically injected into views.
The base path of the request which displayed the view can be accessed like this:
#(Context.Request.PathBase)
In a net core 5 razor page, I added the following to the top of the page:
#inject IHostEnvironment hostEnvironment
then further down in my code, I used the following code with success:
string filePath = $#"{ hostEnvironment.ContentRootPath }\wwwroot\assets\img\users\{ user.ID }.jpg";
if (System.IO.File.Exists(filePath))
{
imageURL = $"{ user.ID }.jpg";
}

Primefaces fileupload mode="Simple" with commandbutton ajax="true" throws nullpointer exception

This is in reference to the following thread
[File upload doesn't work with AJAX in PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: The request content-type is not a multipart/form-data
The problem I experience is Nullpointer on clicking the command button.
Starting from the web.xml
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value>
</context-param>
xhtml
<p:fileUpload id="file" value="#{userBean.uploadedFile}"
mode="simple" required="true" allowTypes="*.xls,*.xlsx"
requiredMessage="#{msg.vrUserUpload}"
invalidFileMessage="#{msg.vrUserUploadInvalidFile}"
multiple="false" fileUploadListener="userBean.fileUploadListener" />
<p:commandButton id="btnUpload" value="#{displayText.btUpload}"
styleClass="button_lite" actionListener="#{userBean.insert}"
ajax="true" update="userMassUploadForm"
process="userMassUploadForm">
</p:commandButton>
UserBean.java
public void fileUploadListener(FileUploadEvent event)
{
uploadedFile = event.getFile();
}
public void insert(){
if(uploadedFile!=null){
System.out.println(uploadedFile.getFileName());
}
else{
System.out.println("The file object is null.");
}
}
Console prints out "The file object is null." whenever ajax="true" and when set to false, works. I could not find a solution for this in the above referred thread.
Please advise.Also please let me know if you want any further information.
From PrimeFaces user guide:
Simple File Upload
Simple file upload mode works in legacy mode with a file input whose value should be an UploadedFile instance. Ajax uploads are not supported in simple upload.

How to use Basic authentication for web browser requests with ServiceStack?

I have a REST API built using ServiceStack. I am using BasicAuthentication without any issues when calling the REST APIs (I am registering the AuthFeature with the BasicAuthProvider).
Now I am trying to build some HTML management pages. These should also be authenticated.
The [Authenticate] attribute redirects to the /login page, so I created the following DTO and matching service to handle logins:
[DefaultView("Login")]
public class SiteLoginService : EnshareServiceBase
{
public object Get(SiteLoginRequest req)
{
return new SiteLoginRequest();
}
public object Post(SiteLoginRequest req)
{
//I am trying to use the registered IAuthProvider, which is the BasicAuthProvider
var authProvider = ResolveService<IAuthProvider>();
authProvider.Authenticate(this, EnshareSession,
new ServiceStack.ServiceInterface.Auth.Auth()
{
Password = req.Password,
UserName = req.UserName
});
return HttpResult.Redirect(req.Redirect);
}
}
[Route("/login")]
public class SiteLoginRequest : IReturn<SiteLoginRequest>
{
public string Redirect { get; set; }
public string Password { get; set; }
public string UserName { get; set; }
}
However, the BasicAuthProvider always throws HttpError: "Invalid BasicAuth credentials" when I fill in username and password on the Login view page and POST these to the SiteLoginService. It is probably because the web browser is not filling in the Basic auth header, but I do not know how to authenticate with filled in username and password.
How to properly authenticate site users against the AuthProvider which works with the existing REST API?
If you are passing the Username & Password as a post, then as you suspect you are not doing Basic Authentication.
This article explains how to do basic authentication with JavaScript. From the article:
function login() {
var username = document.getElementById(this.id + "-username").value;
var password = document.getElementById(this.id + "-password").value;
this.http.open("get", this.action, false, username, password);
this.http.send("");
if (http.status == 200) {
document.location = this.action;
} else {
alert("Incorrect username and/or password.");
}
return false;
}
ServiceStack also supports other forms of authentication including sending a username and password via a POST if that is what you want. If you explain your requirements we can give some recommendations.
I figured I need to include also the CredentialsAuthProvider in the AuthFeature, which will expose /auth/credentials service which I form post a form to.
//this inherits the BasicAuthProvider and is used to authenticate the REST API calls
var myCustomAuthProvider = new CustomAuthProvider(appSettings);
var credentialsProvider = new CredentialsAuthProvider(appSettings);
container.Register<IAuthProvider>(myCustomAuthProvider);
container.Register<CredentialsAuthProvider>(credentialsProvider);
var authFeature = new AuthFeature(() => new EnshareSession(new MongoTenantRepository()),
new IAuthProvider[] {
myCustomAuthProvider,
credentialsProvider
})
So I specified the action in my login form as /auth/credentials, while providing the required UserName and Password fields.
<form action="/auth/credentials" method="post">
<p class="entryfield">
#Html.LabelFor(m => m.UserName, "Login name:")
#Html.TextBoxFor(u => u.UserName)
</p>
<p class="entryfield">
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</p>
<input class="formbutton" type="submit" value="Login" />
</form>
When the form is posted, it hits the authentication code flows properly (TryAuthenticate is called in my IUserAuthRepository and returns true).
Ultimately the request receives a 302 and my login form at /login is redisplayed.
HTTP/1.1 302 Found
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 30 Oct 2013 08:15:54 GMT
X-AspNet-Version: 4.0.30319
X-Powered-By: ServiceStack/3,969 Win32NT/.NET
Location: http://localhost:64944/login?redirect=%2fadmin
Set-Cookie: X-UAId=3; expires=Sun, 30-Oct-2033 08:15:54 GMT; path=/; HttpOnly
It is setting the session cookie (X-AUId) and the user is properly authenticated. Subsequent web browser requests to Services decorated with the Authenticate attribute succeed.
So the only missing part is how to ensure that the user is properly redirected after posting to /auth/credentials.
To ensure the redirection works, a quick look at the has shown that a Continue parameter is expected.
So this is how the login form needs to look like (I reused the Auth class from ServiceStack for the model):
#inherits ViewPage<ServiceStack.ServiceInterface.Auth.Auth>
#{
Layout = "AdminLayout";
}
<form action="/auth/credentials" method="post">
<p class="entryfield">
#Html.LabelFor(m => m.UserName, "Login name:")
#Html.TextBoxFor(u => u.UserName)
</p>
<p class="entryfield">
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
</p>
#Html.HiddenFor(m => m.Continue)
<input type="submit" value="Login" />
</form>
The Continue property is populated in the service from the Redirect property of its model.

Upload a file MVC 4 Web API .NET 4

I'm using the version of MVC that shipped with Visual Studio 2012 express. (Microsoft.AspNet.Mvc.4.0.20710.0)
I assume this is RTM version.
I've found plenty of examples online which all use this code:
public Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
// Read the form data and return an async task.
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
});
return task;
}
But this code always ends up in continueWith where t.IsFaulted == true. The exception reads:
Unexpected end of MIME multipart stream. MIME multipart message is not
complete.
Here is my client form. NOthing fancy, I want to do jquery form pluging for ajax upload, but I can't even get this way to work.
<form name="uploadForm" method="post" enctype="multipart/form-data" action="api/upload" >
<input type="file" />
<input type="submit" value="Upload" />
</form>
I've read that it is caused by the parser expecting /CR /LF at the end of each message, and that bug has been fixed in June.
What I cannot figure out is, if it was really fixed, why isn't it included this version of MVC 4? Why do so many examples on the internet tout that this code works when it does not in this version of MVC 4?
You are missing a name attribute on your file input.
<form name="uploadForm" method="post" enctype="multipart/form-data" action="api/upload" >
<input name="myFile" type="file" />
<input type="submit" value="Upload" />
</form>
Inputs without it will not get submitted by the browser. So your formdata is empty resulting in IsFaulted being asserted.