I have a WCF service which requires an application ID parameter to be passed to each service call. Currently my exposed methods require a parameter. I want to try and push this information into the Channel headers. My WCF is hosted using Net.tcp. Here is my client proxy code:
public class CustomerClient : ClientBase<ICustomerBrowser>, ICustomerBrowser
{
public Customer Get(string ApplicationID, string CustomerId)
{
try
{
using (OperationContextScope _scope = new OperationContextScope(this.InnerChannel))
{
MessageHeader _header = MessageHeader.CreateHeader("AppID", string.Empty, ApplicationId);
OperationContext.Current.OutgoingMessageHeaders.Add(_header);
return Channel.Get(ApplicationId, CustomerId);
// return Channel.Get(CustomerId);
}
}
}
}
(The commented out line is what I want to use going forward).
Server code:
var _context = WebOperationContext.Current;
var h = _context.IncomingRequest.Headers;
In the _context object there are private methods containing my header, but publicly in the _context.IncomingRequest.Headers I get this:
There is no HttpRequestMessageProperty on the incoming Message.
So my question is, am I suffering because I am not hosting on HTTP? Is there a way to trick the server to give me access to those headers by adding some pseudo HTTP headers? Or can I get at the private members maybe via reflection?
You are using the wrong instance of an OperationContext.
The WebOperationContext is specialized for messages that are transported over http. It expects its headers to have a specif name. In the case of WebOperationContext the MessageHeaders dictionary expects a key named httpRequest, which isn't provided in your scenario.
As you're using the standard OperationContext client side should do the same server side:
var _context = OperationContext.Current;
var headers = _context.IncomingMessageHeaders;
foreach (MessageHeaderInfo h in headers)
{
if (h.Name == "AppID") {
Debug.WriteLine(h.ToString());
}
}
Related
I am trying to figure out how to best use the HttpClient class in ASP.Net Core.
According to the documentation and several articles, the class is best instantiated once for the lifetime of the application and shared for multiple requests. Unfortunately, I could not find an example of how to correctly do this in Core so I’ve come up with the following solution.
My particular needs require the use of 2 different endpoints (I have an APIServer for business logic and an API driven ImageServer), so my thinking is to have 2 HttpClient singletons that I can use in the application.
I’ve configured my servicepoints in the appsettings.json as follows:
"ServicePoints": {
"APIServer": "http://localhost:5001",
"ImageServer": "http://localhost:5002",
}
Next, I created a HttpClientsFactory that will instantiate my 2 httpclients and hold them in a static Dictionary.
public class HttpClientsFactory : IHttpClientsFactory
{
public static Dictionary<string, HttpClient> HttpClients { get; set; }
private readonly ILogger _logger;
private readonly IOptions<ServerOptions> _serverOptionsAccessor;
public HttpClientsFactory(ILoggerFactory loggerFactory, IOptions<ServerOptions> serverOptionsAccessor) {
_logger = loggerFactory.CreateLogger<HttpClientsFactory>();
_serverOptionsAccessor = serverOptionsAccessor;
HttpClients = new Dictionary<string, HttpClient>();
Initialize();
}
private void Initialize()
{
HttpClient client = new HttpClient();
// ADD imageServer
var imageServer = _serverOptionsAccessor.Value.ImageServer;
client.BaseAddress = new Uri(imageServer);
HttpClients.Add("imageServer", client);
// ADD apiServer
var apiServer = _serverOptionsAccessor.Value.APIServer;
client.BaseAddress = new Uri(apiServer);
HttpClients.Add("apiServer", client);
}
public Dictionary<string, HttpClient> Clients()
{
return HttpClients;
}
public HttpClient Client(string key)
{
return Clients()[key];
}
}
Then, I created the interface that I can use when defining my DI later on. Notice that the HttpClientsFactory class inherits from this interface.
public interface IHttpClientsFactory
{
Dictionary<string, HttpClient> Clients();
HttpClient Client(string key);
}
Now I am ready to inject this into my Dependency container as follows in the Startup class under the ConfigureServices method.
// Add httpClient service
services.AddSingleton<IHttpClientsFactory, HttpClientsFactory>();
All is now set-up to start using this in my controller.
Firstly, I take in the dependency. To do this I created a private class property to hold it, then add it to the constructor signature and finish by assigning the incoming object to the local class property.
private IHttpClientsFactory _httpClientsFactory;
public AppUsersAdminController(IHttpClientsFactory httpClientsFactory)
{
_httpClientsFactory = httpClientsFactory;
}
Finally, we can now use the Factory to request a htppclient and execute a call. Below, an example where I request an image from the imageserver using the httpclientsfactory:
[HttpGet]
public async Task<ActionResult> GetUserPicture(string imgName)
{
// get imageserver uri
var imageServer = _optionsAccessor.Value.ImageServer;
// create path to requested image
var path = imageServer + "/imageuploads/" + imgName;
var client = _httpClientsFactory.Client("imageServer");
byte[] image = await client.GetByteArrayAsync(path);
return base.File(image, "image/jpeg");
}
Done!
I’ve tested this and it work great on my development environment. However, I am not sure if this is the best way to implement this. I remain with the following questions:
Is this solution thread safe? (according to the MS doc: ‘Any public static (Shared in Visual Basic) members of this type are thread safe.’)
Will this set-up be able to handle a heavy load without opening many separate connection?
What to do in ASP.Net core to handle the DNS problem described in ‘Singleton HttpClient? Beware of this serious behaviour and how to fix.’ located at http://byterot.blogspot.be/2016/07/singleton-httpclient-dns.html
Any other improvements or suggestions?
If using .net core 2.1 or higher, the best approach would be to use the new HttpClientFactory. I guess Microsoft realized all the issues people were having so they did the hard work for us. See below for how to set it up.
NOTE: Add a reference to Microsoft.Extensions.Http.
1 - Add a class that uses HttpClient
public interface ISomeApiClient
{
Task<HttpResponseMessage> GetSomethingAsync(string query);
}
public class SomeApiClient : ISomeApiClient
{
private readonly HttpClient _client;
public SomeApiClient (HttpClient client)
{
_client = client;
}
public async Task<SomeModel> GetSomethingAsync(string query)
{
var response = await _client.GetAsync($"?querystring={query}");
if (response.IsSuccessStatusCode)
{
var model = await response.Content.ReadAsJsonAsync<SomeModel>();
return model;
}
// Handle Error
}
}
2 - Register your clients in ConfigureServices(IServiceCollection services) in Startup.cs
var someApiSettings = Configuration.GetSection("SomeApiSettings").Get<SomeApiSettings>(); //Settings stored in app.config (base url, api key to add to header for all requests)
services.AddHttpClient<ISomeApiClient, SomeApiClient>("SomeApi",
client =>
{
client.BaseAddress = new Uri(someApiSettings.BaseAddress);
client.DefaultRequestHeaders.Add("api-key", someApiSettings.ApiKey);
});
3 - Use the client in your code
public class MyController
{
private readonly ISomeApiClient _client;
public MyController(ISomeApiClient client)
{
_client = client;
}
[HttpGet]
public async Task<IActionResult> GetAsync(string query)
{
var response = await _client.GetSomethingAsync(query);
// Do something with response
return Ok();
}
}
You can add as many clients and register as many as needed in your startup with services.AddHttpClient
Thanks to Steve Gordon and his post here for helping me use this in my code!
In answer to a question from #MuqeetKhan regarding using authentication with the httpclient request.
Firstly, my motivation to use DI and a factory was to allow me to extend my application easily to different and multiple API’s and have easy access to that throughout my code. It’s a template I hope to be able to reuse multiple times.
In the case of my ‘GetUserPicture’ controller decribed in the original question above, I indeed for simplicity reasons removed the authentication. Honestly however, I am still in doubt if I need it there to simply retrieve an image from the imageserver. Anyhow, in other controllers I definitely do need it, so…
I’ve implemented Identityserver4 as my authentication server. This provides me with the authentication on top of ASP Identity.
For authorization (using roles in this case), I implemented IClaimsTransformer in my MVC ‘and’ API projects (you can read more about this here at How to put ASP.net Identity Roles into the Identityserver4 Identity token).
Now, the moment I enter my controller I have an authenticated and authorized user for which I can retrieve an access token. I use this token to call my api which is of course calling the same instance of identityserver to verify if the user is authenticated.
The last step is to allow my API to verify if the user is authorized to call the requested api controller. In the request pipeline of the API using IClaimsTransformer as explained before, I retrieve the authorization of the calling user and add it to the incoming claims.
Note that in case of an MVC calling and API, I thus retrieve the authorization 2 times; once in the MVC request pipeline and once in the API request pipeline.
Using this set-up I am able to use my HttpClientsFactory with Authorization and Authentication.
On big security part I am missing is HTTPS of course. I hope I can somehow add it to my factory. I'll update it once I've implemented it.
As always, any suggestions are welcome.
Below an example where I upload an image to the Imageserver using authentication (user must be logged in and have role admin).
My MVC controller calling the ‘UploadUserPicture’:
[Authorize(Roles = "Admin")]
[HttpPost]
public async Task<ActionResult> UploadUserPicture()
{
// collect name image server
var imageServer = _optionsAccessor.Value.ImageServer;
// collect image in Request Form from Slim Image Cropper plugin
var json = _httpContextAccessor.HttpContext.Request.Form["slim[]"];
// Collect access token to be able to call API
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
// prepare api call to update image on imageserver and update database
var client = _httpClientsFactory.Client("imageServer");
client.DefaultRequestHeaders.Accept.Clear();
client.SetBearerToken(accessToken);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("image", json[0])
});
HttpResponseMessage response = await client.PostAsync("api/UserPicture/UploadUserPicture", content);
if (response.StatusCode != HttpStatusCode.OK)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return StatusCode((int)HttpStatusCode.OK);
}
API handling the user upload
[Authorize(Roles = "Admin")]
[HttpPost]
public ActionResult UploadUserPicture(String image)
{
dynamic jsonDe = JsonConvert.DeserializeObject(image);
if (jsonDe == null)
{
return new StatusCodeResult((int)HttpStatusCode.NotModified);
}
// create filname for user picture
string userId = jsonDe.meta.userid;
string userHash = Hashing.GetHashString(userId);
string fileName = "User" + userHash + ".jpg";
// create a new version number
string pictureVersion = DateTime.Now.ToString("yyyyMMddHHmmss");
// get the image bytes and create a memory stream
var imagebase64 = jsonDe.output.image;
var cleanBase64 = Regex.Replace(imagebase64.ToString(), #"^data:image/\w+;base64,", "");
var bytes = Convert.FromBase64String(cleanBase64);
var memoryStream = new MemoryStream(bytes);
// save the image to the folder
var fileSavePath = Path.Combine(_env.WebRootPath + ("/imageuploads"), fileName);
FileStream file = new FileStream(fileSavePath, FileMode.Create, FileAccess.Write);
try
{
memoryStream.WriteTo(file);
}
catch (Exception ex)
{
_logger.LogDebug(LoggingEvents.UPDATE_ITEM, ex, "Could not write file >{fileSavePath}< to server", fileSavePath);
return new StatusCodeResult((int)HttpStatusCode.NotModified);
}
memoryStream.Dispose();
file.Dispose();
memoryStream = null;
file = null;
// update database with latest filename and version
bool isUpdatedInDatabase = UpdateDatabaseUserPicture(userId, fileName, pictureVersion).Result;
if (!isUpdatedInDatabase)
{
return new StatusCodeResult((int)HttpStatusCode.NotModified);
}
return new StatusCodeResult((int)HttpStatusCode.OK);
}
For situations when you can't use DI:
using System.Net.Http;
public class SomeClass
{
private static readonly HttpClient Client;
static SomeClass()
{
var handler = new SocketsHttpHandler
{
// Sets how long a connection can be in the pool to be considered reusable (by default - infinite)
PooledConnectionLifetime = TimeSpan.FromMinutes(1),
};
Client = new HttpClient(handler, disposeHandler: false);
}
...
}
Reference https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0#alternatives-to-ihttpclientfactory
Running ServiceStack 4.0.44 I have the following on my test client:
return client.Send(new GetVendor { VenCode = vencode });
vs what I had
// return client.Get(new GetVendor { VenCode = vencode });
and then on the server I have/had
public class VendorsService : Service {
public object Any(GetVendor request) {
var vendor = Db.SingleWhere<Vendors>("VenCode", request.VenCode);
return vendor;
}
//public object Get(GetVendor request) {
// var vendor = Db.SingleWhere<Vendors>("VenCode", request.VenCode);
// return vendor;
//}
}
//[Route("/vendor/{VenCode}", "GET")]
[Route("/vendor/{VenCode}")]
public class GetVendor : IReturn<Vendors> {
public string VenCode { get; set; }
}
public class Vendors {
:
:
}
My question is why when I pass "B&T" for VenCode -- and I understand that IIS is interpreting the & as part of the URL -- why does the Send work and return Vendors -- but the Get blows up with Bad Request unless I put
<httpRuntime requestPathInvalidCharacters="" />
into my web.config
Bottom line what is the difference? How would I implement CRUD routines with all the html special characters without modifying the registry etc? Or do I need to urlEncode them somehow?
Using a Get() API sends the request using the ?QueryString which is what requestPathInvalidCharacters is validating against.
When you use Send() you're sending a JSV serialized Request DTO via a HTTP POST which isn't validated by requestPathInvalidCharacters.
It's unlikely there's any way to disable ASP.NET's default behavior other than using Web.config, note this validation happens in ASP.NET before the request reaches ServiceStack.
I want to create an IIS-hosted webservice which I will consume using a universal windows store aoo (windows phone/windows 8.1/windows RT).
As I understand universal applications do not support proxy class generation and SOAP calls using "Add service reference" so I need to create a RESTful webservice and manually consume it in the universal application.
I've tried dozens of tutorials and approaches throughout the net but I never managed to actually POST data to the webservice.
I need to send objects of a custom class which is defined in a shared library to the webservice. I understand that I will need to serialize the Object and include it in the POST request, however no matter what I try I end up with different issues - e.g HTTP 400 Bad Request: The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml'; 'Json'.
I've seen several approaches to manually set the content type header, however the methods I found are not available in a universal application.
Can someone provide information or an example which is fitting my scenario (POST-ing via universal app)?
update 1: For further clarification: I am aware how WCF works and I was already able to complete a basic GET request like described in this post. However I was unable to extend that to also work with POST requests.
Some code I've tried:
public async static void SendStartup(CustomClass customObject)
{
var httpClient = new HttpClient();
var serialized = JsonConvert.SerializeObject(customObject);
var response = await httpClient.PostAsync("http://localhost:49452/Metrics.svc/LogStartup", new StringContent(serialized));
string content = await response.Content.ReadAsStringAsync();
}
Web Service Interface:
[OperationContract]
[WebInvoke(UriTemplate = "LogStartup", Method="POST", BodyStyle=WebMessageBodyStyle.Wrapped)]
string LogStartup(CustomClass obj);
Implementation:
public void LogStartup(CustomClass obj)
{
// nothing
}
This for example failes at runtime with the error mentioned above
There are two problem with your code.
1) You have to send the Content-Type header while your are making a request
var content = new StringContent(serialized,Encoding.UTF8,"application/json");
2) You have to use BodyStyle = WebMessageBodyStyle.Bare
WebMessageBodyStyle.Bare can work with one parameter as in your example, but if you want to post more parameters then you have to use WebMessageBodyStyle.Wrapped but then, your object you post should be modified as
var serialized = JsonConvert.SerializeObject(new { obj = customObject });
Here is a working code you can test with self-hosted WCF service
async void TestRestService()
{
var ready = new TaskCompletionSource<object>();
Task.Factory.StartNew(() =>
{
var uri = new Uri("http://0.0.0.0:49452/Metrics.svc/");
var type = typeof(Metrics);
WebServiceHost host = new WebServiceHost(type, uri);
host.Open();
ready.SetResult(null);
},TaskCreationOptions.LongRunning);
await ready.Task;
var customObject = new CustomClass() { Name = "John", Id = 333 };
var serialized = JsonConvert.SerializeObject(new { obj = customObject });
var httpClient = new HttpClient();
var request = new StringContent(serialized,Encoding.UTF8,"application/json");
var response = await httpClient.PostAsync("http://localhost:49452/Metrics.svc/LogStartup", request);
string content = await response.Content.ReadAsStringAsync();
}
[ServiceContract]
public class Metrics
{
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped)]
public string LogStartup(CustomClass obj)
{
return obj.Name + "=>" + obj.Id;
}
}
public class CustomClass
{
public string Name { set; get; }
public int Id { set; get; }
}
PS: If you want to return a json response then you can use ResponseFormat=WebMessageFormat.Json. You should then change the WebInvoke attribute as
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped,ResponseFormat=WebMessageFormat.Json)]
BTW: You can still dynamically choose the returned content type(xml or json) by setting AutomaticFormatSelectionEnabled.
Have you seen this article?
How to use HttpClient to post JSON data
Basically it seems like you need to add more parameters to your StringContent() constructor like this:
new StringContent(serialized, System.Text.Encoding.UTF8, "application/json");
One thing you need to know about Windows Communication Foundation is the ABC's.
A : Address
B : Binding
C : Contract
So the theory is incredibly simple, though while your coding it, it is quite odd. A simple tutorial can be found here or here. Several other tutorials can be found at Code Project for this exact approach.
Understanding Polymorphism may be helpful for understanding Windows Communication Foundation as it relies heavily on it.
[ServiceContract]
public interface IContent
{
[OperationContract]
void DoSomething(SomeModel model);
}
So what you're doing here is defining your service, defining your method. As I mentioned above we've explicitly declared our contract but we haven't implemented our method. Also we intend to pass SomeModel which would be our Data Contract.
We will build our model:
[DataContract]
public class SomeModel
{
[DataMember]
public string Name { get; set; }
}
The model can be incredibly simple like above, or incredibly complex. It will depend on usage.
Now we would like to implement our method:
public class Content : IContent
{
public void DoSomething(SomeModel model)
{
// Implementation
}
}
Now on the client, you simply consume your service. Once you understand the basics and how it serializes and deserializes you can use it for REST. Which tutorials also exist for that.
I'm currently developing a C# ServiceStack API.
In one of the Services I need to execute another service. I resolve the service from the Funq container and execute the relevant method but get json returned instead of .net objects.
I understand this is because the original request from the front end was for a content-type of json and the default content type is json.
Is there a way I can resolve the service and execute its methods but receive .net objects instead?
You can execute and delegate to another Service in ServiceStack by using ResolveService<T>, e.g:
From inside a ServiceStack Service:
using (var service = base.ResolveService<MyService>())
{
var response = service.Get(new MyRequest { ... });
}
From inside a custom user session:
using (var service = authService.ResolveService<MyService>())
{
var response = service.Get(new MyRequest { ... });
}
From outside of ServiceStack:
using (var service = HostContext.ResolveService<MyService>())
{
var response = service.Get(new MyRequest { ... });
}
ServiceStack Services are just normal Dependencies
Since Services in ServiceStack are just like any other IOC dependency, the implementation of ResolveService simply resolves the Service from ServiceStack's IOC and injects the current Request, i.e:
public static T ResolveService<T>(HttpContextBase httpCtx=null)
where T : class, IRequiresRequest
{
var service = AssertAppHost().Container.Resolve<T>();
if (service == null) return null;
service.Request = httpCtx != null
? httpCtx.ToRequest()
: HttpContext.Current.ToRequest();
return service;
}
I am trying to post to a Web API method from a client, as follows:
// Create list of messages that will be sent
IEnumerable<IMessageApiEntity> messages = new List<IMessageApiEntity>();
// Add messages to the list here.
// They are all different types that implement the IMessageApiEntity interface.
// Create http client
HttpClient client = new HttpClient {BaseAddress = new Uri(ConfigurationManager.AppSettings["WebApiUrl"])};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Post to web api
HttpResponseMessage response = client.PostAsJsonAsync("Communications/Messages", messages).Result;
// Read results
IEnumerable<ApiResponse<IMessageApiEntity>> results = response.Content.ReadAsAsync<IEnumerable<ApiResponse<IMessageApiEntity>>>().Result;
My Web API controller action looks like this:
public HttpResponseMessage Post([FromBody]IEnumerable<IMessageApiEntity> messages)
{
// Do stuff
}
The problem I am having is that messages is always empty (but not null) when coming into the web API controller action. I have verified in the debugger that the messages object on the client side does have items in it right before being posted.
I suspect it might have something to do with the interface type not being converted to a concrete type when trying to pass the objects, but I don't know how to go about making it work. How can I achieve this?
I figured out how to do it without a custom model binder. Posting the answer in case anyone else has this issue...
Client:
// Create list of messages that will be sent
IEnumerable<IMessageApiEntity> messages = new List<IMessageApiEntity>();
// Add messages to the list here.
// They are all different types that implement the IMessageApiEntity interface.
// Create http client
HttpClient client = new HttpClient {BaseAddress = new Uri(ConfigurationManager.AppSettings["WebApiUrl"])};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Post to web api (this is the part that changed)
JsonMediaTypeFormatter json = new JsonMediaTypeFormatter
{
SerializerSettings =
{
TypeNameHandling = TypeNameHandling.All
}
};
HttpResponseMessage response = client.PostAsync("Communications/Messages", messages, json).Result;
// Read results
IEnumerable<ApiResponse<IMessageApiEntity>> results = response.Content.ReadAsAsync<IEnumerable<ApiResponse<IMessageApiEntity>>>().Result;
Add to Register method in WebApiConfig.cs:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
The key is to send the type as part of the json and turn on automatic type name handling, so that web API can figure out what type it is.
Why you use interface type in method? Looks like web API, has no idea what kind of instance should be used for materialize messages argument. Seems that you have to write custom model binder for this action.
I had a similar problem a few weeks ago with .NET Core WebAPI.
The proposed solution of adding the line below did not work for me:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
I ended up creating a generic object that can carry my IEnumerable, where T is my intended class
[Serializable]
public class GenericListContainer<T> where T : class
{
#region Constructors
public GenericListContainer()
{
}
public GenericListContainer(IEnumerable<T> list)
{
List = list;
}
#endregion
#region Properties
public IEnumerable<T> List { get; set; }
#endregion
}
Then I changed my webapi method to :
[Route("api/system-users/save-collection-async")]
[HttpPost]
[ProducesResponseType(typeof(string), 200)]
public async Task<IActionResult> SaveSystemUserCollectionAsync([FromBody] GenericListContainer<SystemUser> dto)
{
var response = await _systemUserService.SaveSystemUserCollectionAsync(dto.List);
return Ok(response);
}
This method returns the saved user's id (Guid in my case).
Hope this helps someone else!