Unexpected response when using HttpClient - c#

I've been coding a simple API with the view of getting placeholder image of a specialized site which offers this service. However, when I make a request to site with correspondent path, I'm not able of getting the image displayed on screen instead I got this HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: http://fakeimg.pl/300x300/. If not click the link.
I read the official documentation all day long but up to now I haven't realized a way to get this work properly.
This is my code. Obs: I have used the url "https://fakeimg.pl/300X300" and the dotnet core version 3.1.302 for this sample request.
FakePhotoService.cs
namespace FakePhotoApi
{
public class FakePhotoService
{
private readonly HttpClient _httpClient;
private readonly ILogger<FakePhotoService> _logger;
public FakePhotoService(HttpClient httpClient, ILogger<FakePhotoService> logger)
{
_logger = logger;
_httpClient = httpClient;
}
public HttpRequestMessage GenerateRequest(Uri uri)
{
return new HttpRequestMessage(HttpMethod.Get, uri);
}
public async Task<string> GetFakePhoto(Tuple<int, int> dimensions)
{
var baseUri = new Uri($"https://fakeimg.pl/{dimensions.Item1}x{dimensions.Item2}");
var request = GenerateRequest(baseUri);
var response = await _httpClient.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
}
FakePhotoController.cs
namespace FakePhotoApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class FakePhotoController : ControllerBase
{
private readonly FakePhotoService _fakePhotoService;
public FakePhotoController(FakePhotoService fakePhotoService)
{
_fakePhotoService = fakePhotoService;
}
[HttpGet("/")]
public async Task<IActionResult> GetFakePhoto()
{
var result = await _fakePhotoService.GetFakePhoto(new Tuple<int, int>(300, 300));
return Ok(result);
}
}
}
Startup.cs
namespace FakePhotoApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHttpClient<FakePhotoService>()
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = true,
MaxAutomaticRedirections = 5
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Program.cs
namespace FakePhotoApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Please let me know what I'm doing wrong.

Update your code like so:
public HttpRequestMessage GenerateRequest(Uri uri)
{
var msg = new HttpRequestMessage(HttpMethod.Get, uri);
msg.Headers.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"); // or some other real browser string
return msg;
}

Related

How to resolve error 'Access to fetch at from origin has been blocked by CORS policy' in Blazor App

I've been searching a lot in the Internet about CORS but I cannot understand what I'm doing wrong.
The errors i'm getting in the console of my browser is:
Access to fetch at 'https://localhost:44361/api/Contracts' from origin 'https://localhost:44337' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
GET https://localhost:44361/api/Contracts net::ERR_FAILED
The method in my Blazor WebAssembly App for getting data:
public class ContractsUIRepository : IContractsUIRepository
{
private readonly IHttpService _httpService;
private readonly string url = "api/Contracts";
public ContractsUIRepository(IHttpService httpService)
{
_httpService = httpService;
}
public async Task<List<ContractDTO>> GetAllContracts()
{
var response = await _httpService.Get<List<ContractDTO>>(url);
if (!response.IsSucceed)
{
throw new ApplicationException(await response.GetBodyOfResponse());
}
return response.Response;
}
}
My HttpService.cs:
public class HttpService : IHttpService
{
private readonly HttpClient _httpClient;
public HttpService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseWrapper<T>> Get<T>(string url)
{
var httpResponseMessage = await _httpClient.GetAsync(url);
if (httpResponseMessage.IsSuccessStatusCode)
{
var response = await Deserialize<T>(httpResponseMessage);
return new HttpResponseWrapper<T>(true, response, httpResponseMessage);
}
else
{
return new HttpResponseWrapper<T>(false, default, httpResponseMessage);
}
}
private static async Task<T> Deserialize<T>(HttpResponseMessage httpResponseMessage)
{
var responseString = await httpResponseMessage.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseString);
}
}
The HttpResponseWrapper.cs:
public class HttpResponseWrapper<T>
{
public bool IsSucceed { get; set; }
public T Response { get; set; }
public HttpResponseMessage HttpResponseMessage { get; set; }
public HttpResponseWrapper(bool isSucceed, T response, HttpResponseMessage httpResponseMessage)
{
IsSucceed = isSucceed;
Response = response;
HttpResponseMessage = httpResponseMessage;
}
public async Task<string> GetBodyOfResponse()
{
return await HttpResponseMessage.Content.ReadAsStringAsync();
}
}
My Program.cs in the Client Project:
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://localhost:44361/") });
builder.Services.AddScoped<IHttpService, HttpService>();
builder.Services.AddScoped<IContractsUIRepository, ContractsUIRepository>();
await builder.Build().RunAsync();
}
}
And finally my Startup.cs file in the Server Project:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HAFProcurement.WebAPIs", Version = "v1" });
});
services.AddCors(options =>
{
options.AddPolicy("default", policy =>
{
policy.WithOrigins("https://localhost:44337/")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddSingleton<IDataAccess, OracleDataAccess>();
services.AddSingleton<IContrRepository, ContrInMemory>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Procurement.WebAPIs v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("default");
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I am new to C# & Blazor and I am stuck right now. I need your help!
Additionally, is something wrong with my HttpService.cs file? Because I'm not sure it's right..
Thank you in advance!
Try to remove the backslash from the url at builder.WithOrigins:
services.AddCors(options =>
{
options.AddPolicy("default",
builder =>
{
builder.WithOrigins("https://localhost:44337")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Use https://localhost:44337 instead of https://localhost:44337/

GRPC service instantiated per call

I've created a GRPC service host under .NET core 3.1 (using Grpc.AspNetCore v2.30 from https://github.com/grpc/grpc-dotnet). By putting a breakpoint in the "ProxyService" constructor, I can see that the class is instantiated per call - every time a GRPC call is coming from a client, the breakpoint is hit. How do I configure it to always use the same ProxyService instance?
These are the program and Startup classes:
class Program
{
const int _port = 23456;
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
Console.WriteLine("started - press any key to quit...");
Console.ReadKey();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
options.ConfigureEndpointDefaults(o =>
{
o.Protocols = HttpProtocols.Http2;
});
options.ListenAnyIP(_port);
});
webBuilder.UseStartup<Startup>();
});
}
public class ProxyService : StreamingApi.Protos.StreamingApi.StreamingApiBase
{
public ProxyService()
{
// gets here with every client call
}
public override Task<UpdateResponse> Update(UpdateRequest request, ServerCallContext context)
{
return Task.FromResult(new UpdateResponse());
}
}
class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<ProxyService>();
});
}
}
First, let me guess why you want to do this:
You have some heavy-logic inside ProxyService like initialization of some sort;
You have static variables which you want to share between calls;
To resolve first case you should use either method itself:
public ProxyService(IFooBar foobar)
{
this.foobar = foobar;
}
public override Task<UpdateResponse> Update(UpdateRequest request, ServerCallContext context)
{
await this.foobar.InitializeAsync();
return Task.FromResult(new UpdateResponse());
}
Or some other trigger in your system, like for example "At service start":
public interface IFooBarInitilizer :IHostedService
{
}
public class FooBarInitilizer : IFooBarInitilizer
{
public async Task StartAsync(CancellationToken token){ await this.foobar.InitializeAsync(); }
}
//in your Configure
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddSingleton<IFooBarInitializer, FooBarInitializer>();
services.AddHostedService(x=> x.GetService<IFooBarInitializer>());
}
For the second case it is even easier, because you just can specify your shared resources through interface dpendency:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddSingleton<IFooBarResource, FooBarResource>();
}
public class ProxyService : StreamingApi.Protos.StreamingApi.StreamingApiBase
{
public ProxyService(IFooBarResource myStaticResource)
{
this.myStaticResource = myStaticResource;
}
public override Task<UpdateResponse> Update(UpdateRequest request, ServerCallContext context)
{
var somethingGood = this.myStaticResource.GetMeSomethingGood();
return Task.FromResult(new UpdateResponse());
}
}
According to this page you can register your grpc service yourself as a singleton.
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddSingleton(new ProxyService());
}

