I got the files to send by Retrofit with Android using Multiparti however on my server I work with .Net C# to build the Restful Service, then How can i create the restful to receive the files multiparti from Retrofit / Android?
sample:
[RoutePrefix("rest/files")]
public class ReceiveImagesController : ApiController
{
[AcceptVerbs("POST")]
[Route("SendFiles")]
public string sendFiles()
{
string retorno = "";
string path = "C:/temp";
// byte[] Bytes = new byte[files.Inpu]
return retorno;
}
}
My sample code and I use file upload in webapi 2. I think your problem will solve below codes.
sing System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace WebMvcTest.Controllers
{
[System.Web.Http.RoutePrefix("api/test")]
public class FileUploadController : ApiController
{
[System.Web.Http.Route("files")]
[System.Web.Http.HttpPost]
[ValidateMimeMultipartContentFilter]
public async Task<FileResult> UploadSingleFile()
{
var streamProvider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(streamProvider);
string descriptionResult = string.Empty;
var description =
streamProvider.Contents.AsEnumerable()
.FirstOrDefault(T => T.Headers.ContentDisposition.Name == "\"description\"");
if (description != null)
{
descriptionResult = await description.ReadAsStringAsync();
}
return new FileResult
{
FileNames = streamProvider.Contents.AsEnumerable().Select(T => T.Headers.ContentDisposition.FileName).ToArray(),
Names = streamProvider.Contents.AsEnumerable().Select(T => T.Headers.ContentDisposition.FileName).ToArray(),
ContentTypes = streamProvider.Contents.AsEnumerable().Where(T => T.Headers.ContentType != null).Select(T => T.Headers.ContentType.MediaType).ToArray(),
Description = descriptionResult,
CreatedTimestamp = DateTime.UtcNow,
UpdatedTimestamp = DateTime.UtcNow,
};
}
}
}
Related
I am attempting to retrieve Azure AD user profile data via the Microsoft Graph API. I have set up a small Visual Studio MVC app using code examples from various sources, primarily Microsoft. In my ignorance, I thought this would be a fairly simple process.
I have browsed other similar cases on SO and have attempted to make use of suggestions from others but to no avail. I have been troubleshooting this issue for four days and would greatly appreciate any assistance.
// UserProfileController.cs
-- contains the calling method: var graphToken = await AuthenticationHelper.GetGraphAccessToken();
//
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using SSO_Test.Models;
using SSO_Test.Utils;
using System.Net.Http.Headers;
namespace SSO_Test.Controllers
{
[Authorize]
public class UserProfileController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
private string clientId = ConfigurationManager.AppSettings["ClientId"];
private string appKey = ConfigurationManager.AppSettings["ClientSecret"];
private string aadInstance = ConfigurationManager.AppSettings["AADInstance"];
private string graphResourceID = "https://graph.microsoft.com";
// GET: UserProfile
public async Task<ActionResult> Index()
{
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
try
{
var graphToken = await AuthenticationHelper.GetGraphAccessToken();
var authenticationProvider = new DelegateAuthenticationProvider(
(requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", graphToken);
return Task.FromResult(0);
});
var graphClient = new GraphServiceClient(authenticationProvider);
var user = await graphClient.Me.Request().GetAsync();
return View(user);
}
catch (AdalException ex)
{
// Return to error page.
ViewBag.Message = ex.Message;
return View("Error");
}
// if the above failed, the user needs to explicitly re-authenticate for the app to obtain the required token
catch (Exception)
{
return View("Relogin");
}
}
public void RefreshSession()
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/Home" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
//AuthenticationHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using SSO_Test.Models;
namespace SSO_Test.Utils
{
public static class AuthenticationHelper
{
public static async Task<string> GetGraphAccessToken()
{
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst(SettingsHelper.ClaimTypeObjectIdentifier).Value;
var clientCredential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
var userIdentifier = new UserIdentifier(userObjectId, UserIdentifierType.UniqueId);
// create auth context
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureAdAuthority, new ADALTokenCache(signInUserId));
//added check point for verification purposes
System.Diagnostics.Debug.WriteLine("Check point #1");
//GOOD TO THIS POINT
var result = await authContext.AcquireTokenSilentAsync(SettingsHelper.AzureAdGraphResourceURL, clientCredential, userIdentifier);
//ERROR MESSAGE: "Failed to acquire token silently as no token was found in the cache. Call method AcquireToken"
System.Diagnostics.Debug.WriteLine("Check point #2");
//app never reaches the second check point
return result.AccessToken;
}
}
}
//ADALTokenCache.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Security;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace SSO_Test.Models
{
public class ADALTokenCache : TokenCache
{
private ApplicationDbContext db = new ApplicationDbContext();
private string userId;
private UserTokenCache Cache;
public ADALTokenCache(string signedInUserId)
{
// associate the cache to the current user of the web app
userId = signedInUserId;
this.BeforeAccess = BeforeAccessNotification;
this.AfterAccess = AfterAccessNotification;
this.BeforeWrite = BeforeWriteNotification;
// look up the entry in the database
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
// place the entry in memory
this.DeserializeAdalV3((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
}
// clean up the database
public override void Clear()
{
base.Clear();
var cacheEntry = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
db.UserTokenCacheList.Remove(cacheEntry);
db.SaveChanges();
}
// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
if (Cache == null)
{
// first time access
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
}
else
{
// retrieve last write from the DB
var status = from e in db.UserTokenCacheList
where (e.webUserUniqueId == userId)
select new
{
LastWrite = e.LastWrite
};
// if the in-memory copy is older than the persistent copy
if (status.First().LastWrite > Cache.LastWrite)
{
// read from from storage, update in-memory copy
Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
}
}
this.DeserializeAdalV3((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
}
// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (this.HasStateChanged)
{
Cache = new UserTokenCache
{
webUserUniqueId = userId,
//cacheBits = MachineKey.Protect(this.Serialize(), "ADALCache"),
cacheBits = MachineKey.Protect(this.SerializeAdalV3(), "ADALCache"),
LastWrite = DateTime.Now
};
// update the DB and the lastwrite
db.Entry(Cache).State = Cache.UserTokenCacheId == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
this.HasStateChanged = false;
}
}
void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}
public override void DeleteItem(TokenCacheItem item)
{
base.DeleteItem(item);
}
}
}
//ApplicationDbContext.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace SSO_Test.Models
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserTokenCache> UserTokenCacheList { get; set; }
}
public class UserTokenCache
{
[Key]
public int UserTokenCacheId { get; set; }
public string webUserUniqueId { get; set; }
public byte[] cacheBits { get; set; }
public DateTime LastWrite { get; set; }
}
}
As you can see, I have noted in the GetGraphAccessToken() method the error message:
"Failed to acquire token silently as no token was found in the cache.
Call method AcquireToken".
I was able to isolate the AcquireTokenSilentAsync() method as the culprit by bracketing it with a pair of Debug.Writeline statements, the first which ran successfully and the second which did not. This was verified by reviewing the contents of the VS Output window, as follows:
Check point #1
Exception thrown:
'Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException'
in mscorlib.dll
The program '[13980] iisexpress.exe' has exited with code -1
(0xffffffff).
I really want this thing to work and I would much prefer utilizing the Graph SDK approach as opposed to using a REST API.
Again, I have been banging my head against the wall for four-plus days. My head is okay but the wall is in bad shape.
Thanks in advance.
If AcquireTokenSilent fails, it means that there is no token in the cache so you have to go get one via AcquireToken as in this.
You've tagged the question with "B2C" but is looks like you are using Azure AD?
There are a full set of Authentication Providers for the standard set of OAuth flows that are now available so you don't have to use the DelegatedAuthenticationProvider any more. https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth There are docs about how to chose the right Auth provider based on scenario here https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS
I am currently trying to authenticate Graph API using my C#. I am able to query this API and receive the token successfully from Postman.But when I call same API, I get 404 error.
My code is as below:
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace GraphTest
{
public class AuthenticationModel
{
public string grantType { get; set; } = "client_credentials";
public string clientId { get; set; } = "my_ad_app_id";
public string clientSecret { get; set; } = "client_secret";
public string scope { get; set; } = "https://graph.microsoft.com/.default";
}
public class Authentication
{
private static string tenantId = "tenant_id";
private static readonly HttpClient Client = new HttpClient();
public Authentication()
{
var authenticationModel = new AuthenticationModel();
RunAsync().GetAwaiter().GetResult();
}
private static async Task RunAsync()
{
Client.BaseAddress = new Uri("https://login.microsoftonline.com/");
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("*/*"));
Client.DefaultRequestHeaders.Add("Host", "login.microsoftonline.com");
Client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
try
{
var authenticationModel = new AuthenticationModel();
var url = await GetTokenAsync(authenticationModel);
Console.WriteLine($"Created at {url}");
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
}
private static async Task<Uri> GetTokenAsync(AuthenticationModel authenticationModel)
{
var keyValues = authenticationModel.GetType().GetProperties()
.ToList()
.Select(p => $"{p.Name} = {p.GetValue(authenticationModel)}")
.ToArray();
var xUrlEncodedBody = string.Join('&', keyValues);
var response = await Client.PostAsJsonAsync(
$"{tenantId}/oauth2/v2.0/token", xUrlEncodedBody);
response.EnsureSuccessStatusCode();
return response;
}
}
}
So, I recieve this in response: StatusCode:404, ReasonPhrase:Not Found
Please help me in knowing that where I am doing it wrong.
Note: API with same data works fine with Postman. Though, I have replaced some values here for security reasons.
You should not post form URL encoded content as JSON (PostAsJsonAsync). The body needs to have a content type of application/x-www-form-urlencoded.
But backing up a second, you don't need to implement the protocol yourself when there are libraries out there that do it for you :). We provide and support the Microsoft Authentication Library (MSAL) which makes this easy.
var app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithTenantId("{tenantID}")
.WithClientSecret(config.ClientSecret)
.Build();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch (MsalServiceException ex)
{
Console.WriteLine($"Error getting token: {ex.Message}");
}
Console.WriteLine($"Token: {result.AccessToken}");
I ran your code and it is wrongly generating query string as below with spaces.
xUrlEncodedBody => grantType = client_credentials&clientId = my_ad_app_id&clientSecret = client_secret&scope = https://graph.microsoft.com/.default
There is space between query parameter name and value, see below line.
.Select(p => $"{p.Name} = {p.GetValue(authenticationModel)}")
Remove the space and try again
.Select(p => $"{p.Name}={p.GetValue(authenticationModel)}")
I'm new to .NET and I'm making a web api with asp.net. I'm trying to post a file to AWS S3 using the AWS .Net SDK.
It works but not if I try to read the fileName and send that as the key. It only works if I hardcode the key to a string (whereas I always want the key to be the same as the filename of the uploaded file).
When I try to read the filename from the file the error I'm getting is "Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed."
So far I'm just using Postman to POST the files. I set the "Content-Type" header to multipart/form-data but exactly the same thing happens if I set it to application/x-www-form-urlencoded.
This is my UploadController:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using BlogApi.Models;
using System.Web;
using awsTestUpload;
using Amazon.S3.Model;
using Microsoft.AspNetCore.Http;
namespace BlogApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class S3UploadController : ControllerBase
{
public S3UploadController()
{
}
[HttpGet]
public ListObjectsResponse GetAll()
{
var uploader = new AmazonUploader();
return uploader.ListingObjectsAsync().Result;
}
[HttpPost]
public PutObjectResponse MyFileUpload()
{
var request = HttpContext.Request;
var fileStream = request.Body;
var contentLength = request.ContentLength;
string filePath = request.Form.Files.First().FileName;
var length = contentLength.HasValue ? (long)contentLength : 0;
var uploader = new AmazonUploader();
return uploader.sendMyFileToS3(fileStream, filePath, length).Result;
}
}
}
and the Uploader class looks like this:
using System;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using System.IO;
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using Microsoft.Extensions.Configuration;
namespace awsTestUpload
{
public class AmazonUploader
{
public AmazonUploader() {
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
client = new AmazonS3Client(Configuration["aws:AWS_KEY"],
Configuration["aws:AWS_SECRET"], bucketRegion);
}
private IAmazonS3 client;
public static IConfiguration Configuration { get; set; }
private const string bucketName = "my-bucket-name";
private static readonly RegionEndpoint bucketRegion = RegionEndpoint.EUWest2;
public async Task<ListObjectsResponse> ListingObjectsAsync()
{
ListObjectsRequest request = new ListObjectsRequest
{
BucketName = bucketName
};
return await client.ListObjectsAsync(request);
}
public async Task<PutObjectResponse> sendMyFileToS3(System.IO.Stream inputStream, string fileNameInS3, long contentLength = 0)
{
PutObjectRequest request = new PutObjectRequest
{
BucketName = bucketName,
Key = fileNameInS3,
InputStream = inputStream
};
request.Headers.ContentLength = contentLength;
return await client.PutObjectAsync(request);
}
}
}
When I debug and set a breakpoint, the value of filePath is a string matching the file name ( as I expect) but the upload is timing out. if I just set filePath to be a hardcoded string (ie replace string filePath = request.Form.Files.First().FileName; with string filePath = "foo.png";) it works fine.
Can anyone see why there's a difference?
I have a Middleware that logs the request information in my ASP.Net core API project, but the proble is when I read the request in the middleware, I can't read the same request in my Controller.
Who can I read the Json request in my Middleware and in my Controller?
Middleware Comtroller
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using srpago_oxxo.Library.Logger;
using srpago_oxxo.Library.Logger.Handlers;
using srpago_oxxo.Library.Logger.Formatters;
using Microsoft.AspNetCore.Http.Internal;
namespace srpago_oxxo.Middlewares
{
public class IOMiddleware
{
private readonly RequestDelegate _next;
private Logger logger;
public IOMiddleware(RequestDelegate next)
{
_next = next;
logger = new Logger("SRPAGO-OXXO");
var formatter = new WebFormatter();
var handler = new StdoutHandler();
handler.SetFormatter(formatter);
logger.AddHandler(handler);
}
public async Task Invoke(HttpContext context)
{
var contentLenth = context.Request.ContentLength.ToString();
contentLenth = contentLenth == "" ? "-" : contentLenth;
var info = new Dictionary<string, string>()
{
{"{code}", "200"},
{"{clientIp}", context.User.ToString()},
{"{method}", context.Request.Method},
{"{path}", context.Request.Path.Value},
{"{bodyLength}", contentLenth},
{"{message}", await GetJsonRequest(context)},
};
logger.Info(info);
await _next.Invoke(context);
}
private async Task<string> GetJsonRequest(HttpContext context)
{
var body = context.Request.Body;
context.Request.EnableRewind();
var request = context.Request;
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
request.Body = body;
if (bodyAsText == "")
{
bodyAsText = "{}";
}
return bodyAsText;
}
}
}
This is my Base Controller
using System.IO;
using Microsoft.AspNetCore.Mvc;
namespace srpago_oxxo.Controllers
{
public class BaseController : Controller
{
protected string GetJsonRequest()
{
var request = this.HttpContext.Request;
var stream = new StreamReader(request.Body);
var body = stream.ReadToEnd()
.Replace("\n", "")
.Replace("\t", "");
return body;
}
}
}
When I use the function GetJsonRequest after the IOMiddleware logs the request data, it does'nt return any data at all.
Can you help me whit this problem?
What URI should I enter to run my Web API 2 rating route?
I have tried the following URIs with POST method and I get 404 errors:
http://localhost:52229/PersonalAutoAPI/RunRating
This one works (different controller):
http://localhost:52229/PersonalAutoAPI/Drivers
I can GET my driver API
Here is my webapiconfig.config.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace PersonalAuto
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "PersonalAutoAPI",
routeTemplate: "PersonalAutoAPI/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
}
}
}
Here it is my RatingControler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using PersonalAuto.Models;
namespace PersonalAuto.Controllers
{
public class RatingControler : ApiController
{
public IEnumerable<RatingResult> PostRunRaing([FromBody] RatingInfo MyRateInfo)
{
RatingResult[] myRatingResult =
{new RatingResult{PremiumDP = 0M,PremiumEFTDownPament = 0M,PremiumMontlyPayment = 0M,PremiumEFTMonthlyPayment=0M,PremiumPIF=0M }
};
return myRatingResult;
}
public IHttpActionResult PostRunRating([FromBody] RatingInfo MyRateInfo)
{
RatingResult MyRating = new RatingResult { PremiumDP = 0M, PremiumEFTDownPament = 0M, PremiumMontlyPayment = 0M, PremiumEFTMonthlyPayment = 0M, PremiumPIF = 0M };
if (MyRating == null)
{
return NotFound();
}
return Ok(MyRating);
}
}
}
Here it is my diverscontroler.cs (this works with above uri)
using PersonalAuto.Models;
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Http;
using System.Linq;
using System.Net.Http;
using System.IO;
namespace PersonalAuto.Controllers
{
public class DriversController : ApiController
{
public IEnumerable<Driver> GetAllDrivers()
{
Driver[] myDriverArray =
{
new Driver { id = "1234", first_name = "eric", last_name = "last", dl_number = "1234", address_1 = "1234 test st", address_2 = "", city = "dallas", state = "TX", zip = "75248", mobile = "214-415-9224" }
};
return myDriverArray;
}
public IHttpActionResult GetDriverByLicenseNo(string drivers_license_number, string drivers_license_state)
{
string PreToken = "xxxxx:xxx:xxxxxxxxxxxxx";
string Token = Convert.ToBase64String(Encoding.UTF8.GetBytes(PreToken));
if (drivers_license_state == null)
drivers_license_state = "TX";
var driver = new Driver { id = Token, first_name = "eric", last_name = "last", dl_number = "1234", address_1 = "1234 test st", address_2 = "", city = "dallas", state = "TX", zip = "75248", mobile = "214-415-9224" }; // products.FirstOrDefault((p) => p.Id == id);
if (driver == null)
{
return NotFound();
}
return Ok(driver);
}
}
}
I have been pulling my hair out over this! Any help would be appreciated!
The routing setup depends on the controller class name ending in Controller. In your sample code, you have it misspelled: RatingControler should be RatingController
Also check the spelling of PostRunRaing - which seems like it should be PostRunRating, (but it can't be because then you would have a naming conflict. The controller wouldn't know whether to return an IEnumerable or an IHttpActionResult.)
Fix that, and then I think your route will be:
http://localhost:52229/PersonalAutoAPI/Rating/PostRunRating
There code has two problem:
Change RatingControler to RatingController. The controller factory doesn't recognize RatingControler because it expects *Controller.
you cannot have two method with same http verb and method name. you can not overload methods in controllers.