Pass JSON data in a http GET request to a REST service - c#

Using the following command:
curl -v -X GET -H "Content-Type: application/json" -d {'"mode":"0"'} http://host.domain.abc.com:23423/api/start-trial-api/
I am able to send the JSON data to web request and get the response back.
How can I do the same in C#?
I am able to POST data to the other service and get the response but don't understand how to send the data to GET request.
I tried searching on google and stackoverflow for the same in C#, but did not find anything.

Sample code - Make sure the request method is set to "GET"
string url = "";
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/json";
var webResponse = request.GetResponse();
using (var s = webResponse.GetResponseStream())
{
using (TextReader textReader = new StreamReader(s, true))
{
string jsonString = textReader.ReadToEnd();
}
}

Plenty of abstractions here, but hopefully will give a rough guide on how to connect to a service in C#
The Interface
public interface IShopifyAPIGateway
{
HttpResponseMessage Get(string path);
}
Shopify API Gateway, which instatiates HTTPClient()
public sealed class ShopifyAPIGateway : IShopifyAPIGateway
{
/// <summary>
///
/// </summary>
private Identity _identity;
/// <summary>
///
/// </summary>
private HttpClient _httpClient;
/// <summary>
///
/// </summary>
public ShopifyAPIGateway(Identity
identity)
{
_identity = identity;
_httpClient = new HttpClient(ClientHandler());
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public HttpResponseMessage Get(string path)
{
try
{
var response = Connect().GetAsync(path).Result;
return response;
}
catch (CustomHttpResponseException ex)
{
new Email().SendEmail(_identity.ClientName, "Http Response Error - Shopify API Module",
"Http Response Error - Shopify API Module: " + ex.Message,
"error#retain.me");
throw new CustomHttpResponseException(ex.Message);
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private HttpClient Connect()
{
try
{
_httpClient.BaseAddress = new Uri(_identity.APIURL);
return _httpClient;
}
catch (CustomHttpRequestException ex)
{
throw new CustomHttpRequestException(ex.Message);
}
}
/// <summary>
///
/// </summary>
/// <param name="userKey"></param>
/// <returns></returns>
private HttpClientHandler ClientHandler()
{
try
{
return new HttpClientHandler()
{
Credentials = new NetworkCredential(_identity.APIKey,
_identity.Password),
PreAuthenticate = true
};
}
catch (CustomClientHandlerException ex)
{
throw new CustomClientHandlerException(ex.Message);
}
}
}
Generic repo to return any object(s) where the response object matches T
public sealed class EntityRepository<T> : IEntityRepository<T>
{
private IShopifyAPIGateway _gateWay;
public T Find(string path)
{
try
{
_gateWay = new ShopifyAPIGateway(_identity);
var json = _gateWay.Get(path).Content.ReadAsStringAsync();
T results = JsonConvert.DeserializeObject<T>(json.Result);
return results;
}
catch (System.Exception ex)
{
throw new ApplicationException(ex.Message);
}
}
}
Usage return type must match the Type your passing and also the Type that's being returned in response.
private IEnumerable<Order> Orders()
{
var entityRepo = new EntityRepository<Order>();
return entityRepo.Find("somedomain/api/orders?mode=0", _identity);
}

Related

.NET 7 MAUI not receiving http response

I am playing around with .NET MAUI, and I got a problem. I call a rest API endpoint, and it is called, no errors (works 100% because I got response in Postman and on the SwaggerUI). But my mobile app client never receives a response. I probably miss something. Any idea is welcome.
namespace Mobile.UI.Clients;
public abstract class BaseClient
{
private readonly HttpClient httpClient;
private readonly MobileAppSettings settings;
private string BaseURL
{
get
{
return DeviceInfo.Platform == DevicePlatform.Android ?
this.settings.AndroidBaseURL :
this.settings.IosBaseURL;
}
}
protected BaseClient(HttpClient httpClient, MobileAppSettings settings)
{
this.settings = settings;
this.httpClient = BuildHttpClient(httpClient);
}
/// <summary>
/// Creates a simple get request
/// </summary>
/// <typeparam name="T">The return type</typeparam>
/// <param name="route">The route part without the base url</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
protected async Task<T> SendGetRequestAsync<T>(string route)
{
try
{
var uri = BuildUri(route);
var response = await httpClient.GetAsync(uri);
if (!response.IsSuccessStatusCode)
{
throw new Exception("Faild to fetch data.");
}
var content = await SerializeResponse<T>(response.Content);
return content;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <summary>
/// Creates a simple get request
/// </summary>
/// <typeparam name="T">The return type</typeparam>
/// <param name="route">The route part without the base url</param>
/// <param name="routParam">Rout parameter</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
protected async Task<T> SendGetRequestAsync<T>(string route, object routParam)
{
try
{
var uri = BuildUri(route, routParam);
var response = await httpClient.GetAsync(uri);
if (!response.IsSuccessStatusCode)
{
throw new Exception("Faild to fetch data.");
}
var content = await SerializeResponse<T>(response.Content);
return content;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
private HttpClient BuildHttpClient(HttpClient httpClient)
{
#if DEBUG
var handler = new HttpsClientHandlerService();
httpClient = new HttpClient(handler.GetPlatformMessageHandler());
#endif
httpClient.BaseAddress = new Uri(BaseURL);
httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
httpClient.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new("application/json"));
return httpClient;
}
private Uri BuildUri(string route)
{
return new Uri(Path.Combine(BaseURL, settings.ApiVersion, route));
}
private Uri BuildUri(string route, object routParam)
{
return new Uri(Path.Combine(BaseURL, settings.ApiVersion, route, $"{routParam}"));
}
private async Task<T> SerializeResponse<T>(HttpContent content)
{
var stream = await content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<T>(stream);
}
}
public class PlayerClient : BaseClient, IPlayerClient
{
public PlayerClient(HttpClient httpClient, MobileAppSettings settings) : base(httpClient, settings)
{}
public async Task<List<PlayerModel>> GetAllAsync()
{
var path = #"players/get-all";
return await SendGetRequestAsync<List<PlayerModel>>(path);
}
}
public class HttpsClientHandlerService : IHttpsClientHandlerService
{
public HttpMessageHandler GetPlatformMessageHandler()
{
#if ANDROID
var handler = new Xamarin.Android.Net.AndroidMessageHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
if (cert != null && cert.Issuer.Equals("CN=localhost"))
return true;
return errors == System.Net.Security.SslPolicyErrors.None;
};
return handler;
#elif IOS
var handler = new NSUrlSessionHandler
{
TrustOverrideForUrl = IsHttpsLocalhost
};
return handler;
#elif WINDOWS || MACCATALYST
return null;
#else
throw new PlatformNotSupportedException("Only Android, iOS, MacCatalyst, and Windows supported.");
#endif
}
#if IOS
public bool IsHttpsLocalhost(NSUrlSessionHandler sender, string url, Security.SecTrust trust)
{
if (url.StartsWith("https://localhost"))
return true;
return false;
}
#endif
}
In the android message handler I captured an error (if it is an error):
System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors | System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch

Custom auth "The oauth state was missing or invalid"

My team and I created a custom OAuth to be used for external SSO. It works on localhost but as soon as we take it up to our staging environment we get an "The oauth state was missing or invalid." error.
We used "https://auth0.com/" for testing.
To try and troubleshoot this we overrode the following built in methods and via breakpoints can see that Query state comes back null.
protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri);
protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync();
I need some help figuring out why this is a problem on staging and not on local as we are a bit stumped. A theory we have is that the decoder used inside these methods change on var properties = Options.StateDataFormat.Unprotect(state); and thus because they aren't the same they can't decode each others states. I will put our implementation below, if its required I can paste the built in methods as well but I can't fathom the problem lying with the built in functions.
Startup:
foreach (var customAuthItem in customAuthList)
{
services.AddAuthentication().AddCustom(customAuthItem.CampaignId, options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.AuthorizationEndpoint = customAuthItem.AuthEndpoint;
options.TokenEndpoint = customAuthItem.TokenEndpoint;
options.UserInformationEndpoint = customAuthItem.UserInfoEndpoint;
options.ClientId = customAuthItem.ClientId;
options.ClientSecret = customAuthItem.ClientSecret;
});
}
Options:
public class CustomAuthenticationOptions : OAuthOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="CustomAuthenticationOptions"/> class.
/// </summary>
public CustomAuthenticationOptions()
{
ClaimsIssuer = CustomAuthenticationDefaults.Issuer;
CallbackPath = CustomAuthenticationDefaults.CallbackPath;
AuthorizationEndpoint = CustomAuthenticationDefaults.AuthorizationEndpoint;
TokenEndpoint = CustomAuthenticationDefaults.TokenEndpoint;
UserInformationEndpoint = CustomAuthenticationDefaults.UserInformationEndpoint;
Scope.Add("openid");
Scope.Add("profile");
Scope.Add("email");
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
}
/// Gets the list of fields to retrieve from the user information endpoint.
/// </summary>
public ISet<string> Fields { get; } = new HashSet<string>
{
"email",
"name",
"sub"
};
Defaults:
public static class CustomAuthenticationDefaults
{
/// <summary>
/// Default value for <see cref="AuthenticationScheme.Name"/>.
/// </summary>
public const string AuthenticationScheme = "Custom";
/// <summary>
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
/// </summary>
public static readonly string DisplayName = "Custom";
/// <summary>
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
/// </summary>
public static readonly string Issuer = "Custom";
/// <summary>
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
/// </summary>
public static readonly string CallbackPath = "/signin-custom";
/// <summary>
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
/// </summary>
public static readonly string AuthorizationEndpoint = "https://dev-egd511ku.us.auth0.com/authorize";
/// <summary>
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
/// </summary>
public static readonly string TokenEndpoint = "https://dev-egd511ku.us.auth0.com/oauth/token";
/// <summary>
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
/// </summary>
public static readonly string UserInformationEndpoint = "https://dev-egd511ku.us.auth0.com/userinfo";
}
Handler:
protected override async Task<AuthenticationTicket> CreateTicketAsync(
[NotNull] ClaimsIdentity identity,
[NotNull] AuthenticationProperties properties,
[NotNull] OAuthTokenResponse tokens)
{
Serilog.Log.Debug("CustomAuthenticationHandler.CreateTicketAsync: STARTED!");
string endpoint = Options.UserInformationEndpoint;
if (Options.Fields.Count > 0)
{
endpoint = QueryHelpers.AddQueryString(endpoint, "fields", string.Join(',', Options.Fields));
}
using var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
Serilog.Log.Debug("CustomAuthenticationHandler.CreateTicketAsync: ABOUT TO SEND REQUEST!");
using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
if (!response.IsSuccessStatusCode)
{
Serilog.Log.Debug($"CustomAuthenticationHandler.CreateTicketAsync: FAILED REQUEST: {response.ReasonPhrase}");
await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);
throw new HttpRequestException("An error occurred while retrieving the user profile from Custom.");
}
var payloadString = await response.Content.ReadAsStringAsync();
Serilog.Log.Debug($"CustomAuthenticationHandler.CreateTicketAsync: PAYLOAD: {payloadString}");
using var payload = JsonDocument.Parse(payloadString);// Context.RequestAborted));
var principal = new ClaimsPrincipal(identity);
var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
context.RunClaimActions();
await Events.CreatingTicket(context);
return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
}
EDIT: The error is received after successful login and after being redirected back to our site. I can see through sentry breadcrumbs that the states are correct so it seems to be a decryption issue.
It turns out the problem is because I AddAuthentication() twice, it ignores the follow-up registration of the auth methods, resulting in only one OAuth working. This is a bit of a problem because we want to support multiple SSO options for our clients, but might need to figure out a different approach. I am just glad I finally know where the problem is.

How to implement a custom IAuthenticationModule for bearer tokens with RestSharp

I am using RestSharp to make requests to an API which uses bearer token authentication. Most requests run as expected, but there is a specific endpoint which always redirects the request to a dynamic location. When this redirect occurs, the Authorization header is lost (by design), thus resulting in a Bad Request.
I've done some looking into the issue and found one similar issue here, but the custom AuthenticationModule I made is never having the Authenticate function called.
Am I missing something obvious in the setup that is preventing the Authentication module from being used, or is something else going on?
Thanks!
My Authenticator class:
public class AdpAuthenticator : IAuthenticator
{
/// <summary>
/// The current access token for making requests to the API.
/// </summary>
private static string AccessToken { get; set; }
/// <summary>
/// When the current access token expires.
/// </summary>
private static DateTime TokenExpiresOn { get; set; }
private static CredentialCache CredentialCache { get; set; }
/// <summary>
/// Singleton instance for making requests for access tokens.
/// </summary>
private static IRestClient AuthenticationClient { get; set; }
/// <summary>
/// Singleton instance of the request for obtaining access tokens.
/// </summary>
private static IRestRequest AuthenticationRequest { get; set; }
/// <summary>
/// Construct a new AdpAuthenticator.
/// </summary>
/// <param name="adpClientId"></param>
/// <param name="adpClientSecret"></param>
/// <param name="adpCertPath"></param>
public AdpAuthenticator(string adpClientId, string adpClientSecret, string adpCertPath)
{
if (string.IsNullOrWhiteSpace(adpClientId)) throw new ArgumentNullException("Passed adpClientId was empty or null.");
if (string.IsNullOrWhiteSpace(adpClientSecret)) throw new ArgumentNullException("Passed adpClientSecret was empty or null.");
if (CredentialCache == null)
{
CredentialCache = new CredentialCache
{
{new Uri("https://api.adp.com"), "Basic", new NetworkCredential(adpClientId, adpClientSecret) }
};
}
if (AuthenticationClient == null)
{
X509Certificate2Collection certificateCollection;
X509Certificate2 certificate = new X509Certificate2(adpCertPath);
certificateCollection = new X509Certificate2Collection
{
certificate
};
AuthenticationClient = new RestClient("https://api.adp.com")
{
ClientCertificates = certificateCollection,
Authenticator = new HttpBasicAuthenticator(adpClientId, adpClientSecret)
};
AuthenticationClient.UseSerializer(new JsonNetSerializer());
}
if (AuthenticationRequest == null)
{
AuthenticationRequest = new RestRequest("auth/oauth/v2/token", Method.POST)
{
Credentials = CredentialCache
};
AuthenticationRequest.AddOrUpdateParameter("grant_type", "client_credentials", ParameterType.QueryString);
}
RegisterAuthenticationModule(new Uri("https://api.adp.com/"));
}
/// <summary>
/// Authenticate a request.
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
public void Authenticate(IRestClient client, IRestRequest request)
{
//If accessToken is null or expired, get a new one.
if (!HasValidToken())
{
RefreshAccessToken();
}
//request.AddOrUpdateParameter("Authorization", AccessToken, ParameterType.HttpHeader);
//var newCache = new CredentialCache
//{
// {new Uri("https://api.adp.com/"), "Bearer", new NetworkCredential(AccessToken, "") }
//};
var newCache = new CredentialCache();
newCache.Add(new Uri("https://api.adp.com/"), AdpAuthenticationModule.TheAuthenticationType, new NetworkCredential(AccessToken, ""));
request.Credentials = newCache;
//request.AddOrUpdateParameter("Authorization", "Bearer " + AccessToken, ParameterType.HttpHeader);
}
private void RefreshAccessToken()
{
try
{
var response = AuthenticationClient.Execute<AuthorizationResponse>(AuthenticationRequest);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new FailedAuthenticationException($"Authentication failed to refresh access token, returned with code {response.StatusCode}. Content: \"{response.Content}\".", null);
}
if (string.IsNullOrWhiteSpace(response.Data.access_token))
{
throw new Exception("Error: response returned during access token refresh gave Status 200 OK, but access_token returned was null or whitespace.");
}
AccessToken = response.Data.access_token;
if (response.Data.expires_in <= 0)
{
throw new Exception("Error: response returned during access token refresh gave Status 200 OK, but expires_in value returned was <=0.");
}
TokenExpiresOn = DateTime.Now.AddSeconds(response.Data.expires_in);
}
catch (FailedAuthenticationException)
{
throw;
}
catch (Exception e)
{
throw new FailedAuthenticationException($"Authentication failed to refresh access token, see inner exception details.", e);
}
}
/// <summary>
/// Returns whether the current access token is valid.
/// </summary>
/// <returns>False if token is null or has 10 or less minutes until expiry; else returns true.</returns>
public bool HasValidToken()
{
return !string.IsNullOrEmpty(AccessToken) && DateTime.Now.CompareTo(TokenExpiresOn.AddMinutes(-10.0)) < 0;
}
private static AdpAuthenticationModule RegisterAuthenticationModule(Uri loginServerUrl)
{
var registeredModules = AuthenticationManager.RegisteredModules;
AdpAuthenticationModule authenticationModule;
while (registeredModules.MoveNext())
{
object current = registeredModules.Current;
if (current is AdpAuthenticationModule)
{
authenticationModule = (AdpAuthenticationModule)current;
if (authenticationModule.LoginServerUrl.Equals(loginServerUrl))
{
return authenticationModule;
}
}
}
authenticationModule = new AdpAuthenticationModule(loginServerUrl);
AuthenticationManager.Register(authenticationModule);
return authenticationModule;
}
}
My Custom Authentication Module:
public class AdpAuthenticationModule : IAuthenticationModule
{
/// <summary>
/// The name of the custom authentication type.
/// </summary>
public string AuthenticationType => TheAuthenticationType;
public static string TheAuthenticationType => "AdpAuthentication";
/// <summary>
/// Returns false, as this IAuthenticationModule cannot pre-authenticate.
/// </summary>
public bool CanPreAuthenticate => false;
private readonly CredentialCache credentialCache = new CredentialCache();
private readonly Uri loginServerUrl;
internal CredentialCache CredentialCache
{
get
{
return credentialCache;
}
}
internal Uri LoginServerUrl
{
get
{
return loginServerUrl;
}
}
internal AdpAuthenticationModule(Uri loginServerUrl)
{
this.loginServerUrl = loginServerUrl ?? throw new ArgumentNullException("AdpAuthenticationModule.loginServerUrl");
}
/// <summary>
/// Builds and returns a <see cref="Authorization"/> object for a request.
/// </summary>
/// <param name="challenge"></param>
/// <param name="request"></param>
/// <param name="credentials"></param>
/// <returns></returns>
public Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials)
{
Authorization result = null;
if (request != null && credentials != null)
{
NetworkCredential creds = credentials.GetCredential(LoginServerUrl, AuthenticationType);
if (creds == null)
{
return null;
}
ICredentialPolicy policy = AuthenticationManager.CredentialPolicy;
if (policy != null && !policy.ShouldSendCredential(LoginServerUrl, request, creds, this))
{
return null;
}
string token = Convert.ToBase64String(Encoding.UTF8.GetBytes(creds.UserName));
result = new Authorization(string.Format("Bearer {0}", token));
}
return result;
}
/// <summary>
/// Returns null, since this IAuthenticationModule cannot pre-authenticate.
/// </summary>
/// <param name="request"></param>
/// <param name="credentials"></param>
/// <returns></returns>
public Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
{
return null;
}
}
Implementation of IAuthenticationModule need to be registered in the AuthenticationManager class from System.Net.
Use the following code :
AuthenticationManager.Register(new AdpAuthenticationModule());