UnitTest cant find endpoint

Using xUnit 2.4.1 to test the Api always fails to find Controller
When I create a WebApplicationFactory and pass Startup file as parameter the HTTP Client from WebApplicationFactory.CreatVlient() always returns 404 for Get requests.
Testing a .Net Core Api that uses MVC.
The CommonContext is an internal class that sets the connection.
The Configfile reads correctly
The Connectionstring to DB is correct
The Endpoint is not called correctly and therefore never hits the controller.
Class that inherits WebApplocationFactory
public class WebApiTestFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup: class
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var configValues = new Dictionary<string, string>
{
{"RunStatus", "Test"},
};
builder.AddInMemoryCollection(configValues);
return WebHost.CreateDefaultBuilder()
.UseConfiguration(builder.Build())
.UseUrls("http://localhost:53976/")
.UseSetting("applicationUrl", "http://localhost:53976/")
.UseStartup<Else.WebApi.Core.Startup>();
}
}
Unit Test
public class TestControllerTest : IClassFixture<WebApiTestFactory<Startup>>
{
private readonly WebApiTestFactory<Startup> _factory;
public TestControllerTest(WebApiTestFactory<Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("api/Test/GetExample")]
public async Task Create(string url)
{
// Arrange
var clientOptions = new WebApplicationFactoryClientOptions();
clientOptions.BaseAddress = new Uri("http://localhost:53976");
var client = _factory.CreateClient(clientOptions);
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Controller is in the project im testing
[ApiController]
[Route("api/Test")]
public class TestController : Controller
{
[HttpGet("GetExample")]
public ActionResult GetExample()
{
return Ok();
}
}
Startup
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
HostingEnvironment = env;
Configuration = configuration;
EwBootstrapper.BootstrapElsewareServices();
}
public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
if (Configuration["RunStatus"] != "Test")
{
services.AddTransient<AuthenticationTokens>();
services.AddTransient<IPasswordValidator, PasswordValidator>();
services.AddTransient<IUserRepository, UserRepository>();
services.AddMvc();
services.AddScoped(_ =>
new CommonContext(Configuration.GetConnectionString("DbConnection")));
services.AddSwaggerDocumentation();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // Configure authentication (JWT bearer)
.AddJwtBearer(jwtOpt => // Configure JWT bearer
{
jwtOpt.TokenValidationParameters = AuthenticationTokens.GetValidationParameters();
});
}
else
{
//services.AddMvcCore().AddJsonFormatters();
services.AddScoped(_ =>
new CommonContext(Configuration.GetConnectionString("DbTestConnection")));
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
if (Configuration["RunStatus"] != "Test")
{
app.UseSwaggerDocumentation();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
});
app.UseMiddleware<ApiLoggerMiddleware>();
app.UseMvc(builder => builder.MapRoute("Default", "api/{controller}/{action=Get}/{id?}")); // No default values for controller or action
app.UseDefaultFiles(); // Enable default documents ( "/" => "/index.html")
app.UseStaticFiles(); // Static files under wwwroot
app.UseAuthentication();
}
if (HostingEnvironment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
}
According to the attribute routing in your controller, the action method has the url api/Test/GetExample: [HttpGet("GetExample")], yet in your in test you are testing for CreateExample:
[InlineData("api/Test/CreateExample")]
So I guess, your test is correct in returning a 404. That route simply will not resolve to any existing action method.
I suggest you change your theory to [InlineData("api/Test/GetExample")]

