I,m testing ASP.NET Core app with TestServer, and there are controllers that require cookie auth. I've created test server instance like this:
_testServer = new TestServer(new WebHostBuilder()
.UseEnvironment(CustomEnvironments.Test)
.UseContentRoot(currentDirectory)
.UseStartup<Web.Startup>()
.UseUrls("http://localhost/"));
ApiClient = _testServer.CreateClient();
and now I have to add auth cookie, but it is ignored by server. If the client could be created directly I could pass HttpClientHandler to constractor and set UseCookies to false, and it works, but I can't access the handler when I get client from test server. Is there a way to add auth cookies to test client?
I've found the solution. TestServer has method CreateRequest(string path), it returns RequestBuilder, which allows to insert cookies to header
Using #AlexK's answer for inspiration, combined with information from a blog post (as an aside, this post goes into a lot of other useful details when dealing with other issues when sending requests to the test server), here is one way of getting cookies to work with the TestServer using CreateRequest(string path) based on what I used for my own project.
public class TestWebEnvironment : IDisposable
{
private TestServer Server { get; }
private CookieContainer CookieContainer { get; }
public TestWebEnvironment()
{
var builder = new WebHostBuilder()
.UseEnvironment("Test")
.UseStartup<TestWebStartup>();
Server = new TestServer(builder);
CookieContainer = new CookieContainer();
}
private RequestBuilder BuildRequest(string url)
{
var uri = new Uri(Server.BaseAddress, url);
var builder = Server.CreateRequest(url);
var cookieHeader = CookieContainer.GetCookieHeader(uri);
if (!string.IsNullOrWhiteSpace(cookieHeader))
{
builder.AddHeader(HeaderNames.Cookie, cookieHeader);
}
return builder;
}
private void UpdateCookies(string url, HttpResponseMessage response)
{
if (response.Headers.Contains(HeaderNames.SetCookie))
{
var uri = new Uri(Server.BaseAddress, url);
var cookies = response.Headers.GetValues(HeaderNames.SetCookie);
foreach (var cookie in cookies)
{
CookieContainer.SetCookies(uri, cookie);
}
}
}
public async Task<string> GetAsync(string url)
{
using (var response = await BuildRequest(url).GetAsync())
{
UpdateCookies(url, response);
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> PostAsync(string url, HttpContent content)
{
var builder = BuildRequest(url);
builder.And(request => request.Content = content);
using (var response = await builder.PostAsync())
{
UpdateCookies(url, response);
return await response.Content.ReadAsStringAsync();
}
}
public void Dispose()
{
Server.Dispose();
}
}
Related
I'm trying to change the proxy that this request would send with but i do not understand how to, note i make the request in the main method by using Client.put(name)
public class Client
{
private readonly HttpClient _client;
public Client()
{
_client = new HttpClient();
}
public async Task Put(string name)
{
var sent = DateTimeOffset.Now.ToUnixTimeMilliseconds();
Console.WriteLine($"Sent: {sent}");
using (var request = new HttpRequestMessage(HttpMethod.Put, $"https://api.minecraftservices.com/minecraft/profile/name/{name}"))
{
request.Headers.Add("Authorization", "Bearer token");
Thread.Sleep(50);
using (var response = await _client.SendAsync(request))
{
var recv = DateTimeOffset.Now.ToUnixTimeMilliseconds();
Console.WriteLine($"Status Code: {response.StatusCode}");
Console.WriteLine($"Recv: {recv}");
Console.WriteLine();
}
}
}
}
I am trying to build an API that receives a GET request from our website client, then makes a request to Eventbrite's API using out access token and returns the response to the web client.
I am trying to use HttpClient to make the request but it gives my Syste.NullReferenceException.
My code looks like:
public class CANWebsiteV3Controller : ApiController
{
private static readonly HttpClient client = new HttpClient();
private readonly HttpResponse _response;
private readonly HttpRequest _request;
public CANWebsiteV3Controller()
{
/*_response = HttpContext.Current.Response;
_request = HttpContext.Current.Request;*/
}
// GET api/<controller>/5
[Route("api2.0/CANWebsiteV3/GetEvents")]
[HttpGet]
public async void GetEvents()
{
string API_TOKEN = "XXXXXXXXXXXX";
string ORGANIZATION_ID = "YYYYYYYYYY";
string ENDPOINT = "https://www.eventbriteapi.com/v3/organizations/" + ORGANIZATION_ID + "/events/?time_filter=current_future";
try
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("authorization", "Bearer XXXXXXXXXXXX");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var res = await client.GetAsync(ENDPOINT);
if (res.IsSuccessStatusCode)
{
string eventResponse = res.Content.ReadAsStringAsync().Result;
var events = JsonConvert.DeserializeObject(eventResponse);
events.WriteToReponse();
}
}
catch(Exception ex)
{
JsonResponse.NewResponse("Could not fetch events: "+ex.Message, false, ErrorType.Generic, HttpStatusCode.BadRequest);
}
}
}
I am trying to access a protected API using client credential flow in my asp.net core 3.1 application.
For token management I am using IdentityModel.AspNetCore -1.0.0-rc.4.1.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<ApiService>(client =>
{
client.BaseAddress = new Uri("http://localhost:10811/");
})
.AddClientAccessTokenHandler();
services.AddAccessTokenManagement(options =>
{
options.Client.Clients.Add("auth", new ClientCredentialsTokenRequest
{
Address = "http://localhost:10811/token",
ClientId = "client1",
ClientSecret = "Supersecret"
});
});
}
I am always getting 401 while trying to access the protected API service.
ApiService code,
public class ApiService
{
public HttpClient HttpClient;
public ApiService(HttpClient client)
{
HttpClient = client;
}
public async Task<string> GetContactsAsync()
{
var response = await HttpClient.GetAsync("http://localhost:10811/test");
response.EnsureSuccessStatusCode();
return "Done";
}
}
And here I am calling
public class MyCallService
{
private readonly IHttpClientFactory _clientFactory;
public MyCallService(IHttpClientFactory clientFactory)
{
if (clientFactory != null)
_clientFactory = clientFactory;
}
public void Call()
{
var client = _clientFactory.CreateClient();
var apiService= new ApiService(client);
await apiService.GetContactsAsync();
}
}
Is the above code setting any token, what I am missing here? Where to put Bearer token in the authorization header.
In order to send the token with any request from the httpclient , you need to inject it before and to do that you need to use AddClientAccessTokenClient method under the AddAccessTokenManagement
services.AddClientAccessTokenClient("client", configureClient: client =>
{
client.BaseAddress = new Uri("http://localhost:10811/");
});
and you need to specifiy the name of the config to use in order to create httpclient
_client = factory.CreateClient("client");
and now you can simply call
var response = await HttpClient.GetAsync("test"); //no need to specify the full URL
I have a website and web api. What I want is when ever someone call my web api methods, it should deny the request. But My website call the web api then it should process and respond. It's not just CORS but also when my website C# code request from my website the web api should respond. No other domain request should be responded. I am using asp.net MVC 5, same with web api. How can I accomplish the task.also need to know how I can enable my web api to respond to only cors request that are made from my website also how my web api respond to Website's C# request from my website?
You can restrict either Token, IP Address, or both.
For example,
public class TokenValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext == null)
throw new ArgumentNullException("actionContext");
var authorization = actionContext.Request.Headers.Authorization;
if (authorization != null)
{
var authToken = authorization.Parameter;
var token = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
if ("Authorized Token" == token)
return;
}
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
public class IpHostValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var context = actionContext.Request.Properties["MS_HttpContext"]
as HttpContextBase;
string ipAddress = context.Request.UserHostAddress;
if (ipAddress == "Authorized IP Address")
return;
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("Unauthorized IP Address")
};
}
}
Usage
You can place those filters on each controller or use Global filter.
public class FilterConfig
{
public static void RegisterGlobalFilters(HttpFilterCollection filters)
{
filters.Add(new TokenValidationAttribute());
filters.Add(new IpHostValidationAttribute());
}
}
Client Helper
public static HttpClient GetHttpClient()
{
var client = new HttpClient(new RetryHandler(new HttpClientHandler()));
client.BaseAddress = new Uri("API URL");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var bcreds = Encoding.ASCII.GetBytes("Authorized Token Same As Server");
var base64Creds = Convert.ToBase64String(bcreds);
client.DefaultRequestHeaders.Add("Authorization",
"Basic " + base64Creds);
return client;
}
Client Usage
using (var client = GetHttpClient())
{
HttpResponseMessage response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode)
{
result = await response.Content.ReadAsAsync<IList<T>>().ConfigureAwait(false);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
If you want more security, you might want to look into public key and private key method.
What is the preferred way for handling web api endpoints for each controller?
For example, my MVC controller will be calling different endpoints.
These are the only ones for now, but it could change.
Also, I will be developing this locally and and deploying to development server.
http://localhost:42769/api/categories/1/products
http://localhost:42769/api/products/
public class ProductsController : Controller
{
HttpClient client;
string url = "http://localhost:42769/api/categories/1/products"; //api/categories/{categoryId}/products
public ProductsController()
{
client = new HttpClient();
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
// GET: Products
public async Task<ActionResult> ProductsByCategory()
{
HttpResponseMessage responseMessage = await client.GetAsync(url);
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var products = JsonConvert.DeserializeObject<List<GetProductsByCategoryID>>(responseData);
return PartialView(products);
}
return View("Error");
}
}
Not sure I understand you question or problem, but I would create a wrapper class for the service and then have different methods for each resource that you need to call. Always think SOLID.
Example (written by hand)
public class Client
{
private Uri baseAddress;
public Client(Uri baseAddress)
{
this.baseAddress = baseAddress;
}
public IEnumerable<Products> GetProductsFromCategory(int categoryId)
{
return Get<IEnumerable<Product>>($"api/categories/{categoryId}/products");
}
public IEnumerable<Products> GetAllProducts()
{
return Get<IEnumerable<Product>>($"api/products");
}
private T Get<T>(string query)
{
using(var httpClient = new HttpClient())
{
httpClient.BaseAddress = baseAddress;
var response= httpClient.Get(query).Result;
return response.Content.ReadAsAsync<T>().Result;
}
}
}