Record request and response from client layer for audit logging in .net core without using middleware - .net-core

Is there a way of logging the request and response from the client layer(not from controller as we can use middleware to log the same there).
I am looking to eliminate developer code for audit log here (//log request ,//log response and and creating a provider context ) instead move them to a common handler , may be inherit from delegating handler delegating handler and have the Audit log code there.
Any ideas ?
Currently we have audit logging in the client where another service is called but the developer has to do the following :
Client layer code:
{
IRestResponse response = null;
ConnectorHTMLResponse CCMSResponse = null;
request.Validate(request.TemplateName);
var providerContext = _messageTracker.CreateProviderContext(correlationId, "MailTrigger", "GetHTML", OperationProtocols.HTTPS);
//log request
await providerContext.StartAsync(request, param => request.TemplateName);
var bodyJson = ToBodyJson(request, TemplateType.HTML);
try
{
response = await ExecuteAsync(bodyJson, correlationId);
}
catch (Exception ex)
{
await providerContext.RaiseExceptionAsync(ex);
throw;
}
Response = ConstructHTMLDocumentDetails(ValidateResponse(response));
//log response
await providerContext.CompletedAsync(Response);
return Response;
}
//and in the message tracker(Common code )
public static ProviderContext CreateProviderContext(this IMessageTracker messageTracker, string correlationId, string systemId, string operationName, OperationProtocols protocol)
{
var context = new ProviderContext(
messageTracker,
correlationId,
systemId,
operationName,
Assembly.GetCallingAssembly().GetName().Name,
protocol
);
return context;
}
public async Task StartAsync<T>(T payload, Func<T, string> primaryIdentifierFunc = null, Func<T, string> secondaryIdentifierFunc = null)
{
await StartAsync(payload, primaryIdentifierFunc?.Invoke(payload), secondaryIdentifierFunc?.Invoke(payload));
}
public async Task CompletedAsync<T>(T payload, Func<T, string> primaryIdentifierFunc = null, Func<T, string> secondaryIdentifierFunc = null)
{
_source.Payload = payload.AsPayload();
_source.PrimaryIdentifier = primaryIdentifierFunc?.Invoke(payload) ?? _source.PrimaryIdentifier;
_source.SecondaryIdentifier = secondaryIdentifierFunc?.Invoke(payload) ?? _source.SecondaryIdentifier;
await _tracker.TrackProviderResponseAsync(
//track in cloud
);
}``

Related

httpclient call is invoked after await keyword in dotnet core

I would like to do some operation which doesn't depend on API response and at the same time I want API to finish its process.
But in my case, API doesn't receive request when postasync is executed.
Instead, Web api receive request after await weatherForeCastdata.
I noticed strange behavior today
when I executed endpoint for first time(both the apis), webapi received request after postasync method. From second time, api receives request after executing await weatherForeCastdata.
I launched applictaion from vs code
browser : chrome
Can anyone help me ?
public async Task<IEnumerable<WeatherForecast>> Get()
{
var rng = new Random();
var weatherForeCastdata = new HttpClientCall<WeatherForecast>(_configuration).PostRequest(_configuration["Services:Payperiod"],new WeatherForecast());
Console.WriteLine("apiinvoked");
var data = await weatherForeCastdata;
//var data1 = await data.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(data);
}
public class HttpClientCall<T> where T : class
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
private readonly IConfiguration _configuration;
internal HttpClientCall(IConfiguration configuration)
{
httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; //Is valid
}
return true;
};
_configuration = configuration;
}
public async Task<string> PostRequest(string apiUrl, T postObject)
{
using (var client = new HttpClient(httpClientHandler))
{
client.DefaultRequestHeaders.Add("ClientId", _configuration["header"]);
Console.WriteLine(apiUrl);
var response = client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter());
var response1=await response;
return await response1.Content.ReadAsStringAsync();
}
}
}

HttpClient object does not works second time on Xamarin.iOS

