Uploading a file from .NET application to a Web API POST endpoint - asp.net

I'd like to use Web API to build some endpoints for applications to consume. The first job I'd like it to do is allow the client to upload a file to the server.
The client will run a .NET app of some kind, maybe a console app or maybe something else. It won't be a webpage using a form element or file input.
I think the Web API would look something like this:
public class FileController : ApiController
{
public bool Post(File newFile)
{
return true;
}
}
Using this as a model class:
public class File
{
public string name { get; set; }
public Stream uploadStream { get; set; }
}
I'm sure that's horribly wrong but it's my first Web API.
I'm trying to test this in a console application:
namespace TestFileUpload
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting the test...");
using (FileStream readstream = new FileStream(#"C:\\Test\Test2.txt", FileMode.Open, FileAccess.Read))
{
WebAPI.Classes.File newFile = new WebAPI.Classes.File()
{
name = "Test.txt",
uploadStream = readstream
};
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:50326");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
response = client.PostAsJsonAsync("http://localhost:50326/api/file", newFile).Result;
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
};
Console.ReadLine();
}
}
}
Now I'm getting a timeout error when I try to get the response:
"Error getting value from 'ReadTimeout' on 'System.IO.FileStream'."
Help?

There are many ways for a client to consume a Web API service, but the most straight forward would be to use the web api client library. Perhaps you should consider building a simple get method that returns an object before jumping into file uploads.
Web API from .NET Client
You can not add a service reference as Web API does not expose a wsdl.

Related

Consuming asp.net core web api in asp.net core mvc web app

