Can we apply custom WebContentTypeMapper only to a particular WCF call? - asp.net

I have a WCF service that accepts only requests of Content-Type=application/json. I want my service to accept text/plain request only for a particular OperationContract. I can use a custom WebContentTypeMapper to make the service to treat text/plain requests as application/json but I don't know how to scope it to only a particular OperationContract.
Sample stub -
public class MyService
{
[OperationContract]
public string Foo(string arg1, string arg2)
{
//Do something
}
[OperationContract]
public string Bar(string arg1, string arg2)
{
//Do something else
}
}
public class JsonContentTypeMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
if( contentType.ToLowerInvariant() == "text/plain;charset=utf-8" )
{
// The check below fails because for each request this function is called twice.
// The HttpContext is set the first time and its empty the second time.
if( HttpContext.Current.Request.Path.EndsWith("Foo") )
{
return WebContentFormat.Json;
}
return WebContentFormat.Default;
}
}
}
Context: During app closing I'm sending a sendBeacon request to my service to do cleanup. sendBeacon requests cannot have content-type=application/json. I can't use sync-xhr because it is being deprecated in Chrome and are a blocking call and hang navigation to next page. I can't use async-xhr because this request will be called in unload event during which the browsers ignore async-xhr requests.

Related

redirect one POST query to another with the same params .NET Core Web API

I have .NET Core Web API app. And I have 3 API endpoints:
"/a", "/b", "/c" that accept the same parameters (Form parameters with files).
So, user do request to "/a", I check some conditions and need to redirect him to "/b" or "/c" with the same (Form data with files) parameters.
The important thing: user can also execute "/b" or "/c" separately without request to "/a". Also I need to redirect user to "/b" or "/c" inside "/a", so no response I wait for from "/a" or, in other words, response from "/a" = redirection to other endpoint.
Is there way to do it?
You can't redirect to a different Web API endpoint, but you can do one of those:
User requests "/a", and you give him a response containing the URL/URLs to "/b" or "/c".
You have implemented a service and your controllers are logically clean. So in the controller, you only do the check which service method you should call "/a", "/b" or "/c".
Edit3:
I highly recommend option 2, because you've mentioned that you have 3 controller actions that have the same input data. So your best way to do it is to have only one action and inside it call your service methods A, B or C depending on the input data.
Edit2:
First option pseudo-code:
[HttpPost("/a")]
public ActionResult<DefaultResponseModel> Verify(MyInputModel myModel)
{
MyResponse response = new MyResponse();
if(myModel.MyProperty1 == "something")
{
response.RedirectURL = "https://myurl/a"
}
else if(myModel.MyProperty1 == "other")
{
response.RedirectURL = "https://myurl/b"
}
else
{
response.RedirectURL = "https://myurl/c"
}
return Ok(respomse);
}
Edit:
Example of how to do the second option.
In your Startup.cs file in the ConfigureServices method you register a service like this:
services.AddTransient<IMyService, MyService>();
In your controller you inject it via dependency injection:
public class BusinessController : ControllerBase
{
private readonly IMyService businessService;
public BusinessController(IMyService businessService)
{
this.businessService = businessService;
}
// the actions are below..
// code here
}
And then let's say in your desired action called "/a"
[HttpPost("/a")]
public ActionResult<DefaultResponseModel> Verify(MyInputModel myModel)
{
if(myModel.MyProperty1 == "something")
{
var response = this.businessService.A(myModel)
}
else if(myModel.MyProperty1 == "other")
{
var response = this.businessService.B(myModel)
}
else
{
var response = this.businessService.C(myModel)
}
}
And don't forget to create the service and the implementation.
public interface IMyService
{
ResponseModel A(MyInputModel myModel);
ResponseModel B(MyInputModel myModel);
ResponseModel C(MyInputModel myModel);
}
And the implementations of the service:
public class MyService : IMyService
{
enter code here
}

Scope in Middleware and Blazor Component

