I use file upload with webapi in my project. I am testing with Postman. However, Request.Content.IsMimeMultipartContent() always returns false.
Postman screenshot:
FileUploadController Code:
public async Task<HttpResponseMessage> UserImageUpload()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var userImageUploadPath = HttpContext.Current.Server.MapPath(CommonParameters.UserProfileImageServerPath);
var streamProvider = new CustomMultipartFormDataStreamProvider(userImageUploadPath);
await Request.Content.ReadAsMultipartAsync(streamProvider);
var files = new List<string>();
foreach (MultipartFileData file in streamProvider.FileData)
{
files.Add(Path.GetFileName(file.LocalFileName));
}
return Request.CreateResponse(HttpStatusCode.OK, files);
}
catch (Exception exception)
{
logger.ErrorFormat("An error occured in UserImageUpload() Method - Class:FileUploadController - Message:{0}", exception);
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
This is Postman bug. Try removing the Content-Type header. When sending the actual Post, the browser will automatically add the proper header and create the boundary.
There is no need to mention Content-Type in headers in Postman, I tried sending attachments without Content-Type it works fine for me.
When i used Content-Type: multipart/formdata it throws an error saying "Could not get any response". Postman sends your file attachments also with Content-Type →text/plain; charset=utf-8.
There are few things to consider:
1) Don't send any Content Type Header if you are using Postman
2) Specify the Key for your choosen file in Body (PFB Screenshot for your reference)
You need to uncheck Content-Type if you have it checked, and let Postman add it for you, like in this picture:
Might by a bit late. I encountered the same error in ARC and resolved by providing a name for the file field (after the blue check mark on your second screenshot)
Related
Under Asp.Net MVC I used to build the body of my mailing messages on a MVC view which the system downloaded through System.Net.HttpWebRequest.
Now that I am migrating to Blazor server, I can browse the page but if I try to download it to fill the body of the message I always get next body:
Loading... An unhandled exception has occurred. See browser dev
tools for details. Reload X
I tried both through a Blazor page and through a Cshtml razor page. My browser can see both successfully but I always get the same exception.
This is the code I use to download the page:
HttpResponseMessage response = await http.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var contentType = response.Content?.Headers?.ContentType?.MediaType;
if (contentType == "application/json" | contentType == "text/html")
{
string responseText = await response.Content?.ReadAsStringAsync() ?? "";
if (typeof(T) == typeof(string))
retval.Value = (T)(object)responseText;
else
retval.Value = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText);
}
else
{
byte[] result = await response.Content.ReadAsByteArrayAsync();
retval.Value = (T)Convert.ChangeType(result, typeof(T));
}
}
}
I finally discovered the problem was on Program.cs from my .Net7 Server side Blazor app.
I was registering HttpClient as follows:
var http = new HttpClient();
builder.Services.AddScoped(sp => http);
This was Ok to access API data, but for some reason if you try to download a Html page source it throws a System.ObjectDisposedException: Cannot access a disposed object.
The right way to register the service to avoid this exception is:
builder.Services.AddHttpClient();
The problem is I no longer have a http variable in Program.cs, which I used to preload data from Api before the index page was ready.
Need a bit more detail:
What line threw the exception?
What was the exception?
What was value of string responseText or byte[] result?
I suspect either the DeserializeObject or Convert.ChangeType call failed. You should debug this; the answer will probably become apparent as you step through the code.
I am creating a proxy using middleware in ASP.NET Core 2.1 that makes 3rd party API (OData endpoint) call to
Get data
Do some changes
Send response to Response.Body
I took a reference from here
Below is the code snippet that works fine as whatever response I am getting from API, I am sending it further
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
However, If I modify the response here, for example, like this, it does not work and it shows blank page without any error.
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
var output = new StringContent("some sample string or may be JSON", Encoding.UTF8, "application/json");
await output.CopyToAsync(context.Response.Body);
}
It looks like we are not allowed to make any change in the response received from API call. Can anyone please tell me how can send modified content back to Response.Body?
I am able to solve the problem by updating "Content-Length" response header before rendering modified response to context.Response.Body something like this:
context.Response.Headers.Remove("Content-Length");
context.Response.Headers.Add("Content-Length", modifiedResponseStream.Length.ToString());
You might run into a System.InvalidOperationException: Response Content-Length mismatch: too few bytes written or similar exception (which you should see in the Output window). So do not use the Content-Length and maybe Content-Type headers from the response, because they probably don't match with the Content-Length and Content-Type of your modified content, e.g.:
private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
{
...
foreach (var header in responseMessage.Content.Headers)
{
// do not use the content headers from the response because the content will be modified
// context.Response.Headers[header.Key] = header.Value.ToArray();
}
...
}
I have a REST api endpoint like this -
#RequestMapping(value = "/createform/custom/name", method = RequestMethod.POST)
public String nameSubmit(#RequestBody String name) {
return "you have submitted this name ***** "+name;
}
From angular service I tried to make a REST call like this -
var data = 'name='+inputName;
$http.post(uploadUrl, data, {
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(
function(success){
alert(success.data);
},
function(error){
alert(error.status);
}
);
Now I always get a -1 HTTP status and control goes to error block. I also tried #RequestParam instead of #RequestBody but no luck. But if I try to access the service through curl or chrome postman , everything works fine. Only when I try through angular application I get stuck with -1 response.
It was a CORS issue. I tried the Spring proposed solutions first. But the "gs-rest-service-cors" jar was not found in Maven repository. Hence ended up writing a filter as shown here and things worked fine.
I'm trying to get file uploads working with the new OneDrive API. I'm starting out with just simple files (i.e. < 100MB) with the intention of progressing to resumable uploads once I've got the simple ones working!
I've tried using http://onedrive.github.io/items/upload_put.htm but I'm getting 403 back. I thought that this might be because the file didn't already exist but I've uploaded the file using the OneDrive web UI successfully and the code still can't upload the file.
The URL I'm using is something like:
https://api.onedrive.com/v1.0/drive/root:/:/content?access_token=
The C# code is:
using (Stream fileStream = await file.OpenStreamForReadAsync())
{
try
{
HttpStreamContent streamContent = new HttpStreamContent(fileStream.AsInputStream());
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, oneDriveURI);
request.Content = streamContent;
request.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("application/octet-stream");
HttpResponseMessage response = await client.SendRequestAsync(request);
Debug.WriteLine("UploadFileToOneDrive: response = {0}", response.StatusCode);
}
catch (Exception ex)
{
Debug.WriteLine("UploadFileToOneDrive failed with exception {0}", ex.Message);
}
}
What have I got wrong/misunderstood? The API documentation needs a LOT more examples :-(
403 error code is related to permissions so it could be possible that you may have forgotten to include the proper scopes to upload the file. When you send your OAuth request, you'll want to also include "onedrive.readwrite" as one of the scopes.
GET https://login.live.com/oauth20_authorize.srf?client_id={client_id}&scope={scope}&response_type=token&redirect_uri={redirect_uri}
More scopes can be found at "http://onedrive.github.io/auth/msa_oauth.htm". I hope that helps.
I'm using FOSRestBundle for my REST API and so far it has been a great tool. I use HTTP Basic Auth and in most of the cases it works just fine. However, I have problems with the bundle's exception behaviour when bad credentials are submitted. When handling exceptions (via the integrated authentication handlers or the exception mapping configuration), the bundle always gives me a response with the correct HTTP status and JSON/XML content similar to this:
{
"code": 401,
"message": "You are not authenticated"
}
This is fine, it also works when no authentication information is submitted at all. However, when submitting bad credentials (e.g. unknown username or incorrect password) I get the HTTP code 401 Bad credentials (which is fine) with an empty message body. Instead, I would have expected something similar to the JSON above.
Is it a bug or a configuration issue on my side? I would also love to know how these kinds of authentication errors are exactly handled by the bundle, since overriding the BadCredentialsException's status code in the codes section of the bundle's exception configuration section seems to be ignored.
Thanks!
Alright, after digging into the bundle's code some more, I figured it out. The problem results from the way bad credentials are handled by Symfony's HTTP Basic Authentication impementation. The 401 Bad Credentials response is a custom response created by BasicAuthenticationEntryPoint, which is called by the BasicAuthenticationListener's handle function, immediately after an AuthenticationException has been thrown in the same function. So there is no way of catching this exception with a listener:
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
if (false === $username = $request->headers->get('PHP_AUTH_USER', false)) {
return;
}
if (null !== $token = $this->securityContext->getToken()) {
if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $username) {
return;
}
}
if (null !== $this->logger) {
$this->logger->info(sprintf('Basic Authentication Authorization header found for user "%s"', $username));
}
try {
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
$this->securityContext->setToken($token);
} catch (AuthenticationException $failed) {
$this->securityContext->setToken(null);
if (null !== $this->logger) {
$this->logger->info(sprintf('Authentication request failed for user "%s": %s', $username, $failed->getMessage()));
}
if ($this->ignoreFailure) {
return;
}
$event->setResponse($this->authenticationEntryPoint->start($request, $failed));
}
}
The entry point's start function creates the custom response, with no exceptions involved:
public function start(Request $request, AuthenticationException $authException = null)
{
$response = new Response();
$response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName));
$response->setStatusCode(401, $authException ? $authException->getMessage() : null);
return $response;
}
The fist if-clause in the handle function above also explains why it works in the case of "no user credentials at all", since in that case, the listener just stops trying to authenticate the user, and therefore an exception will be thrown by Symfony's firewall listeners (not quite sure where exactly), so FOSRestBundle's AccessDeniedListener is able to catch the AuthenticationException and do its thing.
You can extend AccessDeniedListener and tell FOSRestBundle to use your own listener with the parameter %fos_rest.access_denied_listener.class%. (service definition)
parameters:
fos_rest.access_denied_listener.class: Your\Namespace\For\AccessDeniedListener
Then add an additional check for BadCredentialsException and emmit an HttpException with the desired code/message similar to the check for AuthenticationException at Line 70.