C
C#14mo ago
kurumi

❔ REST API but it is HttpListener only available

Hello there, I got a challenge to write good scalable C# REST API app by using only .NET 7 (instead of ASP .NET) with HttpListener. So I have some questions: 1) As I understood, it will only one HttpListener instance that catch all requests on different endpoints - not dedicated HttpListener to dedicated endpoint? 2) After I catch connection, I should do httpListenerInstance.GetContextAsync() and then transfer it to ThreadPool.QueueUserWorkItem? 3) How to execute some logic on needed endpoint? I found an answer by notifying all endpoints like hey my dudes, it is new connection to /endpoint can all of you handle it and execute if needed by checking itself context by using Observer pattern. But I see it may slower my system, is it any way to notify only 1 needed endpoint class? 4) ThreadPool.QueueUserWorkItem or it is better to create custom class which will store ConcurrentQueue<HttpListenerContext> with all my connections and then run another Task which will peak and notify endpoint func (see p.2 and p.3)? Please leave good advices - I can not use sweety ASP .NET.
16 Replies
JakenVeina
JakenVeina14mo ago
1) Yes, that seems correct, assuming there's lots of overlap between the endpoints you're listening to. If not, it may be better to split them up, to reduce the amount of work you have to do to decide what to do with each request. 2) If you're going to use the Async() APIs (which you should, this will be crucial for getting the most out of the ThreadPool) then don't mix-and-match with non-Async ones. Fire off your processing to the ThreadPool with Task.Run() and keep a reference to that Task to make sure it eventually gets awaited, which will help you make sure that exceptions bubble out and get logged or otherwise handled, and that you can do a proper cleanup during shutdown. 3) I have no idea what you're trying to ask here. 4) ThreadPool.QueueUserWorkItem() is probably better, so long as it gives you a way to synchronise when the work item finishes. Don't try and second-guess the efficiency and optimization of the Framework. If you think you can do something better, you'll usually be wrong, unless you have hard data to back up how your specific scenario can be optimized better. But as stated above, use Task/Async() APIs instead.
kurumi
kurumiOP14mo ago
let me rephrase p. 3: relationship 1-to-many. Where 1 is my class that should ask to execute and many are my endpoints classes. I guess, that by notify all endpoints (even if it's doesn't match user Uri) I can solve this problem, but the fault is we are wasting server resources. So is it any solution to use MediatR to execute exactly needed endpoint func (if user is on /endpoint1 only notify class of this endpoint). p. s. the dummy idea that my friend said is store dictionary with key: endpoint Uri and value: needed executor class (but I guess it is super bad idea caz not scalable enough)
Angius
Angius14mo ago
Back in ye olde PHP days before proper frameworks were a thing, it wasn't uncommon to roll your own front router That is, you redirect all traffic to index.php and, depending on the requested URL, you call different other files You're basically looking at doing something like that Whether it'll be a dictionary, or something based on reflections and attributes, is up to you
kurumi
kurumiOP14mo ago
xd, so it is challenge that my teacher gave us: no Spring for Java and no ASP for C# harold
Angius
Angius14mo ago
And depends on the complexity of the URLs
JakenVeina
JakenVeina14mo ago
Where 1 is my class that should ask to execute
What? What does this mean? What class is asking to execute what? We've talked about building an HTTP server, with an HttpListener, it doesn't ask to execute endpoints, it listens and is told to execute endpoints I like this, BTW, this is a cool challenge not practical, but an excellent learning experience
Angius
Angius14mo ago
Basically, something akin to
var endpoints = new Dictionary<string, IEndpoint> {
["/"] = new HomeEndpoint(),
["/privacy"] = new PrivacyEndpoint(),
["/about"] = new AboutEndpoint()
};
endpoints[the_requested_url].Handle();
var endpoints = new Dictionary<string, IEndpoint> {
["/"] = new HomeEndpoint(),
["/privacy"] = new PrivacyEndpoint(),
["/about"] = new AboutEndpoint()
};
endpoints[the_requested_url].Handle();
interface IEndpoint
{
void Handle();
}
interface IEndpoint
{
void Handle();
}
kurumi
kurumiOP14mo ago
I thought that the idea is store all contexts in 1 executor function and then it will ask directly 1 endpoint func
JakenVeina
JakenVeina14mo ago
assuming your queries will not have any parameters, and assuming that dictionary remains static after its initial construction on startup, yes, that is a reasonable approach if you DO need some amount of parameter matching, I would recommend a set of Regexs or just one regex, if you can manage it
kurumi
kurumiOP14mo ago
I was thinking of that too, but imagine we have more than 10 endpoints. Not the best idea for scalable?..
JakenVeina
JakenVeina14mo ago
I don't see why again, that sounds perfectly reasonable
Angius
Angius14mo ago
M8, you want scalable you use ASP KEKW is a school project
JakenVeina
JakenVeina14mo ago
the more endpoints you have, the better a Dictionary gets except for memory footprint but that's not gonna be significant compared to the lookup gains
kurumi
kurumiOP14mo ago
huh, yeap 🙂 my perfectionism 💀 I guess I will store regexp as @V.EINA Jaken mentioned
JakenVeina
JakenVeina14mo ago
unless the HttpListener parses out the query for you I would do that for parsing the path out from the params or if you have parameters within the path
Accord
Accord14mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server