I'm working on a server-side Blazor application and ran into some problems regarding a scoped service.
For simplicity's sake I have re-created my issue using the default Blazor template (the one with the counter).
I have a service "CounterService", which initializes a counter to 1 and exposes this counter together with a method to increment it. Really basic:
public class CounterService
{
public int Counter { get; private set; }
public CounterService()
{
Counter = 1;
}
public void IncrementCounter()
{
Counter++;
}
}
I have then registered this counter in my Startup.cs as a scoped service: `services.AddScoped()
Then I have a custom ASP.NET middleware, in this case a "CounterInitializerMiddleware".
public class CounterInitializerMiddleware
{
public CounterInitializerMiddleware(RequestDelegate next)
{
_next = next;
}
public RequestDelegate _next { get; }
public async Task Invoke(HttpContext context, CounterService counterService)
{
Console.WriteLine($"CounterInitializer invoked from request path: {context.Request.Path.Value}");
counterService.IncrementCounter();
counterService.IncrementCounter();
await _next(context);
}
}
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseCounterInitializer(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<CounterInitializerMiddleware>();
}
}
Basically, its a middle layer to increment the counter so that it starts at 3 rather than 1 when I get the service injected to my component(s). I register it in my Configure-method in Startup.cs: `app.UseCounterInitializer();
This middleware-layer is invoked 4 times when I start up my application (note that it has RenderMode set to ServerPreRendered): At the page-load request and at the _blazor-requests:
CounterInitializer invoked from request path: /counter
CounterInitializer invoked from request path: /_blazor/disconnect
CounterInitializer invoked from request path: /_blazor/negotiate
CounterInitializer invoked from request path: /_blazor
The scoped service is injected, and all seems good.
Then, if I have a component with the CounterService injected, it seems the scopes get messed up.
If I look at the OnInitialized-method, this is called twice. Once during the pre-render and once during normal render.
At the pre-render execution, the CounterService has Counter set to 3 as expected, since it has been through the CounterInitializerMiddleware. However, during render execution, the CounterService is spawned fresh. So it seems the scope of the normal render and the scope(s) of the requests going through the middleware are different. I thought the scope of the components would be bound to the "_blazor"-signalR connection which is processed my the middleware.
Anyone who can figure out what is going on and help me understand how to accomplish what I'm trying to do?
Best,
Mathias
EDIT: Just to clarify. My real use-case is something entirely different, and the Counter-example is just a simplified case showcasing the issue and is more easily reproducible (I hope).
I've hit the same problem and needed a quick workaround.
The workaround is to get the service from the HttpContext, which is an anti-pattern but better than nothing.
class YourClass
{
private readonly SomeMiddlewareScopedService _service;
public YourClass(SomeMiddlewareScopedServiceservice)
{
_service = service;
}
}
The workaround:
class YourClass
{
private readonly SomeMiddlewareScopedService _service;
public YourClass(IHttpContextAccessor contextAccessor)
{
_service= (SomeMiddlewareScopedService)contextAccessor.HttpContext.RequestServices.GetService(typeof(SomeMiddlewareScopedService));
}
}
Don't forget to add to your builder:
builder.Services.AddHttpContextAccessor();

same HttpPut request code format not working but the works on another request

I have a HTTPPUT request that is not being called. I have a similar put request that manages another tab and it works. Both pages are pretty identical. I don't know what I am doing wrong.
I have tried almost everything and don't know what else to try.
controller:
[HttpPut]
[Route("updateAllocations({type})")]
public IHttpActionResult UpdateAllocations(string type, T_LOC entity)
{
System.Diagnostics.Debug.WriteLine("inside");
_allocationsService.UpdateAllocations(type,entity);
return Ok();
}
interface:
using OTPS.Core.Objects;
using System.Collections.Generic;
using OTPS.Core.Models;
namespace OTPS.Core.Interfaces
{
public interface IAllocationsService
{
void UpdateAllocations(string type, T_LOC entity);
}
}
service:
public void UpdateAllocations(string type, T_LOC entity)
{
System.Diagnostics.Debug.WriteLine("inside");
}
CLIENT SIDE:
public updateAllocation(type: string , entity) {
console.log("sdfsdf")
console.log(`${this.baseUrl}/api/allocations/updateAllocations(${type})`)
return this.http.put(`${this.baseUrl}/api/allocations/updateAllocations({type})`, entity, { headers: this.headers, withCredentials: true })
.pipe(catchError((error: Error) => {
console.log("sdfasd111111sdf")
return this.errorService.handleError(error);
}));
}
I am expecting the clinet side to call the put request before making any further logic but the print on server side never gets called..
Make sure that you subscribe to the service method inside component:
this.myService.updateAllocation(type, entity).subscribe( response => {
// do something here with response
});
You must call subscribe() or nothing happens. Just calling
Service method does not initiate the PUT/DELETE/POST/GET request.
Always subscribe!
An HttpClient method does not begin its HTTP request until you call
subscribe() on the observable returned by that method. This is true
for all HttpClient methods.