Unauthorized GET request for .NET Core RESTful web API

Yesterday I coded myself a simple RESTful web API in .NET Core (solution named Vault) with a single method that gets the profile of the user depending only on the Windows user name. I now have a second solution that will call some requests to my self-hosting service previously mentioned. When I use Postman, I can retrieve the data with ease when I call a GET on my only method in Vault, but when I build the URI in Mainframe and execute, I get an Unauthorized error and it confuses me as Vault does not require specific login like username and password. I also put a breakpoint in Vault and unlike when I'm using Postman, it does not reach my code when calling via the Mainframe solution.
Here where is build my REST request and call the service (GetProfile())
public VaultApiClient(ConfigurationManagerWrap configuration)
{
this.configuration = configuration;
this.client = new RestClient(new Uri(this.configuration.GetAppSetting<string>(ConfigurationKeys.VaultApiURL)));
}
/// <summary>
/// The get profile.
/// </summary>
/// <returns>
/// The <see cref="UserProfile"/>.
/// </returns>
public UserProfile GetProfile()
{
var request = new RestRequest("profile") { Method = Method.GET};
//request.AddParameter("profile", ParameterType.UrlSegment);
var response = this.client.Execute(request);
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception(
$"Could not get the user profile ({response.StatusCode} {response.StatusDescription})");
}
return RestJsonSerializer.Default.Deserialize<UserProfile>(response);
}
Im hosting local so the base URI, aka ConfigurationKeys.VaultApiURL, is localhost5000/api/
My Mainframe controller :
public HomeController()
: this(new VaultApiClient(new ConfigurationManagerWrap()))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HomeController"/> class.
/// </summary>
/// <param name="vaultApiClient">
/// The vault api client.
/// </param>
public HomeController(IVaultApiClient vaultApiClient)
{
this.vaultApiClient = vaultApiClient;
}
/// <summary>
/// The index.
/// </summary>
/// <returns>
/// The <see cref="ActionResult"/>.
/// </returns>
public ActionResult Index()
{
var profile = this.GetProfile();
this.ViewBag.IsEdit = false;
this.ViewBag.IsError = false;
this.ViewBag.ErrorMessage = "";
if (this.TempData.ContainsKey("IsEdit"))
{
this.ViewBag.IsEdit = true;
this.TempData.Remove("IsEdit");
if (this.TempData.ContainsKey("ErrorMessage"))
{
this.ViewBag.IsError = true;
this.ViewBag.ErrorMessage = this.TempData["ErrorMessage"];
this.TempData.Remove("ErrorMessage");
}
}
return this.View("Index", profile);
}
private UserProfile GetProfile()
{
return this.vaultApiClient.GetProfile();
}
And here is the vault controller method that handles the GET request in question:
[HttpGet]
[Route("/api/Profile")]
[Produces(typeof(UserProfile))]
public IActionResult SearchProfile()
{
try
{
if (!this.currentuser.IsAuthenticated)
{
throw new Exception("This service does not support anonymous calls.");
}
var profile = Task.Run(() => this.personalizationService.GetUserProfileAsync(this.currentuser.GetCurrentWindowsIdentityName)).Result;
var userProfile = this.persistenceToDataModelConverter.Convert(profile);
userProfile.UserAdLogin = this.currentuser.GetCurrentWindowsIdentityName;
return this.Ok(userProfile);
}
catch (Exception ex)
{
return this.NotFound(ex);
}
}
Lastly, here are a few pics of before and when the error is thrown.
Credential information must be provided with client request in order to authenticate with the server.

