Calling ServiceStack internally with URL & JSON - c#

I have log data consisting of the original request body JSON and path it was received on. When a certain service is triggered I in effect want to run this.
Ie for each url/json object in the log I want to resolve the service and have it handle the request. The API is large and growing so I don't want to have to manually wire up each call.
Is there a way to do this internally? Ie without sending new requests out on the loopback?
Thanks,
Hector

It is possible to do using ServiceStack's BasicRequest which can be plugged into the ServiceStack request pipeline using the HostContext.ServiceController.Execute method.
Note this method does not trigger the filters or dependancy injection (You can still resolve from the container in your action method). So this is essentially the same behaviour as MQ requests. So if your requests require to use filters etc, this method of direct service execution may not be suitable for you.
What this does do:
Parses the provided URL to identify the request DTO type
Resolves the Service responsible for handling the DTO
Calls the action method passing in the DTO
Returns the result from the DTO
Use this method to process the request:
static object CallInternalService(string path, string method = "GET", string jsonData = null)
{
// Determine the request dto type based on the rest path
var restPath = HostContext.ServiceController.GetRestPathForRequest(method, path);
if(restPath == null || restPath.RequestType == null)
throw new Exception("No route matched the request"); // Note no fallbacks
// Create an instance of the dto
var dto = Activator.CreateInstance(restPath.RequestType);
if(jsonData != null)
{
var data = ServiceStack.Text.JsonSerializer.DeserializeFromString(jsonData, restPath.RequestType);
dto.PopulateWith(data);
}
// Create a basic request
var request = new BasicRequest(dto, RequestAttributes.None);
// Execute the request
return HostContext.ServiceController.Execute(dto, request);
}
So you simply pass the URL /Someaction/value and the method POST, along with the JSON data payload:
var result = CallInternalService("/users/123", "POST", "{\"Name\":\"Bob\",\"Age\":28}");
I hope that helps.

For one-way messages the Messaging API is your best option. At the moment there isn't any inter process communication option, such as, named pipes. However, I did submit a feature request for named pipes.

Related

CreateCheckStatusResponse Durable Functions attach Data

I have an Durable Azure Function and I was hoping to add data to the response. Just one field really. I cant seem to find any possible way to do it and this is where I am basically leaving off on code wise. I thought about maybe adding to the request a deliminating character but that just adds to the entirety of the string for the urls.
So my question is: Is there a way for me to append a value to the CreateCheckStatusResponse so that way I can have an Id of the object instead of polling against the url for that response?
string instanceId = await starter.StartNewAsync("SendCorrespondence", request);
var correspondenceId = Guid.NewGuid().ToString();
req.HttpContext.Items.Add("correspondenceId", correspondenceId);
req.Headers.Add("correspondenceId", correspondenceId);
var test = starter.CreateCheckStatusResponse(req, instanceId+"."+correspondenceId);
return test;
We can add the object id to the URL in the HttpStart Class and the durable client by default polls the URL in the location header if the function is in Async operation.
To add the object id, this Microsoft documentation may helps you

Nest - Search path

I am trying to use the Nest client to consume an Elasticsearch instance.
However, the /_search endpoint is in fact /search.
Is there any possible way to change the behavior of the client to reflect this change?
I have tried looking into the source code but just can't figure out a way this can be done.
Disclaimer: I have no control over the ES instance, neither I know if there is some sort of proxy in the middle that alters the /_search into /search.
Thanks in advance.
/search is not an API endpoint in Elasticsearch. It is not possible to change the API endpoints within the client, without changing the API specs from which it is generated and recompiling.
You can use the low level client's DoRequest and DoRequestAsync methods to call
a non-standard API
var client = new ElasticClient();
var request = new SearchRequest<LogMessage>
{
Query = new MatchQuery
{
Field = Infer.Field<LogMessage>(f => f.Level),
Query = "warning"
}
};
var response = client.LowLevel.DoRequest<SearchResponse<LogMessage>>(
Elasticsearch.Net.HttpMethod.POST,
"/search",
PostData.Serializable<ISearchRequest>(request)
);

Get the data sent by MailChimp API V3 Webhook

