ServiceStack Performance - c#

Let me start by saying I love the design of ServiceStack as a client. (I've never used it for server side)
I'm writing a C# wrapper for API calls and I keep getting timeout and authentication errors. I've contacted the developers at the other end and they assure me that there are no issues on their end and that I must be doing something wrong. Normally I wouldn't believe them and I'd build a sample project to demonstrate the issue but in this case they pointed me to a web page that will test the same API I'm running in C# and they can re-authenticate as fast as they can click the submit button. I forget the exact site they use for testing but enough of my story... I'm sure I'm doing something wrong I just don't know what.
Here's my Unit Test. If I run it by itself or with one copy it works fine (150-1100ms) but if I make 3 or more copies of it they I will get only 2-3 that pass and the rest will timeout.
[TestMethod]
[Timeout(5000)]
public void Login_Success1()
{
var client = new JsonServiceClient("apiurl");
var response = client.Login("XXXAccessKeyXXX", "XXXSecretKeyXXX");
//Assertions
}
This is my extension method:
public static class Extensions
{
public static (bool Success, string Message, string Token) Login(this JsonServiceClient client, string accessKey, string secretKey)
{
try
{
var response = client.Post(new LoginRequest(accessKey, secretKey));
var authorization = response.Headers.GetValues("Authorization")[0];
return (true, string.Empty, authorization);
}
catch (Exception ex)
{
return (false, $"Authentication failed: {ex.Message}", string.Empty);
}
}
}
And here's the login request:
[Route("/sessions")]
[DataContract]
internal class LoginRequest
{
internal LoginRequest(string accessKey, string secretKey)
{
AccessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey));
SecretKey = secretKey ?? throw new ArgumentNullException(nameof(secretKey));
}
[DataMember(Name = "accessKey")]
internal string AccessKey { get; set; }
[DataMember(Name = "secretKey")]
internal string SecretKey { get; set; }
}
I think this is all the relevant code but if you feel I missed something please lmk.

Your Request DTO's should implement either IReturn<T> or IReturnVoid otherwise if you're sending just an object you will call the deprecated Post() method:
/// <summary>
/// APIs returning HttpWebResponse must be explicitly Disposed, e.g using (var res = client.Post(url)) { ... }
/// </summary>
[Obsolete("Use: using (client.Post<HttpWebResponse>(requestDto) { }")]
public virtual HttpWebResponse Post(object requestDto)
{
return Send<HttpWebResponse>(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto);
}
Which because ServiceStack doesn't know how you want the Response deserialized it will return the open HttpWebResponse so you can inspect the Response yourself (as you're doing in your example). But this needs to be explicitly disposed as .NET's HttpWebRequest only allows a couple of concurrent requests open per domain which will cause your App to hang/timeout as it's waiting for Requests to be disposed to stay within the concurrent limit.
The preferred solution is to always annotate Request DTO's that you send with ServiceStack clients with a IReturn or a IReturn<T> interface marker, if it has none or you want to ignore the Response implement IReturnVoid otherwise implement IReturn<ResponseDtoType>:
class LoginRequest : IReturnVoid {}
Which instead calls the non-deprecated Post() method which disposes of the HttpWebResponse.
Otherwise if you want to send plain object DTO's you need to dispose of the HttpWebResponse after usage, e.g:
using (var response = client.Post<HttpWebResponse>(new LoginRequest(accessKey, secretKey)))
{
var authorization = response.Headers.GetValues("Authorization")[0];
}
API's which implicitly return HttpWebResponse were deprecated to avoid hard to identify issues like this, instead we recommend using the explicit API above which declares the HttpWebResponse return type at the call-site so it's easier to identify it needs to be disposed.
Also note the ServiceStack Service Clients are opinionated for calling ServiceStack Services, for calling other Services we recommend using HTTP Utils instead.

Related

How to gracefully await while object not changed (no work code required!)

