Best practices for returning different conditional results in web api - asp.net-web-api

Suppose I have web api application using ASP.NET. In one of the actions I'm checking if I can activate a free trial period for the given user. Now from one point of view, result of this action is either successful or unsuccessful. But in case of an unsuccessful result, I'd like to inform the client why the request has been denied and then the client can decide what to do.
There are multiple if-checks in this action and each one of them can deny the incoming request. Further more, we can have different successful result conditions as well.
What is the right way to return these different conditions so the client can easily distinguish between them?
What I'm doing right now is something like this:
public IHttpActionResult ActivateFreeTrial()
{
BaseResult result = new BaseResult(); // This class has a property called Status
if(!FirstCondition)
{
// Unsuccessful
result.Status = 90;
return Ok(result)
}
if(!SecondCondition)
{
// Unsuccessful
result.Status = 91;
return Ok(result)
}
// Some more checks...
if(!SixthCondition)
{
// Successful
result.Status = 1;
return Ok(result);
}
else {
// Successful
result.Status = 2;
return Ok(result);
}
}
Client needs to know which condition generated the result so that's why I'm assigning a number to distinguish between the different conditions. So in client-side I can do something like:
CallActivateFreeTrial()
.then(function(res){
if (res.Status == 1)
{
// Successful result but with status 1
}
else if (res.Status == 2)
{
// Successful result but with status 2
}
else if (res.Status == 90)
{
// Unsuccessful result but with status 90
}
else if (res.Status == 91)
{
// Unsuccessful result but with status 91
}
}, function(error){
})
I hardly think what I'm doing right now is considered to be a good practice. Is there a better way to return different conditional results to the client?

I would like to know where you get !FirstCondition, !SecondCondition values to confirm my answer but I believe it's a bad practice. If you have a different result, then you'll change ActivateFreeTrial method. I believe a strategy or state pattern could help you. So instead of return "status code" to your client, you can return the name of a function that client should call.
Strategy pattern
http://www.dofactory.com/net/strategy-design-pattern
State pattern
http://www.dofactory.com/net/state-design-pattern

Related

Cypress returning Synchronous value within Async command?

So I think this is probably me mixing up sync/async code (Mainly because Cypress has told me so) but I have a function within a page object within Cypress that is searching for customer data. I need to use this data later on in my test case to confirm the values.
Here is my function:
searchCustomer(searchText: string) {
this.customerInput.type(searchText)
this.searchButton.click()
cy.wait('#{AliasedCustomerRequest}').then(intercept => {
const data = intercept.response.body.data
console.log('Response Data: \n')
console.log(data)
if (data.length > 0) {
{Click some drop downdowns }
return data < ----I think here is the problem
} else {
{Do other stuff }
}
})
}
and in my test case itself:
let customerData = searchAndSelectCustomerIfExist('Joe Schmoe')
//Do some stuff with customerData (Probably fill in some form fields and confirm values)
So You can see what I am trying to do, if we search and find a customer I need to store that data for my test case (so I can then run some cy.validate commands and check if the values exist/etc....)
Cypress basically told me I was wrong via the error message:
cy.then() failed because you are mixing up async and sync code.
In your callback function you invoked 1 or more cy commands but then
returned a synchronous value.
Cypress commands are asynchronous and it doesn't make sense to queue
cy commands and yet return a synchronous value.
You likely forgot to properly chain the cy commands using another
cy.then().
So obviously I am mixing up async/sync code. But since the return was within the .then() I was thinking this would work. But I assume in my test case that doesn't work since the commands run synchronously I assume?
Since you have Cypress commands inside the function, you need to return the chain and use .then() on the returned value.
Also you need to return something from the else branch that's not going to break the code that uses the method, e.g an empty array.
searchCustomer(searchText: string): Chainable<any[]> {
this.customerInput.type(searchText)
this.searchButton.click()
return cy.wait('#{AliasedCustomerRequest}').then(intercept => {
const data = intercept.response.body.data
console.log('Response Data: \n')
console.log(data)
if (data.length) {
{Click some drop downdowns }
return data
} else {
{Do other stuff }
return []
}
})
}
// using
searchCustomer('my-customer').then((data: any[]) => {
if (data.length) {
}
})
Finally "Click some drop downdowns" is asynchronous code, and you may get headaches calling that inside the search.
It would be better to do those actions after the result is passed back. That also makes your code cleaner (easier to understand) since searchCustomer() does only that, has no side effects.
you just need to add return before the cy.wait
here's a bare-bones example
it("test", () => {
function searchCustomer() {
return cy.wait(100).then(intercept => {
const data = {text: "my data"}
return data
})
}
const myCustomer = searchCustomer()
myCustomer.should("have.key", "text")
myCustomer.its("text").should("eq", "my data")
});

Synchronous Ajax Call in Angular