TelemetryProcessor - Multiple instances overwrite Custom Properties

I have a very basic http-POST triggered api which creates a TelemetryClient. I needed to provide a custom property in this telemetry for each individual request, so I implemented a TelemtryProcessor.
However, when subsequent POST requests are handled and a new TelemetryClient is created that seems to interfere with the first request. I end up seeing maybe a dozen or so entries in App Insights containing the first customPropertyId, and close to 500 for the second, when in reality the number should be split evenly. It seems as though the creation of the 2nd TelemetryClient somehow interferes with the first.
Basic code is below, if anyone has any insight (no pun intended) as to why this might occur, I would greatly appreciate it.
ApiController which handles the POST request:
public class TestApiController : ApiController
{
public HttpResponseMessage Post([FromBody]RequestInput request)
{
try
{
Task.Run(() => ProcessRequest(request));
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, Constants.GenericErrorMessage);
}
}
private async void ProcessRequest(RequestInput request)
{
string customPropertyId = request.customPropertyId;
//trace handler creates the TelemetryClient for custom property
CustomTelemetryProcessor handler = new CustomTelemetryProcessor(customPropertyId);
//etc.....
}
}
CustomTelemetryProcessor which creates the TelemetryClient:
public class CustomTelemetryProcessor
{
private readonly string _customPropertyId;
private readonly TelemetryClient _telemetryClient;
public CustomTelemetryProcessor(string customPropertyId)
{
_customPropertyId = customPropertyId;
var builder = TelemetryConfiguration.Active.TelemetryProcessorChainBuilder;
builder.Use((next) => new TelemetryProcessor(next, _customPropertyId));
builder.Build();
_telemetryClient = new TelemetryClient();
}
}
TelemetryProcessor:
public class TelemetryProcessor : ITelemetryProcessor
{
private string CustomPropertyId { get; }
private ITelemetryProcessor Next { get; set; }
// Link processors to each other in a chain.
public TelemetryProcessor(ITelemetryProcessor next, string customPropertyId)
{
CustomPropertyId = customPropertyId;
Next = next;
}
public void Process(ITelemetry item)
{
if (!item.Context.Properties.ContainsKey("CustomPropertyId"))
{
item.Context.Properties.Add("CustomPropertyId", CustomPropertyId);
}
else
{
item.Context.Properties["CustomPropertyId"] = CustomPropertyId;
}
Next.Process(item);
}
}
It's better to avoid creating Telemetry Client per each request, isntead re-use single static Telemetry Client instance. Telemetry Processors and/or Telemetry Initializers should also typically be registered only once for the telemetry pipeline and not for every request. TelemetryConfiguration.Active is static and by adding new Processor with each request the queue of processor only grows.
The appropriate setup would be to add Telemetry Initializer (Telemetry Processors are typically used for filtering and Initializers for data enrichment) once into the telemetry pipeline, e.g. though adding an entry to ApplicationInsights.config file (if present) or via code on TelemetryConfiguration.Active somewhere in global.asax, e.g. Application_Start:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new MyTelemetryInitializer());
Initializers are executed in the same context/thread where Track..(..) was called / telemetry was created, so they will have access to the thread local storage and or local objects to read parameters/values from.

gRPC services's Context CancellationListener is not fired when client cancels a service call