I would like to gather your advice on the following problem:
Task
There are two microservices running A and B. At some time microservice_A will create a request message (or array of RequestMessage[]) which is an example "RequestMessage" and send it to microservice_B.
public class RequestMessage
{
public Guid guid;
public string result;
public DateTime expirationDateTime;
public RequestMessage()
{
guid = Guid.NewGuid();
result = "no_result";
}
}
The way of getting a response is built in the way that there is a service class that implements the method, which will be called at some time after the request was sent. The call (of the function ResolveRequestedMessage()) will be performed under the hood and only function implementation lay on the developer.
public class RequestMessageResolver : IRequestMessageResolver
{
public bool TResolveRequestedMessage(Guid Id, string result)
{
// find the Request e.g in Request[] by id, Implementation lay on the developer side and can be various.
RequestCollection.Get(id).result = "resolved";
}
}
Problem
The RequestMessage should be awaitable, however the interface of the RequestMessage is defined and cant be changed.
Microservise_A should not proceed to any further action (within the call scope, however still be valid for some other requests e.g status/error/etc) until it id not get the resolution of the requested message.
My idea and thoughts
First I tried to create a wrapper class that will have TaskCompletionSource and can be set from outside (example 1). This works but required a lot of extra wrappers to achieve the desired results.
Another idea is to modify the wrapper to implement INotifyCompletion instead of having TaskCompletionSource, but not sure if this will bring big overhead and make the solution complex for no reason - did not try yet.
Code Example 1:
public class RequestMessageWrapper
{
public TaskCompletionSource<bool> completionSource;
public RequestMessage requestMessage;
public RequestMessageWrapper(RequestMessage requestMessage)
{
completionSource = new TaskCompletionSource<bool>();
this.requestMessage = requestMessage;
}
public async Task GetResponseAsync()
{
// also need to be cancelled somehow if (DateTime.Now > requestMessage.expirationDateTime)
await completionSource.Task;
}
}

A proxy web service to a web service - where am I missing?

I program ASP.NET Framework MVC and Web API 2
I have to access a REST service for some information. The nature of the security requirements for this service require that I ask from a limited set of known IP addresses. The nature of my client requirements is that there will be an unknown number of them with IPs that are assigned by some DHCP. I think I need to stand up a proxy that will forward requests to the service and return responses to the client that asked. This server can be assigned a single static IP, that I can register with the target service. I don't want to try to duplicate the signatures of the target service and have to maintain my proxy whenever they decide to improve interfaces.
I would have the service that is restricting IPs and accepts a GET for http://S/action as an example. I would have the proxy at http://P/action. The client would send GET http://P/action and P would, in response, send GET http://S/action, collect the response, return it back to the client.
An attempt to implement this strategy, here is a handler I built for P that doesn't work:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
DelegatingHandler handler = new DelegatingHandlerProxy<ProxyHandler>();
config.MessageHandlers.Add(handler);
}
}
DelegatingProxyHandler is a way to get my dependency injection container involved:
public sealed class DelegatingHandlerProxy<THandler> : DelegatingHandler
where THandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
IDependencyScope scope = request.GetDependencyScope();
Task<HttpResponseMessage> task;
if (scope.GetService(typeof(THandler)) is DelegatingHandler handler)
{
if (!ReferenceEquals(handler.InnerHandler, InnerHandler))
{
handler.InnerHandler = InnerHandler;
}
HttpMessageInvoker invoker = new HttpMessageInvoker(handler);
task = invoker.SendAsync(request, cancellationToken);
}
else
{
throw new InvalidOperationException("Handler not registered with DI container");
}
return task;
}
}
The ProxyHandler that I want to do the work is:
public class ProxyHandler: DelegatingHandler
{
public ProxyHandler(
ITransformRequest preProcessor,
ITransformResponse postProcessor,
IForwardRequest forwarder)
{
PreProcessor = preProcessor;
PostProcessor = postProcessor;
Forwarder = forwarder;
}
private ITransformRequest PreProcessor { get; }
private ITransformResponse PostProcessor { get; }
private IForwardRequest Forwarder { get; }
#region Overrides of DelegatingHandler
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (PreProcessor != null)
{
request.RequestUri = PreProcessor.Transform(request.RequestUri);
}
HttpResponseMessage response = await Forwarder.Forward(request, cancellationToken);
HttpResponseMessage transformedResponse = PostProcessor.Transform(response);
return transformedResponse;
}
#endregion
}
In this case, the DI container supplies a PreProcessor that changes host, port, and prefix of the request to the target service. The Forwarder sends the request to the target using HttpClient. The PostProcessor will be a noop.
I didn't build any controllers. My thinking is that if this pipeline behaves as I expect, there won't be any controller that needs invoking. When I send ant request to this, http://P/anything returns a 404, not htto://S/anything. What am I missing?
Any particular reason you're not just writing a set of matching controllers that accept client requests and then execute the equivalent request on the 3rd arty API using a service that implements a simple web client and then returning the responses - perhaps including some authentication & caching logic to lower the impact on their API?
If your 3rd party API provider is limiting requests by IP, that is likely because they trust (or explicitly require) you to manage requests to their API in order to protect it from excessive load and/or security risks. Directly forwarding all client requests without any logic in your middleware means you're negating this limitation.
If the only purpose of your application is to provide a static IP (and you do not need to add any logic in your code) then you should consider using one of the many off the shelf API gateway products - e.g. Kong, which is an open source and very well established with plenty of community support https://konghq.com/kong-community-edition/

