I've consumed a WSDL as and have succesfully called web-service methods.The request has an Authorization header that can only be added at the point the request is made:
public static NumberCaptureClient Connect()
{
var remoteAddress = new EndpointAddress("https://website.com:8443/webservice/WebServiceNumberCapture");
using (var NumberCaptureClient = new NumberCaptureClient(new BasicHttpBinding(BasicHttpSecurityMode.Transport), remoteAddress))
{
NumberCapture.ClientCredentials.UserName.UserName = "test";
NumberCapture.ClientCredentials.UserName.Password = "test";
try
{
using (OperationContextScope scope = new OperationContextScope(NumberCaptureClient.InnerChannel))
{
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " +
Convert.ToBase64String(Encoding.ASCII.GetBytes(NumberCaptureClient.ClientCredentials.UserName.UserName + ":" + NumberCaptureClient.ClientCredentials.UserName.Password));
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
}
}
catch (Exception error)
{
MessageBox.Show("Error");
return null;
}
return NumberCaptureClient;
}
}
As you can see I'm in need of returning an instance of the proxy client (the client has hundereds of methods that all need the header) but need it so the headers are always sent, with the 'using' clause this isn't possible as the scope is lost outside of it.
Is there a way to permanantly add the headers so they are sent with every request to the webservice?
This is a WCF proxy, right? Generally speaking, you should remove the using from your Connect method. If the method is used to get a prepared service proxy, then it makes no sense to dispose it as part of the method that creates it.
Instead, the method/code that uses the Connect method should be responsible of using it:
using(var proxy = theClass.Connect())
{
// call service using proxy here
// process response here, if you may need to call the service again
// as part of processing
}
// process response here if you don't need to call the service again
There is a catch however, since for WCF proxies, the Dispose method internally calls the Close method, which in turn can throw exceptions. For this reason, Microsoft has a recommendation for how to handle cleaning up of WCF proxies. See here.
Related
We have a global handler setup for catching a specific type of exception. It is possibly thrown from multiple service endpoints using a base service implementation. We bind the error handlers and try redirect using a RedirectHttpHandler:
ServiceExceptionHandlers.Add(HandledErrorLogging);
...
private object HandledErrorLogging(IRequest httpreq, object request, Exception ex)
{
if (ex is NoActiveSubscriptionException)
{
return new RedirectHttpHandler
{
RelativeUrl = "/account?error=",
StatusCode = HttpStatusCode.TemporaryRedirect
};
}
}
We are using JsonServiceClient to query these endpoints.
The JsonServiceClient is not respecting the RedirectHttpHandler redirect. When we connect jsonclient.responsefilter(r), r.redirectedicted is false:
let client = new JsonServiceClient(process.env.REACT_APP_API_BASE_URL);
client.setBearerToken(cookie.load("XSRF-TOKEN"));
JsonServiceClient.globalResponseFilter = function(e)
{
console.log("e.redirect:", e.redirected));
};
return client;
}
What is the best way to cause a redirect using the ServiceExceptionHandlers and the JsonServiceClient ?
RedirectHttpHandler is an IHttpAsyncHandler, it can only be used at the start of the request pipeline in RawHttpHandlers which is used to tell ServiceStack which HttpHandler it should use to handle the request.
ServiceExceptionHandlers is used to override handling of an Exception which you can override to return a different error Response DTO.
If nothing has been written to the Response you can return a redirect response with HttpResult.Redirect().
I've been strugling for days trying to prevent multiple connections in pool when using HttpClient
I've built a REST WebApi from which I'm calling an external endpoint by using HttpClient inside of it. A controller's action is called multiple times by a client in order to perform aforementioned external call.
Previously I was using WebClient but it suffered of SocketExhaustation when several requests were made. Searching on the web I found that migrating to HttpWebClient will solve my issues if used statically or within a singleton. I even tried to implement IHttpClientFactory but it was useless, as I experienced the same issue on my injected services.
This is my current implementation, after several refactors. Sorry if currently this code is not optimal but after multiple rewritings this was my last resort.
public static class CustomHttpClientManager
{
private static HttpClient _httpClient;
public static void Initialize()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
CookieContainer cookies = new CookieContainer();
_httpClient = new HttpClient(
new SocketsHttpHandler()
{
Proxy = new CustomProxy(),
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromSeconds(30),
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(20),
MaxConnectionsPerServer = 2,
CookieContainer = cookies,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
});
_httpClient.DefaultRequestHeaders.ConnectionClose = false;
}
public static async Task<string> GetAsString(string url){
HttpResponseMessage response = null;
try{
HttpRequestMessage httpRequestMessage = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
httpRequestMessage.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
httpRequestMessage.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");
httpRequestMessage.Headers.Add("Accept-Language", "en-GB,en-US;q=0.8,en;q=0.6");
httpRequestMessage.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3");
response = await _httpClient.SendAsync(httpRequestMessage);
if(response.IsSuccessStatusCode){
return await response.Content.ReadAsStringAsync();
}
else{
throw new Exception(ex.Message);
}
}
catch(Exception ex){
return $"Could not fetch response from: {connectionParameters.Url}, due to: {ex.Message}";
}
finally{
if(response != null){
response.Dispose();
}
}
}
}
The callstack until reaching the current code goes like this:
At Startup the WebApi app calls CustomHttpClientManager's Initialize method, which instantiates the HttpClient only once.
A client calls WebApi controller's action.
The action calls a business logic class (service class)
The logic class uses the CustomHttpClientManager's GetAsString method to perform the external call by using previously instantiated HttpClient
The client performs a lot of calls to the WebApi (around 200 rq per minute), and when running netstat -ano on the command prompt, it shows a quite long list of pooled connections whose status is TIME_WAIT, which shows that HttpClient is not reusing connections from the pool at all. These connections only dissappear around 2 minutes after being used.
Besides this code my other approaches were.
Injecting IHttpClientFactory on Startup by using .AddHttpClient()
Using the same class above, but using a thread safe singleton version.
None of them worked. I don't know what else to do. This has driven me crazy for around 2 days straight.
I've tested this implementation from a simple console app, and the issue is not happening. This suggests me that somehow WebApi action keeps closing the external connection once a reply to its caller has been given, and once another call is made, it creates another connection in the pool and it goes on and on for the next incomming requests as well.
BTW, I'm using .NET Core 3.1
Thanks in advance
Using the
PooledConnectionLifetime = TimeSpan.FromSeconds(30),
means that the connection is removed form the pool after 30s.
You may change that to be longer (e.g. 10 mins) - bot not too long to accommodate
the DNS changes.
I currently have an ServiceStack Service that does nothing but relay requests to an internal ServiceStack service.
The relay service is setup something like this (code made brief as an example):
public class RelayService : Service
{
public SomeDTO Get(FetchSomething request)
{
try
{
return new JsonServiceClient(settings.OtherServiceURL).Get(request);
}
catch (Exception)
{
throw;
}
}
public void Put(PersistSomething request)
{
try
{
new JsonServiceClient(settings.OtherServiceURL).Put(request);
}
catch (Exception)
{
throw;
}
}
}
My questions are:
Is it best practice to new up a JsonServiceClient for each request? Or should I inject an instance?
Since the relay service can contain variations on Put/Get that return DTO's or void, is there a cleaner way to relay all calls to the backing ServiceStack service instead of having to duplicate each method in the relay service? Is it possible to do this all in one or a few methods using Any()?
Thanks for any input.
This previous answer for an example of a generic reverse proxy in ServiceStack.
The simplest and most generic approach in ServiceStack would be to register a RawHttpHandler that just forwards the Request to the downstream server and writes the Response to the Output Stream, e.g:
RawHttpHandlers.Add(_ => new CustomActionHandler((req, res) =>
{
var bytes = req.InputStream.ReadFully();
var proxyUrl = settings.OtherServiceURL.CombineWith(req.RawUrl);
var responseBytes = proxyUrl.SendBytesToUrl(method: req.Verb,
requestBody: bytes,
accept:MimeTypes.Json,
contentType: req.ContentType,
responseFilter: webRes =>
{
res.StatusCode = (int)webRes.StatusCode;
res.StatusDescription = webRes.StatusDescription;
res.ContentType = webRes.ContentType;
});
res.OutputStream.Write(responseBytes, 0, responseBytes.Length);
}));
In order to access the RequestStream you'll also want to tell ServiceStack to not inspect the FormData when creating the Request (as this forces reading the request body), which you can skip with:
SetConfig(new HostConfig {
SkipFormDataInCreatingRequest = true
});
Another approach would be to configure something like IIS Application Request Routing and URL Rewriting to use as a reverse proxy.
I have some problem on a WCF proxy class (not sure if it's the proxy or the service class), here is the context:
I have a WCF service that I consume on a web application, this service calls another service and then process the response to take it back to the web app. Here is the construction of that method
public CreateProjectResponse CreateNewProject(List<CreateProjectRequestProject> projects)
{
ServiceHelper helper = new ServiceHelper();
CreateProjectResponse response = helper.CreateNewProject(projects);
return response;
}
Everything is just fine up to the response object assignation. I have my correct list of "CreateProjectResponseProject" objects. The problem is that after the return statement I see that the service class is creating a NEW set of "CreateProjectResponseProject" objects as if it's calling the constructor again and assigning the default values (null in this case).
Does anyone have an idea what can be happening? I have been researching and don't seem to find any related solution. BTW... this process was working before, nothing have changed on the solution. Hope someone can help. Thanks!
EDIT: Here is the code for the helper class:
public class ServiceHelper
{
public CreateProjectResponse CreateNewProject(List<CreateProjectRequestProject> projects)
{
CreateProjectRequest request = new CreateProjectRequest();
CreateProjectResponse response = new CreateProjectResponse();
ProjectCreator create = new ProjectCreator();
WebServiceConfig configs = new WebServiceConfig();
request.Projects = projects;
configs.Password = "XXXXXXX";
configs.Username = "USER";
configs.RemoteAddress = "https://server/listener/connector";
configs.EndpoingConfig = "CreateProjectEndpoint";
try
{
response = create.CreateProject(configs, request);
}
catch (Exception ex)
{
string messageError = "unable to create project:" + ex.Message.ToString();
}
return response;
}
}
I was using the WCF service as an intermediate to communicate to another service, I removed the intermediate and called my helper class directly from the web application (with the proper endpoint config) and everything works fine now.
I created several POCO then created a DbContext (FooDbContext) - I then created a DataService class device from DataService< FooDbContext > calll FooDatService. I can access all my data in my silverlight app and if I start a Web Browser I can access it through the URL as expected. Now I want to allow to the DataService only after a successful login.
I've blogged on that like 3 years ago
http://netpl.blogspot.com/2010/04/aspnet-forms-authentication-sharing-for.html
The idea is to reuse the forms cookie to guard your invocations so that only logged in users are allowed to call the service.
You can add a service authorization manager to your WCF service to put all methods and endpoints of that service under access control, without modifying any of the implementation of the service.
Creating and starting your WCF service:
Uri[] restUris = new Uri[] { new Uri(baseUri, "Api/v1/") };
// substitute your service host type here. I'm using WCF OData DataServiceHost
restAPIServiceHost = new DataServiceHost(typeof(API.RestAPIService), restUris);
var saz = restAPIServiceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
if (saz == null)
{
saz = new ServiceAuthorizationBehavior();
restAPIServiceHost.Description.Behaviors.Add(saz);
}
saz.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
restAPIServiceHost.Open();
The above can also be done via web.config magic.
In your MyServiceAuthorizationManager implementation:
public class MyServiceAuthorizationManager: System.ServiceModel.ServiceAuthorizationManager
{
public override bool CheckAccess(OperationContext operationContext, ref Message message)
{
var reqProp = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
var authHeader = new AuthorizationHeader(reqProp.Headers[HttpRequestHeader.Authorization]);
bool authorized = // your code to decide if caller is authorized;
if (!authorized)
{
var webContext = new WebOperationContext(operationContext);
webContext.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
// optional: give caller hints where to go to login
webContext.OutgoingResponse.Headers.Add( HttpResponseHeader.WwwAuthenticate, String.Format("Bearer realm=\"{0}\"", baseUri.AbsoluteUri));
}
return authorized;
}
}
This CheckAccess method will be called for every request received by your WCF service, before the request is dispatched to the WCF implementation methods.