I'm trying to write a webhook for Mailchimp events using API version three and I'm struggling a bit due to their lack of a library, documentation, and basic examples, but also my lack of experience.
I know we should secure the webhook by putting a secret in the URL, that's fine. By the way, MailChimp doesn't allow configuration of basic access authentication in their portal.
They say "While we do send HTTP POST for actual data, our webhook validator will only send HTTP GET requests. You'll need to allow both in order for your webhook to function properly." Ok, I guess I can use Request.HttpMethod to return a success status code if it's a GET and process some data if it's a POST.
Not sure how to pick stuff out of the request though, and ideally don't want to write heaps of classes and properties to cover all the event types, C# being statically typed, although I guess the dynamic keyword is also an option.
Do I need to deserialise JSON? I've only written one webhook before for another API with the help of a library, you could construct an API event using either a string, stream, or textreader, which came from the request. The library made everything very simple.
For reference, there's also this question which shows how to get some data using PHP: How to pass email address to webhook from mailchimp
The data that gets posted looks like this (supposedly, there doesn't seem to be any documentation for V3):
"type": "unsubscribe",
"fired_at": "2009-03-26 21:40:57",
"data[action]": "unsub",
"data[reason]": "manual",
"data[id]": "8a25ff1d98",
"data[list_id]": "a6b5da1054",
"data[email]": "api+unsub#mailchimp.com",
"data[email_type]": "html",
"data[merges][EMAIL]": "api+unsub#mailchimp.com",
"data[merges][FNAME]": "MailChimp",
"data[merges][LNAME]": "API",
"data[merges][INTERESTS]": "Group1,Group2",
"data[ip_opt]": "10.20.10.30",
"data[campaign_id]": "cb398d21d2",
"data[reason]": "hard"
I just basically need to get this data into variables so I can sync it with my database.
Here's my (skeleton) controller so far:
[Route("mailchimp/newsletter-webhook/super-secret-key-goes-here")]
public HttpStatusCodeResult ChargeBeeWebhook()
{
return new HttpStatusCodeResult(200);
}
Assuming you've already set up your MailChimp Webhooks as described here, you can get the posted data using Request.Form syntax. Using the example posted data from the question, here's how your controller code should look like:
[AllowAnonymous]
public void ChargeBeeWebhook()
{
// type variable will be "unsubscribe"
string type = Request.Form["type"];
// action variable will be "unsub"
string action = Request.Form["data[action]"];
// reason variable will be "manual"
string reason = Request.Form["data[reason]"];
// ...
// ...
// ... do the same with the rest of the posted variables
// ...
// sync the posted data above with your database
// ...
// ...
}

Check if route is valid in RequestStartup in NancyFX

I have a self-hosted REST API in my wpf application, built using NancyFX. The application is used for updating firmware and running diagnostics on different consumer products that are connected to the computer with a USB cable.
A product must be connected to the computer in order to use the API. So I thought it would be smart to do this check in the overriden RequestStartup() method in WindsorNancyBootstrapper which means that the check can be done in one location, instead of in every module. It worked as expected. No modules will handle the request if a product isn't connected.
But this led to an unwanted side effect in the following scenario:
A product is not connected to the computer
The path is invalid
This will always return a 404 with a message saying that the device is not connected, instead of a "bad url" message. I could move the check to each module, but i'd hate doing so. What I want:
If the url is invalid, no matter if there's a connected device, always return a 404 "Bad url" response without involving any modules
if the url is valid, but there is no connected device, return a 400 "no connected device" without involving any modules
And I would like to do this in one place. I have looked around for a solution but I haven't found anything. I'm thinking that maybe my approach is a dead end. After all, I'm using the BeforeRequest pipeline which could mean that there's no way of validating the URL yet?
My method (simplified) looks like this at the moment:
protected override void RequestStartup(IWindsorContainer container, IPipelines pipelines, NancyContext context)
{
pipelines.BeforeRequest.AddItemToEndOfPipeline(ctx =>
{
// TODO: Here I would like to check if the url is valid in order to be able to return a 404 "bad url" response
if (!_hasConnectedDevice)
{
// ResponseBase is my base class for all my JSON responses
var response = new ResponseBase(ctx.Request.Url, Messages.DeviceNotConnected);
return new JsonResponse(response, new DefaultJsonSerializer())
{
StatusCode = HttpStatusCode.NotFound
};
}
if (!_deviceIsReady)
{
var response = new ResponseBase(ctx.Request.Url, Messages.DeviceNotReady);
return new JsonResponse(response, new DefaultJsonSerializer())
{
StatusCode = HttpStatusCode.BadRequest
};
}
return null;
});
// Catch all unhandled exceptions here.
pipelines.OnError += (ctx, ex) =>
{
var response = new ResponseBase(ctx.Request.Url, ex.Message);
return new JsonResponse(response, new DefaultJsonSerializer())
{
StatusCode = HttpStatusCode.InternalServerError
};
};
}
The best way I can think of to do this would be to do something similar to what we do with security where you create an extension method on INancyModule that adds something to the pipline that checks if the device is plugged in and ready and returns the relevant status code if it's not, otherwise it just returns null, then in each module you want to only work with the USB devices you add:
this.RequiresUsbThings(); // Or whatever you want to call it :)

Return Varying Type from REST Endpoint

I'm constructing a WCF REST service, with a single PUT operation. The purpose is to update an account balance by adding or subtracting funds. There are two primary responses the PUT operation can make:
200/OK. An AccountUpdate object is serialized back to the caller, which contains the new account balance information.
409/Conflict. A AccountError object is serialized back to the caller, which states the problem (usually an "account overdrawn" message).
The above is what the client expects for return values. If this was ASP.NET Web API, this service would be easy to make.
However, with WCF REST I'm not sure how I am supposed to have two different return types for the same service operation. Here is sample code:
public AccountUpdate PutSomething([Safe] Funding funding)
{
if (HandleFunds(funding)){
//**** Handle a 200 "OK" ****
return new AccountUpdate(...);
}else{
//**** Handle a 409 "Conflict" ****
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = HttpStatusCode.Conflict;
return new AccountError(...);
}
}
The above code won't compile, because AccountError is not polymorphic with AccountUpdate.
Is there a proper idiomatic way to handle this scenario with WCF REST? Am I supposed to have my service operation simply return object?
One way I can solve this problem is to do a throw new WebFaultException<AccountError>, but that seems like an awkward way to handle an otherwise normal business flow.
Thoughts?
I would handle this by incorporating a Succeeded bit and ErrorMessage string in your AccountUpdate class. This class looks like it's used solely to update the account, and not to store account data. Therefore returning an AccountUpdate object should also return the result of that update. You could even have AccountUpdate contain a "Result" class with the data you want.

Categories

Resources