Json data type response of IP rate limit API - asp.net

I am using middleware provided by AspNetCoreRateLimit to rate limit incoming requests to an ASP.NET Core 2.x REST API web application.
Currently this library returns html responses for rejected requests. How can I make it return json responses instead?

You could custom your response in the IpRateLimitMiddleware.
IpRateLimitMiddleware
public class MyIpRateLimitMiddleware : IpRateLimitMiddleware
{
public MyIpRateLimitMiddleware(RequestDelegate next
, IOptions<IpRateLimitOptions> options
, IRateLimitCounterStore counterStore
, IIpPolicyStore policyStore
, IRateLimitConfiguration config
, ILogger<IpRateLimitMiddleware> logger)
: base(next, options, counterStore, policyStore, config, logger)
{
}
public override Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitRule rule, string retryAfter)
{
//return base.ReturnQuotaExceededResponse(httpContext, rule, retryAfter);
var message = new { rule.Limit, rule.Period, retryAfter };
httpContext.Response.Headers["Retry-After"] = retryAfter;
httpContext.Response.StatusCode = 200;
httpContext.Response.ContentType = "application/json";
return httpContext.Response.WriteAsync(JsonConvert.SerializeObject(message));
}
}
Configure the middlware in Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//app.UseIpRateLimiting();
app.UseMiddleware<MyIpRateLimitMiddleware>();
//your rest middlware
}

Read the docs
If the request gets blocked then the client receives a text response like this:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
You can customize the response by changing these options HttpStatusCode and QuotaExceededMessage, if you want to implement your own response you can override the IpRateLimitMiddleware.ReturnQuotaExceededResponse. The Retry-After header value is expressed in seconds. (emphasis mine)

Related

Modify request/observable between retry

I have rxjava(observable) + retrofit2 together to make http requests to my application. I create OkHttpClient once for app and don't want to recreate it.
I have retry logic implemented on observable level - using filter, retryWhen together.
What I want - if request finished with error from server side, i want to retry it and send additional header with it.
So, I dont understand neither how can I modify observable inside retryWhen nor how to get the knowledge about observable from interceptor level.
Any ideas and/or knowledge about possible approaches?
You need to create your own Interceptor implementation where you can setup the header logic. Something like
public class FallbackInterceptor implements Interceptor {
static String header1Key = "key1";
static String extraHeaderKey = "key2";
String header1, extraHeader;
boolean useextraheader = false;
public FallbackInterceptor(string header1, string extraheader) {
this.header1 = header1;
this.extraheader = extraheader;
}
public void setUseExtraHeader(boolean useextraheader) {
this.userextraheader = useextraheader;
}
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Add request headers
Request.Builder requestBuilder = original.newBuilder().header(header1Key, header1);
if (useExtraHeader) {
requestBuilder = requestBuilder.header(extraHeaderKey, extraHeader)
}
Request newRequest = requestBuilder.method(original.method(), original.body())
.build();
// Return the response
return chain.proceed(request);
}
}
Add this to an okhttpclient and have your retrofit instance use this this. You can then manipulate the extraheader flag in your retry logic.

How to pass header in Azure endpoint..?

I am using Azure API , URL getting below error please help on this issue. please share codesnip, how to change in web.config and endpoints.
The HTTP request is unauthorized with client authentication scheme
'Anonymous'. The authentication header received from the server was
'AzureApiManagementKey
realm="https:/azure.azure-api.net/MethodName",name="Ocp-Apim-Subscription-Key",type="header"'.
I know this is a very old question still, my answer would help someone faces the same issue.
The solution is to create a custom endpoint behavior where you add a custom message handler to the binding parameters.
In the custom message handler, please add your request headers. After this, use any of the binding technique (like basichttpsbinding or NetHttpsBinding) with security mode as "Transport" and MessageEncoding as "Text" for creating soap client object. Add custom endpoint behavior to the soap client.
public class CustomEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(x =>
{
return new CustomMessageHandler(x);
}));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
public class CustomMessageHandler : DelegatingHandler
{
public CustomMessageHandler(HttpClientHandler handler)
{
InnerHandler = handler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Add("xxxx", "abcde");
return base.SendAsync(request, cancellationToken);
}
}
The console app to consume the service.
static async Task Main(string[] args)
{
var client = GetSOAPClient();
try
{
var result = await client.MyOperation().ConfigureAwait(false);
if(result.Body != null && result.Body.status == "Success")
{
Console.WriteLine(result.Body.myValue);
}
}
catch (Exception ex)
{
Console.WriteLine(ex?.Message);
}
Console.ReadKey();
}
static MyServiceClient GetSOAPClient()
{
NetHttpsBinding binding = new NetHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.MessageEncoding = NetHttpMessageEncoding.Text;
EndpointAddress ea = new EndpointAddress(new Uri("https://myazureurl"));
var client = new MyServiceClient(binding, ea);
client.Endpoint.EndpointBehaviors.Add(new CustomEndpointBehavior());
return client;
}
}
This is complaining that your Subscription key is wrong. If you check the response body, it will give you a readable message of what the real problem is. Double check you are entering the correct subscription key for your Azure API access.
You get your subscription key from the Developer Portal under your profile menu. You can see an example of the subscription key being used in this article under the section "Call an operation from the developer portal": https://learn.microsoft.com/en-us/azure/api-management/api-management-get-started
Also, the 'The HTTP request is unauthorized with client authentication scheme 'Anonymous'.' part of the message is a red herring and a separate problem with how responses work.

Zuul reverse proxy preserve URL