I have a streaming service that indefinitely streams from the server to a client until the client cancels.
On the server side, I have a thread that populates an ehcache with data sourced from a database.
Ehcache provides callbacks on cache events, i.e, when an item is added, when an item is removed, etc. I only care about notifying clients when an element is put into the cache, so when a client connects to my gRPC service, I register a notifyElementPut() callback with the cache, that has a reference to the connected clients StreamObserver:
public class GrpcAwareCacheEventListener extends CacheEventListenerAdapter {
private StreamObserver<FooUpdateResponse> responseObserver;
public GrpcAwareCacheEventListener(
StreamObserver<FooUpdateResponse> responseObserver) {
this.responseObserver = responseObserver;
}
#Override
public void notifyElementPut(Ehcache cache, Element element) throws CacheException {
Foo foo = (Foo) element.getObjectValue();
if (foo != null) {
responseObserver.onNext(
FooResponse.newBuilder().setFoo(foo).build());
}
}
}
My streaming foo service is as follows:
public void streamFooUpdates(Empty request,
StreamObserver<FooResponse> responseObserver) {
final CacheEventListener eventListener = new GrpcAwareCacheEventListener(responseObserver);
fooCache.getCacheEventNotificationService().registerListener(eventListener);
Context.current().withCancellation().addListener(new CancellationListener() {
public void cancelled(Context context) {
log.info("inside context cancelled callback");
fooCache.getCacheEventNotificationService().unregisterListener(eventListener);
}
}, ForkJoinPool.commonPool());
}
This all works fine, the client is notified of all foo updates as long as he is connected.
However, after the client disconnects or explicitly cancels the call, I expect that the server's Context's cancellation listener would fire, unregistering the callback with the cache.
This is not the case, regardless of whether the client shutdowns the channel, or explicitly cancels the call. (I expect the server side cancelled context to fire for both of these events). I'm wondering if my cancel semantics on the client side are incorrect, here is the my client code, taken from a test case:
Channel channel = ManagedChannelBuilder.forAddress("localhost", 25001)
.usePlaintext().build();
FooServiceGrpc.FooService stub = FooServiceGrpc
.newStub(channel);
ClientCallStreamObserver<FooResponse> cancellableObserver = new ClientCallStreamObserver<FooResponse>(){
public void onNext(FooResponse response) {
log.info("received foo: {}", response.getFoo());
}
public void onError(Throwable throwable) {
}
public void onCompleted() {
}
public boolean isReady() {
return false;
}
public void setOnReadyHandler(Runnable runnable) {
}
public void disableAutoInboundFlowControl() {
}
public void request(int i) {
}
public void setMessageCompression(boolean b) {
}
public void cancel(#Nullable String s, #Nullable Throwable throwable) {
}
};
stub.streamFooUpdates(Empty.newBuilder().build(), cancellableObserver);
Thread.sleep(10000); // sleep 10 seconds while messages are received.
cancellableObserver.cancel("cancelling from test", null); //explicit cancel
((ManagedChannel) chan).shutdown().awaitTermination(5, TimeUnit.SECONDS); //shutdown as well, for good measure.
Thread.sleep(7000); //channel should be shutdown by now.
}
I'm wondering why the server is not firing the "Context cancelled" callback.
Thanks!
You are not cancelling the client call correctly. The StreamObserver on the second argument of stub.streamFooUpdates() is your callback. You shouldn't call anything on that StreamObserver.
There are two ways to cancel the call from the client-side.
Option 1: Pass a ClientResponseObserver as the second argument, implement beforeStart(), which gives you a ClientCallStreamObserver, on which you can call cancel().
Option 2: Run stub.streamFooUpdates() inside a CancellableContext, and cancel the Context to cancel the call. Note that a CancellableContext must be always be cancelled, that's what the finally block is for.
CancellableContext withCancellation = Context.current().withCancellation();
try {
withCancellation.run(() -> {
stub.streamFooUpdates(...);
Thread.sleep(10000);
withCancellation.cancel(null);
});
} finally {
withCancellation.cancel(null);
}

Resources