IP address when self hosting and using xUnit Test

I have the following function, GetIpAddress in my production code. I have a xUnit test which calls the website where the function is called. The function works correctly when running normally but RemoteIpAddress is always null if run from the xUnit test. Below is my test start up class that is called by the host builder and the Test function is used to send the request.
internal static string GetIpAddress(HttpRequest request)
{
try
{
if (request.HttpContext.Connection.RemoteIpAddress != null)
return request.HttpContext.Connection.RemoteIpAddress.ToString();
}
catch (System.Exception ex)
{
DataLink.ProcessError(ex, "Error getting IP address");
return "error";
}
return "unknown";
}
class TestStartup
{
public TestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
ApmCore.Startup.Connection = Configuration.GetConnectionString("DefaultConnection");
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMemoryCache();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
public static HttpClient GetClient()
{
var server = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>());
var client = server.CreateClient();
client.DefaultRequestHeaders
.Accept
.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
public static HttpRequestMessage GetRequest(string args, string url)
{
return new HttpRequestMessage(HttpMethod.Get, new System.Uri(url))
{
Content = new StringContent(args, Encoding.UTF8, "application/json")
};
}
}
[Theory]
[MemberData(nameof(TestDataHandler.LogData), MemberType = typeof(TestDataHandler))]
public async Task TestGet(string args, bool expected)
{
var response = await this._Client.SendAsync(TestStartup.GetRequest(args, "http://localhost/api/Log"));
var data = await response.Content.ReadAsStringAsync();
var result = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(data, new { success = false });
Assert.Equal(result.success, expected);
}
This question seems to be a duplicate of Set dummy IP address in integration test with Asp.Net Core TestServer
That question has an answer that could be used to resolve this issue:
https://stackoverflow.com/a/49244494/90287
You can write middleware to set custom IP Address since this property
is writable:
public class FakeRemoteIpAddressMiddleware
{
private readonly RequestDelegate next;
private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.168.1.32");
public FakeRemoteIpAddressMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Connection.RemoteIpAddress = fakeIpAddress;
await this.next(httpContext);
}
}
Then you can create StartupStub class like this:
public class StartupStub : Startup
{
public StartupStub(IConfiguration configuration) : base(configuration)
{
}
public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<FakeRemoteIpAddressMiddleware>();
base.Configure(app, env);
}
}
And use it to create a TestServer:
new TestServer(new WebHostBuilder().UseStartup<StartupStub>());

asp.net 5 vnext integration testing and views

I am building a new app using ASP.NET 5 with MVC6 and trying to set up a project with tests based on this description.
It is working fine for API calls (returning ObjectResults), but when I hit a response returning a view, I get 404 response.
I am using startup class from the project unter test, and the code is the same as in tutorial:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseIISPlatformHandler();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
[Route("/")]
public class HomeController : Controller
{
[HttpGet]
[AllowAnonymous]
[Route("/")]
public IActionResult Index()
{
return View();
}
[HttpGet]
[AllowAnonymous]
[Route("/ok")]
public IActionResult Ok()
{
return new ObjectResult("OK");
}
}
[TestClass]
public class HomeTests
{
private readonly HttpClient _client;
public HomeTests()
{
var server = new TestServer(TestServer.CreateBuilder()
.UseStartup<Startup>());
_client = server.CreateClient();
}
[TestMethod]
public async Task Index_returns_page()
{
var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
//Assert.Equals("OK", responseString);
}
[TestMethod]
public async Task Index_OK_returns_OK()
{
var response = await _client.GetAsync("/ok");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.AreEqual("OK", responseString);
}
}
Of course in the browser everything works as expected.
It can't find your views because the IApplicationEnvironment.ApplicationBasePath is set to the test project's directory. You should be replacing the IApplicationEnvironment implementation with your own that points at the project you're testing. This will get a bit easier in RC2.

Categories