In my angular application, I have a scenario where i need to make looping over ajax calls. Scenario is as follows:
Based upon response from first request i need to initiate 2nd request, response from 2nd request would trigger 3rd request and so on. Wherever response does not meet certain criteria, loop needs to be broken.
Loop can go several times 10 or 20 based upon configured value.
Just want to make it synchronous. Anyone who can suggest approach to implement it ?
someList.forEach(async (value,index,arr)=> {
if(!isPrintingError)
{
let out = await this.someService.Print(someBuffer);
if(!out)
{
isPrintingError = true;
}
else {
console.log("Print successful");
}
}
}
Just have a look at Promises or async/await.
I'm not sure about how you want to do your ajax calls, and it would be great to have a small chunk of code.
But the idea is to do something like that
try {
const response1 = await this.apiCall1();
if (!response1) {
throw new Error('error1');
}
const response2 = await this.apiCall2();
if (!response2) {
throw new Error('error2');
}
// etc...
} catch (e) {
// logic in case of error
}
Also you can do it in a loop. But in order to give better help, i'll need some code
Try using RxJS Library, it will help you also in other different async stuff issues.
Using RxJS operators I'd take advantage of the Merge Operator.
More info here: RxJS Merge Operator
Thanks for your snippet. Here is how to break the loop in case of bad output
try {
someList.forEach(async (value, index, arr) => {
let output = await this.someService.Print(someBuffer);
if(!output) {
// break the loop
throw new Error('error');
} else {
console.log("Print successful");
}
}
} catch (e) {
// what to do if failed ?
}

await, how it works?

if I have something like:
var x = await retrieveData()
if (x!= nil){
do stuff
}
where retrieveData() does an http request.
The question is: Does the if condition wait for the retrieving data or not? (In a better way, does the if condition always return false or not?)
Yes, the if condition "waits".
The code only continues to execute after the Future returned from retrieveData completed.
Without async/await it would be
return retrieveData().then((x) {
if(x!= null) {
do stuff
}
})

.NET web api HttpPatch returning 403 forbidden

I have a simple resource that provides a list of translations. The get end point takes a language and returns a dictionary of translations. Any updates are going to be on just one translation, so I figured that would be appropriate to do as a patch.
In my api controller, I can make a put work just fine, but any call I make to my patch end point is giving me a 403 forbidden error and I don't understand why.
[HttpGet]
// GET api/<controller>
public Dictionary<string,string> Get(String id)
{
return TranslationSvc.GetTranslatedStrings(id);
}
[HttpPatch]
public TranslationEntry Patch(TranslationEntry data)
{//403 prevents this end point from ever executing
if (TranslationSvc.UpdateTranslation(data.Lang, "", data.Translation.Key, data.Translation.Value))
{
return data;
}
else
{
//return a 500 error;
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpPut]
public TranslationEntry Put(TranslationEntry data)
{//works, but technically a put should be the full resource which is the full collection
if (TranslationSvc.UpdateTranslation(data.Lang, "", data.Translation.Key, data.Translation.Value))
{
return data;
}
else
{
//return a 500 error;
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
I found the problem. I had forgotten that I was running against a local proxy that simulated our single sign on behaviors. That local proxy was configured to deny anything but GET and post actions basically. Sorry for the false alarm question :)

Handling more than 2 possible returned values?

When a function returns a boolean you can easily
if (task()){
// it worked!
}else{
// it failed.
}
But when it returns multiple different values it gets messier
var status = task();
if (status == 1){
// hmm
}else if (status == 2){
// hmmmmm
}else if (status == 3){
// hmmmmmmmm!
}
..is there a neater way of handling it?
Edit: In response to the answers that recommend switch statements, yes I know about those. I was asking for something neater than even that?
I can't tell what language you are using (JavaScript?) but I generally write code like this:
var result = task();
switch (result)
{
case 1:
handleStatus1();
break;
case 2:
handleStatus2();
break;
default:
handleEverythingElse();
break;
}
Depends on a language's possibilities, but I'd do something like this in JavaScript:
var handle_task_1 = function () {
// handle task 1
};
var handle_task_2 = function () {
// handle task 2
};
var tasks = {
1: handle_task_1,
2: handle_task_2,
"default": function() {},
};
tasks[task()]();
// Or, with a default action. But it may be too much for some people :)
(tasks[task()] || tasks["default"])();
Most languages have a switch statement, something like:
switch (task()) {
case 1: // do stuff
break;
case 2: // other stuff
break;
/// etc.
default: // what?!
Error("Unhandleable status code");
}
If you have many chained if commands each executing a unique block of code, you might consider using a map of simple functor classes. Ideally, the application's startup would populate that map and you can just call the actions from an instance of that map
The code would look like this
Action action = (Action) contextMap.get(task());
action.do();
This has the advantage that adding new tasks requires only defining a new class for that task and adding it to the contextMap on startup.
There are some other nice things about this approach
taskA() and taskB() can share the same contextMap and even some of the same Actions so you have less code duplication
Actions can be unit tested more easily (usually)
Sharing of code between actions will be easy without ending up with spaghetti code or complex if(status > 2 && status !=7 ) statements
And of course, interfaces, varargs and other syntactic sugar helps here.
If you're talking about an integer or other primitive return type, the best approach I know is the switch statement.
switch (status)
{
case 1:
// hmm
break;
case 2:
// hmmm
break;
}
However, if you're returning an object that defines the behavior following the method call things become much neater.
Say you have an interface ISomething and two (or more) objects that implement that interface (ASomething and BSomething). If the method's return type is ISomething, the code becomes:
ISomething result = task();
result.DoSomething();
Maybe you are looking for exceptions?
Then you don't need to clutter your code for the successful case, and for the various error cases you can throw different exceptions as necessary.

Resources