How to read request body in an asp.net core webapi controller?

I'm trying to read the request body in the OnActionExecuting method, but I always get null for the body.
var request = context.HttpContext.Request;
var stream = new StreamReader(request.Body);
var body = stream.ReadToEnd();
I have tried to explicitly set the stream position to 0, but that also didn't work. Since this is ASP.NET Core, things are a little different I think. I can see all the samples here referring to old web API versions.
Is there any other way of doing this?
In ASP.Net Core it seems complicated to read several times the body request, however, if your first attempt does it the right way, you should be fine for the next attempts.
I read several turnarounds for example by substituting the body stream, but I think the following is the cleanest:
The most important points being
to let the request know that you will read its body twice or more times,
to not close the body stream, and
to rewind it to its initial position so the internal process does not get lost.
[EDIT]
As pointed out by Murad, you may also take advantage of the .Net Core 2.1 extension: EnableBuffering It stores large requests onto the disk instead of keeping it in memory, avoiding large-streams issues stored in memory (files, images, ...).
You can change the temporary folder by setting the ASPNETCORE_TEMP environment variable, and files are deleted once the request is over.
In an AuthorizationFilter, you can do the following:
// Helper to enable request stream rewinds
using Microsoft.AspNetCore.Http.Internal;
[...]
public class EnableBodyRewind: Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var bodyStr = "";
var req = context.HttpContext.Request;
// Allows using several time the stream in ASP.Net Core
req.EnableRewind();
// Arguments: Stream, Encoding, detect encoding, buffer size
// AND, the most important: keep stream opened
using (StreamReader reader
= new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
{
bodyStr = reader.ReadToEnd();
}
// Rewind, so the core is not lost when it looks at the body for the request
req.Body.Position = 0;
// Do whatever works with bodyStr here
}
}
public class SomeController: Controller
{
[HttpPost("MyRoute")]
[EnableBodyRewind]
public IActionResult SomeAction([FromBody]MyPostModel model )
{
// play the body string again
}
}
Then you can use the body again in the request handler.
In your case, if you get a null result, it probably means that the body has already been read at an earlier stage. In that case, you may need to use a middleware (see below).
However be careful if you handle large streams, that behavior implies that everything is loaded into memory, this should not be triggered in case of a file upload.
You may want to use this as a Middleware
Mine looks like this (again, if you download/upload large files, this should be disabled to avoid memory issues):
public sealed class BodyRewindMiddleware
{
private readonly RequestDelegate _next;
public BodyRewindMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try { context.Request.EnableRewind(); } catch { }
await _next(context);
// context.Request.Body.Dipose() might be added to release memory, not tested
}
}
public static class BodyRewindExtensions
{
public static IApplicationBuilder EnableRequestBodyRewind(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<BodyRewindMiddleware>();
}
}
A clearer solution, works in ASP.Net Core 2.1 / 3.1
Filter class
using Microsoft.AspNetCore.Authorization;
// For ASP.NET 2.1
using Microsoft.AspNetCore.Http.Internal;
// For ASP.NET 3.1
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
public class ReadableBodyStreamAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// For ASP.NET 2.1
// context.HttpContext.Request.EnableRewind();
// For ASP.NET 3.1
// context.HttpContext.Request.EnableBuffering();
}
}
In an Controller
[HttpPost]
[ReadableBodyStream]
public string SomePostMethod()
{
//Note: if you're late and body has already been read, you may need this next line
//Note2: if "Note" is true and Body was read using StreamReader too, then it may be necessary to set "leaveOpen: true" for that stream.
HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
using (StreamReader stream = new StreamReader(HttpContext.Request.Body))
{
string body = stream.ReadToEnd();
// body = "param=somevalue&param2=someothervalue"
}
}
A quick way to add response buffering in .NET Core 3.1 is
app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next();
});
in Startup.cs. I found this also guarantees that buffering will be enabled before the stream has been read, which was a problem for .Net Core 3.1 with some of the other middleware/authorization filter answers I've seen.
Then you can read your request body via HttpContext.Request.Body in your handler as several others have suggested.
Also worth considering is that EnableBuffering has overloads that allow you to limit how much it will buffer in memory before it uses a temporary file, and also an overall limit to you buffer. NB if a request exceeds this limit an exception will be thrown and the request will never reach your handler.
Recently I came across a very elegant solution that take in random JSON that you have no idea the structure:
[HttpPost]
public JsonResult Test([FromBody] JsonElement json)
{
return Json(json);
}
Just that easy.
To be able to rewind the request body, #Jean's answer helped me come up with a solution that seems to work well. I currently use this for Global Exception Handler Middleware but the principle is the same.
I created a middleware that basically enables the rewind on the request body (instead of a decorator).
using Microsoft.AspNetCore.Http.Internal;
[...]
public class EnableRequestRewindMiddleware
{
private readonly RequestDelegate _next;
public EnableRequestRewindMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Request.EnableRewind();
await _next(context);
}
}
public static class EnableRequestRewindExtension
{
public static IApplicationBuilder UseEnableRequestRewind(this IApplicationBuilder builder)
{
return builder.UseMiddleware<EnableRequestRewindMiddleware>();
}
}
This can then be used in your Startup.cs like so:
[...]
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
[...]
app.UseEnableRequestRewind();
[...]
}
Using this approach, I have been able to rewind the request body stream successfully.
This is a bit of an old thread, but since I got here, I figured I'd post my findings so that they might help others.
First, I had the same issue, where I wanted to get the Request.Body and do something with that (logging/auditing). But otherwise I wanted the endpoint to look the same.
So, it seemed like the EnableBuffering() call might do the trick. Then you can do a Seek(0,xxx) on the body and re-read the contents, etc.
However, this led to my next issue. I'd get "Synchronous operations are disallowed" exceptions when accessing the endpoint. So, the workaround there is to set the property AllowSynchronousIO = true, in the options. There are a number of ways to do accomplish this (but not important to detail here..)
THEN, the next issue is that when I go to read the Request.Body it has already been disposed. Ugh. So, what gives?
I am using the Newtonsoft.JSON as my [FromBody] parser in the endpoint call. That is what is responsible for the synchronous reads and it also closes the stream when it's done. Solution? Read the stream before it get's to the JSON parsing? Sure, that works and I ended up with this:
/// <summary>
/// quick and dirty middleware that enables buffering the request body
/// </summary>
/// <remarks>
/// this allows us to re-read the request body's inputstream so that we can capture the original request as is
/// </remarks>
public class ReadRequestBodyIntoItemsAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context == null) return;
// NEW! enable sync IO because the JSON reader apparently doesn't use async and it throws an exception otherwise
var syncIOFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
var req = context.HttpContext.Request;
req.EnableBuffering();
// read the body here as a workarond for the JSON parser disposing the stream
if (req.Body.CanSeek)
{
req.Body.Seek(0, SeekOrigin.Begin);
// if body (stream) can seek, we can read the body to a string for logging purposes
using (var reader = new StreamReader(
req.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 8192,
leaveOpen: true))
{
var jsonString = reader.ReadToEnd();
// store into the HTTP context Items["request_body"]
context.HttpContext.Items.Add("request_body", jsonString);
}
// go back to beginning so json reader get's the whole thing
req.Body.Seek(0, SeekOrigin.Begin);
}
}
}
}
So now, I can access the body using the HttpContext.Items["request_body"] in the endpoints that have the [ReadRequestBodyIntoItems] attribute.
But man, this seems like way too many hoops to jump through. So here's where I ended, and I'm really happy with it.
My endpoint started as something like:
[HttpPost("")]
[ReadRequestBodyIntoItems]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData([FromBody] MyJsonObjectType value)
{
var bodyString = HttpContext.Items["request_body"];
// use the body, process the stuff...
}
But it is much more straightforward to just change the signature, like so:
[HttpPost("")]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData()
{
using (var reader = new StreamReader(
Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false
))
{
var bodyString = await reader.ReadToEndAsync();
var value = JsonConvert.DeserializeObject<MyJsonObjectType>(bodyString);
// use the body, process the stuff...
}
}
I really liked this because it only reads the body stream once, and I have have control of the deserialization. Sure, it's nice if ASP.NET core does this magic for me, but here I don't waste time reading the stream twice (perhaps buffering each time), and the code is quite clear and clean.
If you need this functionality on lots of endpoints, perhaps the middleware approaches might be cleaner, or you can at least encapsulate the body extraction into an extension function to make the code more concise.
Anyways, I did not find any source that touched on all 3 aspects of this issue, hence this post. Hopefully this helps someone!
BTW: This was using ASP .NET Core 3.1.
for read of Body , you can to read asynchronously.
use the async method like follow:
public async Task<IActionResult> GetBody()
{
string body="";
using (StreamReader stream = new StreamReader(Request.Body))
{
body = await stream.ReadToEndAsync();
}
return Json(body);
}
Test with postman:
It's working well and tested in Asp.net core version 2.0 , 2.1 , 2.2, 3.0.
I hope is useful.
Writing an extension method is the most efficient way in my opinion
public static string PeekBody(this HttpRequest request)
{
try
{
request.EnableBuffering();
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
request.Body.Read(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
finally
{
request.Body.Position = 0;
}
}
You can use Request.Body.Peeker Nuget Package as well (source code)
//Return string
var request = HttpContext.Request.PeekBody();
//Return in expected type
LoginRequest request = HttpContext.Request.PeekBody<LoginRequest>();
//Return in expected type asynchronously
LoginRequest request = await HttpContext.Request.PeekBodyAsync<LoginRequest>();
I had a similar issue when using ASP.NET Core 2.1:
I need a custom middleware to read the POSTed data and perform some security checks against it
using an authorization filter is not practical, due to large number of actions that are affected
I have to allow objects binding in the actions ([FromBody] someObject). Thanks to SaoBiz for pointing out this solution.
So, the obvious solution is to allow the request to be rewindable, but make sure that after reading the body, the binding still works.
EnableRequestRewindMiddleware
public class EnableRequestRewindMiddleware
{
private readonly RequestDelegate _next;
///<inheritdoc/>
public EnableRequestRewindMiddleware(RequestDelegate next)
{
_next = next;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
context.Request.EnableBuffering(); // this used to be EnableRewind
await _next(context);
}
}
Startup.cs
(place this at the beginning of Configure method)
app.UseMiddleware<EnableRequestRewindMiddleware>();
Some other middleware
This is part of the middleware that requires unpacking of the POSTed information for checking stuff.
using (var stream = new MemoryStream())
{
// make sure that body is read from the beginning
context.Request.Body.Seek(0, SeekOrigin.Begin);
context.Request.Body.CopyTo(stream);
string requestBody = Encoding.UTF8.GetString(stream.ToArray());
// this is required, otherwise model binding will return null
context.Request.Body.Seek(0, SeekOrigin.Begin);
}
I was able to read request body in an asp.net core 3.1 application like this (together with a simple middleware that enables buffering -enable rewinding seems to be working for earlier .Net Core versions-) :
var reader = await Request.BodyReader.ReadAsync();
Request.Body.Position = 0;
var buffer = reader.Buffer;
var body = Encoding.UTF8.GetString(buffer.FirstSpan);
Request.Body.Position = 0;
The IHttpContextAccessor method does work if you wish to go this route.
TLDR;
Inject the IHttpContextAccessor
Rewind -- HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
Read --
System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body);
JObject asObj = JObject.Parse(sr.ReadToEnd());
More -- An attempt at a concise, non-compiling, example of the items you'll need to ensure are in place in order to get at a useable IHttpContextAccessor.
Answers have pointed out correctly that you'll need to seek back to the start when you try to read the request body. The CanSeek, Position properties on the request body stream helpful for verifying this.
.NET Core DI Docs
// First -- Make the accessor DI available
//
// Add an IHttpContextAccessor to your ConfigureServices method, found by default
// in your Startup.cs file:
// Extraneous junk removed for some brevity:
public void ConfigureServices(IServiceCollection services)
{
// Typical items found in ConfigureServices:
services.AddMvc(config => { config.Filters.Add(typeof(ExceptionFilterAttribute)); });
// ...
// Add or ensure that an IHttpContextAccessor is available within your Dependency Injection container
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
// Second -- Inject the accessor
//
// Elsewhere in the constructor of a class in which you want
// to access the incoming Http request, typically
// in a controller class of yours:
public class MyResourceController : Controller
{
public ILogger<PricesController> Logger { get; }
public IHttpContextAccessor HttpContextAccessor { get; }
public CommandController(
ILogger<CommandController> logger,
IHttpContextAccessor httpContextAccessor)
{
Logger = logger;
HttpContextAccessor = httpContextAccessor;
}
// ...
// Lastly -- a typical use
[Route("command/resource-a/{id}")]
[HttpPut]
public ObjectResult PutUpdate([FromRoute] string id, [FromBody] ModelObject requestModel)
{
if (HttpContextAccessor.HttpContext.Request.Body.CanSeek)
{
HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body);
JObject asObj = JObject.Parse(sr.ReadToEnd());
var keyVal = asObj.ContainsKey("key-a");
}
}
}
I Know this my be late but in my case its Just I had a problem in routing as bellow
At startup.cs file I was beginning the routing with /api
app.MapWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")),
a =>
{
//if (environment.IsDevelopment())
//{
// a.UseDeveloperExceptionPage();
//}
a.Use(async (context, next) =>
{
// API Call
context.Request.EnableBuffering();
await next();
});
//and I was putting in controller
[HttpPost]
[Route("/Register", Name = "Register")]
//Just Changed the rout to start with /api like my startup.cs file
[HttpPost]
[Route("/api/Register", Name = "Register")]
/and now the params are not null and I can ready the body request multiple
I also wanted to read the Request.Body without automatically map it to some action parameter model. Tested a lot of different ways before solved this. And I didnĀ“t find any working solution described here. This solution is currently based on the .NET Core 3.0 framework.
reader.readToEnd() seamed like a simple way, even though it compiled, it throwed an runtime exception required me to use async call. So instead I used ReadToEndAsync(), however it worked sometimes, and sometimes not. Giving me errors like, cannot read after stream is closed. The problem is that we cannot guarantee that it will return the result in the same thread (even if we use the await). So we need some kind of callback. This solution worked for me.
[Route("[controller]/[action]")]
public class MyController : ControllerBase
{
// ...
[HttpPost]
public async void TheAction()
{
try
{
HttpContext.Request.EnableBuffering();
Request.Body.Position = 0;
using (StreamReader stream = new StreamReader(HttpContext.Request.Body))
{
var task = stream
.ReadToEndAsync()
.ContinueWith(t => {
var res = t.Result;
// TODO: Handle the post result!
});
// await processing of the result
task.Wait();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to handle post!");
}
}
The simplest possible way to do this is the following:
In the Controller method you need to extract the body from, add this parameter:
[FromBody] SomeClass value
Declare the "SomeClass" as:
class SomeClass {
public string SomeParameter { get; set; }
}
When the raw body is sent as json, .net core knows how to read it very easily.
To those who simply want to get the content (request body) from the request:
Use the [FromBody] attribute in your controller method parameter.
[Route("api/mytest")]
[ApiController]
public class MyTestController : Controller
{
[HttpPost]
[Route("content")]
public async Task<string> ReceiveContent([FromBody] string content)
{
// Do work with content
}
}
As doc says: this attribute specifies that a parameter or property should be bound using the request body.
Here's a solution for POSTed JSON body that doesn't require any middleware or extensions, all you need is to override OnActionExecuting to have access to all of the data set in the body or even the arguments in the URL:
using System.Text.Json;
....
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// You can simply use filterContext.ActionArguments to get whatever param that you have set in the action
// For instance you can get the "json" param like this: filterContext.ActionArguments["json"]
// Or better yet just loop through the arguments and find the type
foreach(var elem in filterContext.ActionArguments)
{
if(elem.Value is JsonElement)
{
// Convert json obj to string
var json = ((JsonElement)elem.Value).GetRawText();
break;
}
}
}
[HttpPost]
public IActionResult Add([FromBody] JsonElement json, string id = 1)
{
return Ok("v1");
}
I run into the same problem under .NET5.0, none of the solutions above worked.
It turned out that the issue was the return value of the Post method. It must be Task and not void.
Bad code:
[HttpPost]
public async void Post() {...}
Good code:
[HttpPost]
public async Task Post() {...}

Consuming WebAPI JSON

I'm trying to build some kind of RESTful-like API, I'm aware of that my first draft probably isn't anything near the real RESTful design pattern. However my real question is how should I consume my service using JSON?
In my so called real world example I want my users to sign in via the service so I have this AuthenticationController
namespace RESTfulService.Controllers
{
public class AuthenticationController : ApiController
{
public string Get(string username, string password)
{
// return JSON-object or JSON-status message
return "";
}
public string Get()
{
return "";
}
}
}
Considering the increasing popularity with the technology I assumed that very little code would be needed for consuming the service. Do I really need to serialize the JSON manually with some kind of third party package like json.net? Beneath is my draft for the client
private static bool DoAuthentication(string username, string password)
{
var client = InitializeHttpClient();
HttpResponseMessage response = client.GetAsync("/api/rest/authentication").Result;
if (response.IsSuccessStatusCode)
{
//retrieve JSON-object or JSON-status message
}
else
{
// Error
}
return true;
}
private static HttpClient InitializeHttpClient()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
How do I send JSON from service and how do I interpreting it on the client?
Have a look at the System.Net.Http.HttpContentExtensions in System.Net.Http.Formatting.dll. As explained here (and suggested by Mike Wasson in a comment above), you can call ReadAsAsync<T>() on the response content to deserialize from JSON (or XML) to a CLR type:
if (response.IsSuccessStatusCode)
{
var myObject = response.Content.ReadAsAsync<MyObject>();
}
If you need to customize the deserialization, that article links to a further explanation of MediaTypeFormatters.

Grouping same WCF Requests

If a WCF Service gets the same request, means the same MD5 over all Parameters i want to block all except the first request until the processing is done and notify all waiting clients.
What is the best way doing this? I thaugh of something like a channel sink, maybe there is a finnished implementation for archieving this?
I'm not sure about what would be the 'best' fit for the WCF architecture but you should consider setting your InstanceContextMode to Single as you're likely to be doing a lot of synchronization steps for what you want to do here.
How about something like this? You will obviously need to do some synchronization on the dictionary itself, but at least it's a start.
private IDictionary<string, RequestToken> RequestTokens =
new Dictionary<string, RequestToken>();
public MyResponse MyMethod(MyRequest request)
{
// get the MD5 for the request
var md5 = GetMD5Hash(request);
// check if another thread is processing/has processed an identical request
RequestToken token;
if (RequestTokens.TryGetValue(md5, out token))
{
// if the token exists already then wait till we can acquire the lock
// which indicates the processing has finished and a response is ready
// for us to reuse
lock (token.Sync)
{
return token.Response;
}
}
else
{
var token = new Token(md5);
lock (token.Sync)
{
RequestTokens.Add(md5, token);
// do processing here..
var response = ....
token.Response = response;
return response;
}
}
}
private class RequestToken
{
private readonly object _sync = new object();
public RequestToken(string md5)
{
MD5 = md5;
}
public string MD5 { get; private set; }
public object Sync { get { return _sync; } }
public MyResponse Response { get; set; }
}
To me, this is something I'd want to abstract away from my business logic, and I'll personally use PostSharp and write a little attribute to handle all this.
I've written a Memoizer attribute which does something similar in the lines of caching responses based on request but without the synchronization steps, so you could probably take a look at what I've done and modify it accordingly to achieve what you're after.
At first, I am thinking of a custom implementation for a hashtable to keep the MD%'s being processed and lookup for the same before starting a new request processing.

Categories