C# Flurl and HttpClient, no response from REST API

I am having a problem with getting a response from an API with my code, the request does not time out and it does not give me a response at all. I have made an api endpoint in my own API to return the json string to then manually post the json data with "Firefox Poster" and it works just fine. With this I believe that the problem is somewhere in my code.
I got a C# WebAPI which I am developing to be used with an Angular frontend(This works, it's just for history). When calling my API I create the object "EnrollmentRequestDto"
public class EnrollmentRequestDto
{
/// <summary>
/// Context Information for the request. Contains the Ship-To, language code and timezone.
/// </summary>
[JsonProperty("requestContext")]
public RequestContextDto RequestContext { get; set; }
/// <summary>
/// Unique ID provided by DEP to reseller on provisioning DEP access. This would be the reseller's DEP ID if its posted by distributor OBO a reseller, and would be his own depResellerId if a reseller is posting for self.
/// </summary>
[JsonProperty("depResellerId")]
public string DepResellerId { get; set; }
/// <summary>
/// Unique transaction ID provided by the reseller
/// </summary>
[JsonProperty("transactionId")]
public string TransactionId { get; set; }
/// <summary>
/// List of orders in the transaction (limit 1000 per transaction)
/// </summary>
[JsonProperty("orders")]
public List<OrderDto> Orders { get; set; }
}
After this object is created I send to to my class RequestHandler and the method BulkEnrollRequest, which atm is written with HttpClient extension Flurl, which can be found here: github
public IResponse BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
try
{
var result = _bulkEnrollUrl.PostJsonAsync(enrollmentRequest).ReceiveJson<SuccessResponse>();
result.Wait();
return result.Result;
}
catch (FlurlHttpTimeoutException)
{
throw new AppleTimeOutException();
}
catch (FlurlHttpException ex)
{
return _errorHandler.DeserializeFlurlException(ex);
}
}
I have also tried this to make sure that nothing happens in Flurl(This is just to debug to the point where I want to get the response):
public async Task<IResponse> BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
var json = JsonConvert.SerializeObject(enrollmentRequest);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.PostAsync(_bulkEnrollUrl, new StringContent(json, Encoding.UTF8, "application/json"));
return new SuccessResponse();
}
And the code freezes at the await point and nothing is happening. I have tried to place a breakpoint after the BulkEnrollRequest so it never leaves this method.
Now here's for the funny part: It did work while I was working on the ErrorHandler and at some point the API just stopped responding. Wireshark shows that a request is being made... so I am stuck.
Any help is appreciated.
EDIT
This now works! I implemented async all the way from the API Controller down to RequestHandler, then awaited for every call. For reference:
public async Task<IResponse> BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
try
{
var result = await _bulkEnrollUrl.PostJsonAsync(enrollmentRequest).ReceiveJson<SuccessResponse>();
return result;
}
catch (FlurlHttpTimeoutException)
{
throw new AppleTimeOutException();
}
catch (FlurlHttpException ex)
{
return _errorHandler.DeserializeFlurlException(ex);
}
}
This is now fixed. I believe that the reason why the Wireshark traffic looked so weird was because of a deadlock in the code, and the timeout was on my side which meant that I could never FIN ACK the information. To fix this i implemented async on all methods, from the Controller down to my RequestHandler class. For reference:
public async Task<IResponse> BulkEnrollRequest(EnrollmentRequestDto enrollmentRequest)
{
try
{
var result = await _bulkEnrollUrl.PostJsonAsync(enrollmentRequest).ReceiveJson<SuccessResponse>();
return result;
}
catch (FlurlHttpTimeoutException)
{
throw new AppleTimeOutException();
}
catch (FlurlHttpException ex)
{
return _errorHandler.DeserializeFlurlException(ex);
}
}
Try this code
Install-Package Microsoft.AspNet.WebApi.Client
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("api/products", enrollmentRequest);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<T>();
}
}

Categories

Resources