I noticed in one of the ASP.NET Web API getting started tutorials that the option to add a test project is disabled and I'm not sure why that would be. Would I just test an ASP.NET Web API project like any other MVC project?
I'm prototyping something and I'm being lazy and just using an MVC project and returning JSON from some controllers to mock up a web service. As things start getting a bit more serious, I need to start doing things "more correctly".
So how should I write tests for an ASP.NET Web API project and, more broadly, how could I automate the testing of the actual web service?
I have done this like so:
[TestFixture]
public class CountriesApiTests
{
private const string BaseEndPoint = "http://x/api/countries";
[Test]
public void Test_CountryApiController_ReturnsListOfEntities_ForGet()
{
var repoMock = new Mock<ISimpleRepo<Country>>();
ObjectFactory.Initialize(x => x.For<ISimpleRepo<Country>>().Use(repoMock.Object));
repoMock.Setup(x => x.GetAll()).Returns(new List<Country>
{
new Country {Name = "UK"},
new Country {Name = "US"}
}.AsQueryable);
var client = new TestClient(BaseEndPoint);
var countries = client.Get<IEnumerable<CountryModel>>();
Assert.That(countries.Count(), Is.EqualTo(2));
}
}
TestClient code:
public class TestClient
{
protected readonly HttpClient _httpClient;
protected readonly string _endpoint;
public HttpStatusCode LastStatusCode { get; set; }
public TestClient(string endpoint)
{
_endpoint = endpoint;
var config = new HttpConfiguration();
config.ServiceResolver.SetResolver(new WebApiDependencyResolver());
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
_httpClient = new HttpClient(new HttpServer(config));
}
public T Get<T>() where T : class
{
var response = _httpClient.GetAsync(_endpoint).Result;
response.EnsureSuccessStatusCode(); // need this to throw exception to unit test
return response.Content.ReadAsAsync<T>().Result;
}
}
Related
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/"));
I am new to Bazor web assembly (Blazor Client).
I have created a project with Asp.net Core web api with Angular Application.
In order to work with asp.net core web api and angular,
I can use the default functionality like
AddSpaStaticFiles
UseSpa
How can I use Blazor webassembly like the angular?
Or
How can replace the existing Angular SPA with Blazor Client?
Some links provided a solution for Blazor assembly preview.
But the same functionality not found on the latest.
https://csharp.christiannagel.com/2019/08/27/blazorserverandclient/
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
Remember that Web Assembly Apps are created to work like another SPA like Angular or React, it means that you create your view presentation or Blazor Web Assembly App in a independent project, then you get the data from some Web Service, for example an Rest API made in .Net Core 3.1, to create the Blazor Web Assembly Project you just go to File -> New -> Project -> Blazor App -> Blazor WebAssembly App, do not choose ASP.NET Core Hosted option, this will attach your project to the .net core backend directly like an MVC Project.
After having your new project created, you just simple need to call your backend end-points with the Built-In Http Client library that comes with .Net Core or you can create your own library using .Net HttpClient and Inject it in your components or pages using Dependency Injection, if you want to create your own library, follow this process:
First Create this HttpObject:
public class HttpResultObject<T>
{
public bool IsSuccessful { get; set; }
public HttpStatusCode HttpResultCode { get; set; }
public T Result { get; set; }
}
Then create your Library Class:
public class MyLibrary : IMyLibrary
{
public string GetApiUri(string server)
{
if (server.Equals("auth"))
return "https://localhost:8080/api/";
return "https://localhost:8080/api/";
}
//Http Get Method Example
public async Task<HttpResultObject<U>> SetAppMethodGetParametersAsync<U>(string server, string method, Dictionary<string, object> parameters, CancellationToken token) where U : class
{
string getParameters = string.Empty;
foreach(var p in parameters)
{
if (p.Value.GetType() == typeof(string))
getParameters = getParameters.Equals(string.Empty) ? "?" + p.Value.ToString() : "&" + p.Value.ToString();
}
var uri = new System.Uri(GetApiUri(server) + method + "/" + getParameters) ;
var response = await CallAppMethodGetAsync(uri, token);
var result = new HttpResultObject<U>()
{
IsSuccessful = response.IsSuccessStatusCode,
HttpResultCode = response.StatusCode,
Result = JsonSerializer.Deserialize<U>(response?.Content?.ReadAsStringAsync()?.Result)
};
return result;
}
private async Task<HttpResponseMessage> CallAppMethodGetAsync(System.Uri uri, CancellationToken token)
{
Console.WriteLine(uri.ToString());
HttpStatusCode responseStatus = HttpStatusCode.BadRequest;
try
{
HttpClient client = new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(240000)
};
HttpResponseMessage response = new HttpResponseMessage(responseStatus);
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = uri,
Method = HttpMethod.Get
};
var authToken = this.GetLocalStorageItem("authToken");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (authToken != null && authToken.GetType() == typeof(string))
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Convert.ToString(authToken));
token.ThrowIfCancellationRequested();
response = await client.SendAsync(request, token);
responseStatus = response == null ? HttpStatusCode.BadRequest : response.StatusCode;
if (response != null && responseStatus != HttpStatusCode.OK && responseStatus != HttpStatusCode.Accepted)
{
HttpResponseMessage result = new HttpResponseMessage(responseStatus)
{
Content = new StringContent(response.Content?.ReadAsStringAsync()?.Result, Encoding.UTF8, "application/json")
};
return response;
}
return response;
}
catch (WebException webException)
{
}
catch (System.Net.Sockets.SocketException socketException)
{
}
catch (HttpRequestException httpRequestException)
{
}
catch (ArgumentException argumentException)
{
}
catch (System.Exception exception)
{
}
return new HttpResponseMessage(responseStatus);
}
}
Now create your ILibrary Interface and declare the Implemented Methods:
public interface IMyLibrary
{
string GetApiUri(string server);
Task<HttpResultObject<U>> SetAppMethodGetParametersAsync<U>(string server, string method, Dictionary<string, object> parameters, CancellationToken token) where U : class;
}
Declare your Dependency Injection in your startup.cs in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyLibrary, MyLibrary>();
}
Now, if you want to you use your library in some Razor Component or Page just inject it like this:
#inject IMyLibrary _myLibrary
#code
{
private async Task MyHttpGetCall()
{
var cts = new CancellationTokenSource();
var result = await _myLibrary.SetAppMethodPostParametersAsync<HttpResultObject<MyCustomObject>>("auth", new Dictionary<string, object>(), cts.Token);
if (result.IsSuccesful)
{
//whatever you want to do
}
}
}
And that is all!, those are the 2 ways to connect your front-end web site developed with Blazor Web Assembly App to your Backend the same way you does with Angular or React.
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;
}
}
}
I have just added token-based security to my Web API using ASP.net identity OWIN and OAuth 2. As a result of this I am getting 405 unauthorized error on all my tests. How can I mock the securitycontext. I've seen some samples where other have overridden the Thread.CurrentPrincipal but unsure if this is the correct way.
sample test
[TestMethod]
public void Verify_GetReferenceData_Http_Get()
{
var configAE = new HttpSelfHostConfiguration("http://localhost:53224");
Konstrukt.SL.AggregationEngine.WebApiConfig.Register(configAE, new AutoFacStandardModule());
using (HttpSelfHostServer serverAE = new HttpSelfHostServer(configAE))
{
serverAE.OpenAsync().Wait();
HttpResponseMessage responseMessage;
using (var client = new HttpClient())
{
responseMessage =
client.GetAsync(
"http://localhost:53224/AggregationEngine/GetReferenceData/1/Dummy/..."
).Result;
serverAE.CloseAsync().Wait();
configAE.Dispose();
Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode, "Wrong http status returned");
}
}
}
sample controller
public class GetReferenceDataController : ApiController
{
private readonly IDeserializeHelper _deserializeHelper;
private readonly IGetBudgetData _getBudgetData;
private readonly IRevision _revision;
public GetReferenceDataController(IDeserializeHelper deserializeHelper, IGetBudgetData getBudgetData, IRevision revision)
{
_deserializeHelper = deserializeHelper;
_getBudgetData = getBudgetData;
_revision = revision;
}
[Authorize]
[Route("AggregationEngine/GetReferenceData/{budgetId}/{userId}/{filterJSON}")]
[HttpGet]
public HttpResponseMessage Get(int budgetId, string userId, [FromUri]string filterJSON)
{
FlatBudgetData data = new FlatBudgetData();
IDataQueryFilter dataQueryFilter = _deserializeHelper.DeserializeToFilterObject(EntityType.UserReferenceLine, _revision.GetLatesRevision(budgetId), userId, filterJSON);
data.Data = _getBudgetData.GetData(dataQueryFilter);
string jsonFlatBudget = JsonConvert.SerializeObject(data);
var jsonResponse = new HttpResponseMessage()
{
Content = new StringContent(jsonFlatBudget)
};
jsonResponse.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return jsonResponse;
}
}
I followed the first answer in the following stack thread and got it working. Integration Test Web Api With [Authorize]
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.