I use HttpClient object for PostAsync. I need to add BackgroundSessionConfiguration for iOS while I am creating HttpClient object. So I changed my code like this:
var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration ("my.app.identifier");
_client = new HttpClient (new NSUrlSessionHandler (configuration));
This works when I send first request with PostAsync. But when I send request second time, it doesn't work.
I did it for Login Operation like this: (It works first time but if I logout and login again, it doesn't work.)
public class LoginService
{
private HttpClient _client;
public LoginService()
{
if (_client == null)
{
_client = Helper.CreateHttpClientLogin(_client);
}
}
public async Task<LoginResponse<LoginDataResponse>> Login(LoginRequest request)
{
LoginResponse<LoginDataResponse> responseModel = new LoginResponse<LoginDataResponse>();
try
{
string json = JsonConvert.SerializeObject(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var jsonBody = await _client.PostAsync(App.ServiceURL.Login_Url, content);
string jsonstr = await jsonBody.Content.ReadAsStringAsync();
if (jsonstr == null || jsonstr == "")
{
responseModel.Success = false;
responseModel.Status = 0;
responseModel.Message = AppResources.UnknownHostException;
}
else
responseModel = (LoginResponse<LoginDataResponse>)JsonConvert.DeserializeObject(jsonstr, typeof(LoginResponse<LoginDataResponse>));
}
catch (Exception ex)
{
string text = ex.ToString();
responseModel.Status = 0;
AppResources.Culture = CrossMultilingual.Current.CurrentCultureInfo;
responseModel.Message = AppResources.UnknownHostException;
}
return responseModel;
}
}
public class Helper
{
public static HttpClient CreateHttpClientLogin(HttpClient _client)
{
if (Device.RuntimePlatform == Device.iOS)
{
var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration("my.app.identifier");
_client = new HttpClient(new NSUrlSessionHandler(configuration));
}
else
{
//_client = new HttpClient(new System.Net.Http.HttpClientHandler());
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
_client = new HttpClient(handler);
}
return _client;
}
}
And I have this code on AppDelegate: (I don't know but maybe it causes the bug)
public static Action BackgroundSessionCompletionHandler;
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
// We get a completion handler which we are supposed to call if our transfer is done.
BackgroundSessionCompletionHandler = completionHandler;
}
What must I do for this?
Edit:
I solved the problem I mentioned above by creating the Login Service object once the application was first opened. (After logout previously, I was rebuilding every time I login)
But now I have other error. When I run my app on "iPhone 7 plus - iOS 13.6" device I got this error:
System.Net.Http.HttpRequestException: unknown error ---> Foundation.NSErrorException: Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSErrorFailingURLStringKey=https://mydomain/Api/Login, NSErrorFailingURLKey=https://mydomain/Api/Login, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundDataTask <E69F3EAF-0AE9-4FAE-A01B-988167B7F6BC>.<3>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDataTask <E69F3EAF-0AE9-4FAE-A01B-988167B7F6BC>.<3>, NSLocalizedDescription=unknown error}
--- End of inner exception stack trace ---
at System.Net.Http.NSUrlSessionHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x001d4] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.20.2.2/src/Xamarin.iOS/Foundation/NSUrlSessionHandler.cs:527
at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x0017e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:506
at App.Services.LoginService.Login (FileOrbis.Models.RequestModels.LoginRequest request) [0x00084] in C:\Users\PcName\Desktop\App\App\Services\LoginService.cs:40
And simulator log file is:
Startup:
arguments: --device=06098E5B-1853-4A83-8434-8071D8973A14 --launchsim=//Users/deytek/Library/Caches/Xamarin/mtbs/builds/App.iOS/b2c75f2acbd4ff91c305dba10ca791b7/bin/iPhoneSimulator/Debug/App.iOS.app -argument=-monodevelop-port -argument=51890 -setenv=__XAMARIN_DEBUG_PORT__=51890 --sdkroot=/Applications/Xcode.app -h=192.168.1.7 -ssh=deytek --launched-by=devenv-16.0
version: 16.7.0.0 (54a29526ef6f853bdd37adbcc3791ce90ca82735)
Connecting to existing client
Exit:
Exit Code: 0
I encounter with this error when I use Background Session Configuration. If I use normal HttpClient object (without Background Session Configuration), it works
NOTE: I also tried iPhone 5s iOS 12.4.8 and iPad Pro (3rd Generation) iOS 13.6.1 It works these devices. But it doesn't work on iPhone 7 Plus 13.6

SignalR Core How to get a connection parameter server side

As follow this answer How to send Parameter/Query in HubConnection SignalR Core
I'm setting the client :
const connectionHub = new HubConnectionBuilder()
.withUrl(Constants.URL_WEB_SOCKET + '?token=123')
.build();
but how to get the token value server side?
public override async Task OnConnectedAsync()
{
_connectionId = Context.ConnectionId;
var token = Context.Items["token"]; // this is null
var token2 = Context.QueryString["token"]; // 'HubCallerContext' does not contain a definition for 'QueryString'
await base.OnConnectedAsync();
}
if you want to get token value in .net core, you can use the following code:
var httpContext = Context.GetHttpContext();
var tokenValue = httpContext.Request.Query["token"];
You can send a parameter in QueryString.
In your client, declare a string dictionary and connection
private Dictionary<string, string> _querystringdata = new Dictionary<string, string>();
private HubConnection _connection;
private const string HubUrl = "your hub url";
Then, assign the value you want to send
_querystringdata.Add("key", "Value");
_connection = new HubConnection(HubUrl, _querystringdata);
Start the connection
if (_connection.State == ConnectionState.Disconnected)
{
// Creating the signalrHub proxy
IHubProxy signalrHub = _connection.CreateHubProxy("SignalrHub");
Console.WriteLine("Initiating Connection");
// starting the signalr connection
_connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection:{0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Connected to server");
//Client methods which server can invoke
signalrHub.On<dynamic>("sendMessage", (data) =>
{
Console.WriteLine("Message:- {0}", data);
// do something
});
}
}).Wait();
}
then in your server signalR hub class
public override Task OnConnected()
{
try
{
// getting the value sent with query string
var token = Context.QueryString.Get("Key");
// do something like connection mapping etc
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return (base.OnConnected());
}

Signup user to ASP.NET Identity from Xamarin

I have been stuck all day on a stupid problem with registering a user to my application.
Here is my code once the 'Register' button is clicked:
public ICommand RegisterCommand
{
get
{
return new Command(async() =>
{
var isSuccess = await _apiServices.RegisterAsync(Email, Password, ConfirmPassword);
if (isSuccess){
Message = "Registered Successfully";
}
else
{
Message = "Retry later";
}
});
}
}
Api services Register Async method:
public async Task<bool> RegisterAsync(string email, string password, string confirmPassword)
{
try
{
System.Diagnostics.Debug.WriteLine("Email: "+email);
var client = new HttpClient();
var model = new RegisterBindingModel
{
Email = email,
Password = password,
ConfirmPassword = confirmPassword
};
var json = JsonConvert.SerializeObject(model);
HttpContent content = new StringContent(json);
// content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("http://localhost:63724/api/Account/Register", content);
if (response.IsSuccessStatusCode)
{
return true;
}
return false;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("Error: "+e);
throw;
}
}
}
The Error that I get is:
System.Net.Http.HttpRequestException: An error occurred while sending the request ---> System.Net.WebException: Error: ConnectFailure (Connection refused) ---> System.Net.Sockets.SocketException: Connection refused
at System.Net.Sockets.Socket.Connect (System.Net.EndPoint remoteEP) [0x000b6] in <6c708cf596db438ebfc6b7e012659eee>:0
at System.Net.WebConnection.Connect (System.Net.HttpWebRequest request) [0x0016d] in <6c708cf596db438ebfc6b7e012659eee>:0
--- End of inner exception stack trace ---
To me this is very frustrating as I can register a use using Postman with the exact same localhost address. I am following Houssem Dellai's Xamarin.Forms mvc web api tutorials which can be found here
I had an issue with httpclient during the development of my app. I believe there was an issue with the cross-platform implementation of the httpclient class. iOS didn't know how to handle it.
Instead I implemented a very simple httpclient library called flurl: http://tmenier.github.io/Flurl/
First, you will need to install flurl in all project directories (iOS, Android, and the PCL) then the implementation is very simple.
using Flurl;
using Flurl.Http;
public async Task<User> CreateUserAsync(RegisterUserModel userModel)
{
string url = "your/backend/here";
//resp is a user object received and automatically converted into a c# object through the use of .ReceiveJson<typeofobject>();
var resp = await (url).PostJsonAsync(userModel)
.ReceiveJson<User>();
if (resp.LoginSession != null)
{
//Raise my registered event to let other classes know to proceed
OnUserRegistered(resp);
}
return resp;
}
As you can see it makes httpclient implementation very simple. Hopefully this helps.