I'm trying to consume my asp.net web api in my asp.net core mvc web app which are on the same solution. I configured the solution for multi-project start and they start both.
next I tried to consume the API in the Web part but I'm getting the following error.
InvalidOperationException: A suitable constructor for type 'ProjectName.Web.Services.Interfaces.IAdminService' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, out ConstructorInfo matchingConstructor, out Nullable[] matchingParameterMap)
Here is the complete Stack trace
The Projects are structure like this
SolutionName:
Name.API
Name.Web
each with its own respective structure
This is my Helper Class
public static class HttpClientExtensions
{
public static async Task<T> ReadContentAsync<T>(this HttpResponseMessage response)
{
//if (response.IsSuccessStatusCode == false) return StatusCodes = 300;
//throw new ApplicationException($"Something went wrong calling the API: {response.ReasonPhrase}");
var dataAsString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = JsonSerializer.Deserialize<T>(
dataAsString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return result;
}
}
The IAdmin Inter Face
Task<IEnumerable<Admins>> GetAllAdmins();
The AdminService(Implementation)
private readonly HttpClient _client;
public const string BasePath = "api/Admins";
public AdminService(HttpClient client)
{
_client = client; // ?? throw new ArgumentNullException(nameof(client));
}
public async Task<IEnumerable<Admins>> GetAllAdmins()
{
var response = await _client.GetAsync(BasePath);
return await response.ReadContentAsync<List<Admins>>();
}
Admins Controller
private readonly IAdminService _adminService;
public AdminController(IAdminService adminService)
{
_adminService = adminService;
}
public async Task<IActionResult> Index()
{
var adminsList = await _adminService.GetAllAdmins();
if(adminsList == null)
{
return new JsonResult("There are now Admins");
}
return View(adminsList);
}
Program.cs
builder.Services.AddControllersWithViews();
builder.Services.AddHttpClient<IAdminService, IAdminService>(c =>
c.BaseAddress = new Uri("https://localhost:<port-Num>/"));
var app = builder.Build();
What Could I be doing wrong???
I'm using .NET 6 adn both Projects are in the same solution
NB My end points are working fine, I test them using Postman.
It is failing because DI cannot instantiate your AdminService with parameterized constructor. This is possibly a duplicate of Combining DI with constructor parameters? .
Essentially, you should avoid parameterized constructor injection where possible. Either control it through configuration or have the configuration loaded through common infrastructure such as host configuration.
According to your codes, I found you put two interface inside the AddHttpClient method which caused the issue.
I suggest you could modify it like this and then it will work well.
builder.Services.AddHttpClient<IAdminService, AdminService>(c =>
c.BaseAddress = new Uri("https://localhost:3333/"));

FeignClient configuration in ASP.Net

I am trying to create microservices using Spring-boot Java and SteelToe ASP.NET
Step-1: I created a full service using Java (A service with UI and API. It is hosted on PCF). The API has ClassesControler defined inside.
Step-2: Create a microservice using ASP.NET, SteelToe. Register the service in Eureka and make it discoverable using Zuul.
Step-3: Use the Interface, Service approach to access the JAVA microservice(s)
namespace employee-client.Service
{
public interface IRelayService
{
Task<HttpResponseMessage> getClassesList(string relativeUrl = "/api/v1/classes");
}
}
Service with Implementation for Interface:
namespace employee-client.Service
{
public class RelayService : IRelayService
{
DiscoveryHttpClientHandler _handler;
string _accessToken;
private const string BASE_URL = "https://www.example.com";
public QortaService(IDiscoveryClient client, string accessToken)
{
_handler = new DiscoveryHttpClientHandler(client);
_accessToken = accessToken;
}
public async Task<HttpResponseMessage> getClassesList(string relativeUrl)
{
string classesUrl= BASE_URL + relativeUrl;
HttpClient client = GetClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(classesUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
return await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
}
private HttpClient GetClient()
{
var client = new HttpClient(_handler, false);
return client;
}
}
}
I came up with this approach based on the example in SteelToe but I hate hardcoding the BASE_URL.
Question: I very much like the #FeignClient annotation approach used in Java. Any ideas about how I can access an existing microservice in a better way. If so, an example would be much appreciated
Edit:
I modified the question to make more clear.
The flow of traffic is from Java Service to .NET service. .NET service requests for a list of classes from the controller in JAVA service (ClassesController.java)
I'm unclear which direction traffic is flowing in your scenario, but I think you're saying the .NET application is trying to call the Java application. The code you're using is from before HttpClientFactory was introduced and is a bit clunkier than what's possible now in general. Steeltoe can be used with HttpClientFactory for a better overall experience.
Steeltoe has debug logging available to confirm the results of service lookup if you set logging:loglevel:Steeltoe.Common.Discovery = true in your application config.
You didn't mention specifically what isn't working, but I'm guessing you're getting a 404 since it looks like your code will create a request path looking like https://fortuneService/api/fortunes/random/api/v1/classes
If you're looking for something like Feign in .NET, you could try out DHaven.Faux
For others who are looking for the same:
namespace employee-client.Service
{
public class RelayService : IRelayService
{
private const string CLASSES_API_SERVICEID = "classes-api";
IDiscoveryClient _discoveryClient;
DiscoveryHttpClientHandler _handler;
string _accessToken;
public RelayService(IDiscoveryClient discoveryClient, string accessToken)
{
_discoveryClient = discoveryClient;
_handler = new DiscoveryHttpClientHandler(client);
_accessToken = accessToken;
}
public async Task<HttpResponseMessage> getClassesList()
{
var classesApiInstances = _discoveryClient.GetInstances(CLASSES_API_SERVICEID);
Uri classesApiUri = classesApiInstances[0].Uri;
string classesUrl= classesApiUri.AbsoluteUri + relativeUrl;
HttpClient httpClient = GetClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(classesUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
return await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead);
}
private HttpClient GetClient()
{
var client = new HttpClient(_handler, false);
return client;
}
}
}

Unauthorized when accessing Azure cognitive services from netcore3.1 console app

I have a netcore console app which is accessing the Azure's Text analysis API's using the Client library from the Microsoft.Azure.CognitiveServices.Language.TextAnalytics Nuget package.
When trying to access the API, I receive the following HttpException:
Unauthorized. Access token is missing, invalid, audience is incorrect (https://cognitiveservices.azure.com), or have expired.
Unhandled exception. System.AggregateException: One or more errors occurred. (Operation returned an invalid status code 'Unauthorized')
When accessing the same API using exactly the same code which is hosted on Azure Functions - everything works as expected. I was unable to find any info in the docs or anywhere else.
Try the .net core console app code below to use TextAnalytics SDK :
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.CognitiveServices.Language.TextAnalytics;
using Microsoft.Azure.CognitiveServices.Language.TextAnalytics.Models;
using Microsoft.Rest;
namespace TextAnalysis
{
class Program
{
private static readonly string key = "<your text analyisis service key>";
private static readonly string endpoint = "<your text analyisis service endpoint>";
static void Main(string[] args)
{
var client = authenticateClient();
sentimentAnalysisExample(client);
languageDetectionExample(client);
entityRecognitionExample(client);
keyPhraseExtractionExample(client);
Console.Write("Press any key to exit.");
Console.ReadKey();
}
static TextAnalyticsClient authenticateClient()
{
ApiKeyServiceClientCredentials credentials = new ApiKeyServiceClientCredentials(key);
TextAnalyticsClient client = new TextAnalyticsClient(credentials)
{
Endpoint = endpoint
};
return client;
}
class ApiKeyServiceClientCredentials : ServiceClientCredentials
{
private readonly string apiKey;
public ApiKeyServiceClientCredentials(string apiKey)
{
this.apiKey = apiKey;
}
public override Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
request.Headers.Add("Ocp-Apim-Subscription-Key", this.apiKey);
return base.ProcessHttpRequestAsync(request, cancellationToken);
}
}
static void sentimentAnalysisExample(ITextAnalyticsClient client)
{
var result = client.Sentiment("I had the best day of my life.", "en");
Console.WriteLine($"Sentiment Score: {result.Score:0.00}");
}
static void languageDetectionExample(ITextAnalyticsClient client)
{
var result = client.DetectLanguage("This is a document written in English.","us");
Console.WriteLine($"Language: {result.DetectedLanguages[0].Name}");
}
static void entityRecognitionExample(ITextAnalyticsClient client)
{
var result = client.Entities("Microsoft was founded by Bill Gates and Paul Allen on April 4, 1975, to develop and sell BASIC interpreters for the Altair 8800.");
Console.WriteLine("Entities:");
foreach (var entity in result.Entities)
{
Console.WriteLine($"\tName: {entity.Name},\tType: {entity.Type ?? "N/A"},\tSub-Type: {entity.SubType ?? "N/A"}");
foreach (var match in entity.Matches)
{
Console.WriteLine($"\t\tOffset: {match.Offset},\tLength: {match.Length},\tScore: {match.EntityTypeScore:F3}");
}
}
}
static void keyPhraseExtractionExample(TextAnalyticsClient client)
{
var result = client.KeyPhrases("My cat might need to see a veterinarian.");
// Printing key phrases
Console.WriteLine("Key phrases:");
foreach (string keyphrase in result.KeyPhrases)
{
Console.WriteLine($"\t{keyphrase}");
}
}
}
}
You can find your key and endpoint here on Azure portal :
Result :

Cognitive Face API returning No result from Web Application

I am using the Microsoft Cognitive Face API for implementing the Face Detection to Authenticate the person and login to the application.
For that, I am creating a PersonGroup and add the Person and then Face of Person. Train Person group and so on.
I want to implement it in the MVC Web Application. I have written the same code in MVC Web Application. But the API is not returning anything and just hanged at the Http request call.
So Do we need to get some different API Key for a Web application or Do we need to make any changes in Web.Config file to make a successful call?
I have tried the same code in the Console Application and WPF Application. There everything works perfectly fine.
public class HttpClientHelper
{
HttpClient httpClient = new HttpClient();
private const string subscriptionKey = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
private const string faceEndpoint = "https://southeastasia.api.cognitive.microsoft.com/face/v1.0/";
public HttpClientHelper()
{
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
httpClient.BaseAddress = new Uri(faceEndpoint);
}
public async Task<T> GetAsync<T>(string url)
{
var response = await httpClient.GetAsync(url);
string contentString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(contentString);
}
}
public static async Task CreatePersonGroup(UserGroup userGroup)
{
bool isGroupExists = false;
PersonGroup personGroup;
try
{
HttpClientHelper httpClientHelper = new HttpClientHelper();
var response = httpClientHelper.GetAsync<PersonGroup>(string.Format("persongroups/{0}", userGroup.UserGroupId)).Result;
isGroupExists = true;
}
catch (APIErrorException ex)
{
if (ex.Body.Error.Code == "PersonGroupNotFound")
isGroupExists = false;
}
if (isGroupExists == false)
{
await faceClient.PersonGroup.CreateAsync(userGroup.UserGroupId, userGroup.Name);
}
}
I expect the same code must be working fine in Web Application also. As there is not a big logic in it. Just a simple API call.
private const string faceEndpoint =
"https://southeastasia.api.cognitive.microsoft.com/";
hopefully, it should work

Assembly.UnsafeLoadFrom causes web app crash

I am trying to load a DLL from internet, more specifically it is Azure storage (Blob), so I used "Assembly.UnsafeLoadFrom" like this:
Assembly.UnsafeLoadFrom(#"https://accountname.blob.core.windows.net/test/calculator.dll");
But becuaset this specific call, my web app (published) returns:
"The specified CGI application encountered an error and the server
terminated the process."
The weird part is if I am using my local build, it is fine. there is no crash and the return result is correct.
I am using Visual Studio 2015 and .net 5.
Please let me know how to resolve this issue or how to debug it.
Thanks
For a simple way, you could achieve your purpose by the following code:
calculator.dll
public class Calculator
{
public string HelloWorld(string userName)
{
return string.Format("Hello world, {0}!", userName);
}
}
HomeController.cs
public async Task<ActionResult> Index()
{
string url = "https://brucechen.blob.core.windows.net/dll/calculator.dll";
HttpClient client = new HttpClient();
var bytes = await client.GetByteArrayAsync(url);
//load assembly from bytes
Assembly assembly = Assembly.Load(bytes);
var calc = assembly.CreateInstance("calculator.Calculator");
//invoke the method and get result
var result = calc.GetType().InvokeMember("HelloWorld", BindingFlags.InvokeMethod, null, calc, new[] { "Bruce" });
ViewData["result"] = result;
return View();
}
Result

Resources