I have a microservce architecture with several services build using JHipster.
Inside one service i have implemented a zuul route filter.
public class TestZuulFilter extends ZuulFilter {
#Override
public String filterType() {
return "route";
}
#Override
public int filterOrder() {
return 5;
}
#Override
public boolean shouldFilter() {
String requestUri = RequestContext.getCurrentContext().getRequest().getRequestURI();
return "/serviceid/reverseproxy".equals(requestUri);
}
#Override
public Object run() {
// get url from id
String id = ctx.getRequest().getParameter("id");
Strign url = URLService.getURLFromId(id);
try
{
RequestContext ctx = RequestContext.getCurrentContext();
// redirect
ctx.setRouteHost(new URL(url));
} catch(MalformedURLException ex) {}
return null;
}
}
When a client call my service http://myservice/serviceid/reverseproxy?id=2 zuul redirects (http 302 status) the user to the url with id 2, in this case google.com.
How can i preserve the original request URL from the client ?
The url must remain http://myservice/serviceid/reverseproxy?url=2 instead of http://www.google.com
Thanks in advance.
It seems you misunderstood the concepts of redirection and proxification.
HTTP redirection means URL change because all the work is done by the client who ends up by making 2 request calls (one to your proxy and one to external service).
Here what you want is to proxify the original request to an external service (in your example google), it means that your filter should be a client of your external service. This way your original client makes only on request call and has no idea that it is talking to your external service.

Limit the simultaneous requests served by the ASP.NET Web API

I'm using ASP.NET Web API 2.2 along with Owin to build a web service and I observed each call to the controller will be served by a separate thread running on the server side, that's nothing surprising and is the behavior I expected.
One issue I'm having now is that because the server side actions are very memory intense so if more than X number of users are calling in at the same time there is a good chance the server code will throw an out-of-memory exception.
Is it possible to set a global "maximum action count" so that Web Api can queue (not reject) the incoming calls and only proceed when there's an empty slot.
I can't run the web service in 64bit because some of the referenced libraries won't support that.
I also looked at libraries like https://github.com/stefanprodan/WebApiThrottle but it can only throttle based on the frequency of calls.
Thanks
You could add a piece of OwinMiddleware along these lines (influenced by the WebApiThrottle you linked to):
public class MaxConccurrentMiddleware : OwinMiddleware
{
private readonly int maxConcurrentRequests;
private int currentRequestCount;
public MaxConccurrentMiddleware(int maxConcurrentRequests)
{
this.maxConcurrentRequests = maxConcurrentRequests;
}
public override async Task Invoke(IOwinContext context)
{
try
{
if (Interlocked.Increment(ref currentRequestCount) > maxConcurrentRequests)
{
var response = context.Response;
response.OnSendingHeaders(state =>
{
var resp = (OwinResponse)state;
resp.StatusCode = 429; // 429 Too Many Requests
}, response);
return Task.FromResult(0);
}
await Next.Invoke(context);
}
finally
{
Interlocked.Decrement(ref currentRequestCount);
}
}
}

How to set http cookies (headers) in HTTP request

I'm trying to set up a WCF service hosted in IIS in ASP.Net compatibility mode that is protected via Forms Authentication and accessed via a .Net User Control in IE. (see Secure IIS hosted WCF service for access via IE hosted user control).
The User Control in IE is needed because it uses a specific third-party control for which there doesn't exist anything comparable in Silverlight or AJAX.
So I need the UserControl to set the authentication and session id cookies in the http request headers before it accesses the WCF service. My approach is to set up a Message inspector that does this.
So I've defined the Message Inspector:
public class CookieInspector : IClientMessageInspector {
public void AfterReceiveReply(ref Message reply, object correlationState) {
}
public object BeforeSendRequest(
ref Message request,
IClientChannel channel) {
HttpRequestMessageProperty messageProperty;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) {
messageProperty = (HttpRequestMessageProperty) request.Properties[
HttpRequestMessageProperty.Name
];
}
else {
messageProperty = new HttpRequestMessageProperty();
request.Properties.Add(
HttpRequestMessageProperty.Name,
messageProperty
);
}
// Set test headers for now...
messageProperty.Headers.Add(HttpRequestHeader.Cookie, "Bob=Great");
messageProperty.Headers.Add("x-chris", "Beard");
return null;
}
}
and an Endpoint behaviour:
public class CookieBehavior : IEndpointBehavior {
public void AddBindingParameters(
ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) {
}
public void ApplyClientBehavior(
ServiceEndpoint endpoint,
ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(new CookieInspector());
}
public void ApplyDispatchBehavior(
ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) {
}
public void Validate(ServiceEndpoint endpoint) {
}
}
and I configure and create my channel and WCF client in code:
var ea = new EndpointAddress("http://.../MyService.svc");
// EDIT: Http cookies can't be set with WSHttpBinding :-(
// var binding = WSHttpBinding();
var binding = new BasicHttpBinding();
// Disable automatically managed cookies (which enables user cookies)
binding.AllowCookies = false;
binding.MaxReceivedMessageSize = 5000000;
binding.ReaderQuotas.MaxStringContentLength = 5000000;
var cf = new ChannelFactory<ITranslationServices>(binding, ea);
cf.Endpoint.Behaviors.Add(new CookieBehavior());
ITranslationServices service = cf.CreateChannel();
However when I look at my request with Fiddler, the http header and cookie aren't set, and I have no clue why. I've read various articles on the Net, Stackoverflow etc that basically say that it should work, but it doesn't. Either I'm missing something obvious, or there's a bug in WCF or something else?
Well I figured it out, if I use a basicHttpBinding instead of a WSHttpBinding it works. No idea why though...
WSHttpBinding may be composed of more than one physical message to one logical message. So when successive physical calls are made, they may not be carrying the cookie appropriately

Resources