How to send http request parameters using Jersey client

I use the following rest client implementation of jersey to consume a rest service.I am able to do it successfully.Additionally now I need to send request parameters which will be consumed as part of HttpServletRequest on the producer side.
Consumer side Jersey client code
private ClientResponse getWebClientResponse(String RESOURCE_PATH, String methodType, Object requestObj) {
WebResource webResource;
ClientResponse response = null;
try {
String environmentHost = EnvironmentUtil.resolveEnvironmentHost();
Client client = prepareClient();
String RWP_BASE_URI = environmentHost + "/workflow/rest";
webResource = client.resource(RWP_BASE_URI);
WebResource path = webResource.path(RESOURCE_PATH);
if (GET.equals(methodType)) {
response = path.type(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(
ClientResponse.class);
} else if (POST.equalsIgnoreCase(methodType)) {
response = path.type(javax.ws.rs.core.MediaType.APPLICATION_JSON).post(ClientResponse.class, requestObj);
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return response;
}
Producer side
#Context
public void setContext(SecurityContext context) {
this.context = context;
}
public HttpServletRequest getRequest() {
return request;
}
#Context
public void setRequest(HttpServletRequest request) {
this.request = request;
}
public String getSessionUserPID(final HttpServletRequest request,
final SecurityContext context) {
if (request.getSession(false) == null) {
final String exceptionMessage = "getSessionUserPID() failed, session NOT FOUND for this request";
final Response response = Response.status(ExceptionStatus.UNAUTHORIZED.getNumber())
.entity(exceptionMessage).build();
LOG.error(exceptionMessage);
throw new WebApplicationException(response);
}
if (context.getUserPrincipal() == null) {
final String exceptionMessage = "getSessionUserPID() failed, user principal NOT FOUND";
final Response response = Response.status(ExceptionStatus.UNAUTHORIZED.getNumber())
.entity(exceptionMessage).build();
LOG.error(exceptionMessage);
throw new WebApplicationException(response);
}
final String userPID = context.getUserPrincipal().getName();
if (userPID == null || userPID.isEmpty()) {
final String exceptionMessage = "getSessionUserPID() failed, user principal name cannot be null or empty";
final Response response = Response.status(ExceptionStatus.UNAUTHORIZED.getNumber())
.entity(exceptionMessage).build();
LOG.error(exceptionMessage);
throw new WebApplicationException(response);
}
return userPID;
}
The main intention here is currently I get user information from weblogic security context but for a particular scenario I need to pass this part of rest service request and obtain it from HttpServletRequest object.How can I obtain this from httpservletrequest
You can use QueryParam or PathParam in GET method and FormParam in the POST method for sending request parameter to the server.

Resources