Failing at API dev

One of my teams will kill me but I find its most often web/frontend devs who complain about the rest practices and understanding the response codes properly.
Dunno, front-end my side are the ones who pushed to have it proper REST error passed on, issue was more convincing the older people who had not done so before to allow it to pass code review.
Issue is more breaking flow, that people adapt to new one.
To improve the end-user experience however you define that.
1. You have a shared backend but an optimised UI so customised not responsive. Desktop for power users and mobile for light users, mobile users won’t be using keyboard shortcuts etc
2. You want to deliver a familiar UI across platforms implementing something like React.
3. You don’t need server side rendered HTML, you rather have lower latency responses so you cache at edge nodes.
Just want to add: You can run whatever front-end you like, you are no longer forced to keep your Angular 2 project going forever and moving forward with it, you can just go and build a nice Vue 2/3 app and just make a list of all the endpoints and hook them up nicely.

Want to swap the back-end? No problem, you can have the web app go and call the new service for those that have been migrated and over time migrate everything over.

Large monolith apps are scary, they are often an all or nothing approach, which can often lead to pretty bad situations where you need to constantly roll back for little things that will hold up all the improvements, and clients will complain if constantly back and forth. Change section by section, get each one right before moving on. Easier to do chunks than it is to redo all of it at once.
 
Anyone have experience with whatsapp business?
Is it possible to have people register their number and be sent information without the user having to start a chat every 24 hours?
 
Hi team. Are we still an effective team? Jokes.

I need a little advice on the MVC front end I've built for this API, completely separate project. All has gone very well so far even authorizing and authentication.

On my Edit User view. I have a dropdown for User Type which holds things like Temp, Permanent, Contractor. Below that is another text field called Rate. So depending on what their type selected is I want to do a post back type thing and display the rate.

In the UserType table is Id, Description and Rate.

How do I do this guys? It is such a common and important function of every site, changing other fields based on drop down selections and yet I cannot find an example because I don't know how to phrase the question. Please advise!
 
How do I do this guys? It is such a common and important function of every site, changing other fields based on drop down selections and yet I cannot find an example because I don't know how to phrase the question. Please advise!
JavaScript
 
Hi guys. Please could you help me out here. Somewhere in this chain I have a design problem that I cannot see. Especially with regards to when the API returns a 404 NotFound! Please advise.

It actually works when returning a record but when there is a NotFound I am not getting the result I want aka the user object is empty which means I cannot do a If(user == null) against it. I've just dropped the ball here somewhere.

API
C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);
    return result == null ? NotFound() : _mapper.Map<User>(result);
}

HttpClient (MVC Front End)
C#:
public async Task<string> Get(Uri requestUri)
{
    using (var client = new System.Net.Http.HttpClient())
    {
        var response = await client.GetAsync(requestUri);
        return response.Content.ReadAsStringAsync().Result;
    }
}

ServiceClient (MVC Front End)
C#:
public async Task<TR> Get<TR>(Uri requestUri)
{
    return JsonConvert.DeserializeObject<TR>(await Get(requestUri));
}

UserService (MVC Front End)
C#:
public async Task<User> GetUserByEmail(string email)
{
    return await _serviceClient.Get<User>(new Uri(_jwtSettings.WebServiceURL + ApiRoutes.Users.FindByEmail.Replace("{email}", email)));
}

AccountController (MVC Front End)
C#:
var result = await _userService.GetUserByEmail(model.Email);

User (MVC Front End)
C#:
public class User
{
    public string Id { get; set; }
    public int UserTypeId { get; set; }
    public string UserName { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public bool EmailConfirmed { get; set; }
    public string PhoneNumber { get; set; }
    public string ProfilePicture { get; set; }
    public decimal RemunerationRate { get; set; }
}
 
In short, how do I check against this if the record was NotFound. Do I need to wrap the response possibilities around a class which can handle all of this?
 
First: sort the code out so it's from outer to inner or inner to outer, not random.
Secondly, at what point are you getting the error, where is the user? I would have thrown an entityNotFoundException() and then on the controller side you can catch that and change that to a 404 (via NotFound()) since the 404 is a controller decision and not a repo/service decision (e.g. what happens when I want to get a list of users, that should be a 200 with no results as that is valid).
 
First: sort the code out so it's from outer to inner or inner to outer, not random.
Secondly, at what point are you getting the error, where is the user? I would have thrown an entityNotFoundException() and then on the controller side you can catch that and change that to a 404 (via NotFound()) since the 404 is a controller decision and not a repo/service decision (e.g. what happens when I want to get a list of users, that should be a 200 with no results as that is valid).

It's not random. It follows all the way up the chain from the API right up to the controller!

Also I'm not getting an error at all. What I am getting is an empty user object. It's not null it's just empty.

According to Microsoft documentation if a record is not found a NotFound must be returned. This is what I am using. I am trying to get a cleaner approach where if a record is not found then say, a user will be equal to null.
 
It's not random. It follows all the way up the chain from the API right up to the controller!

Also I'm not getting an error at all. What I am getting is an empty user object. It's not null it's just empty.

According to Microsoft documentation if a record is not found a NotFound must be returned. This is what I am using. I am trying to get a cleaner approach where if a record is not found then say, a user will be equal to null.
Sorry, saw your old post, responded to that, then saw refresh and didn't realize that your systems are separate.

So your issue is the API side? That NotFound is not triggered since is null not working or that your MVC front-end takes that 404 as a null value?
 
really difficult to follow what is going on.

what you should be doing, to keep yourself sane, is to use Postman for you API testing.

If it works as expected in Postman, then worry about the MVC side of things (because the MVC part is irrelevant. This could be a curl request, an AJAX request, a postman request, etc)

as it stands the only issue that it can be is in:

C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);

    return result == null ? NotFound() : _mapper.Map<User>(result);
}

and that _userManager.FindByEmailAsync never returns null
 
API
C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);
    return result == null ? NotFound() : _mapper.Map<User>(result);
}
Neither return type matches the method, this shouldn't be able to compile.

HttpClient (MVC Front End)
C#:
public async Task<string> Get(Uri requestUri)
{
    using (var client = new System.Net.Http.HttpClient())
    {
        var response = await client.GetAsync(requestUri);
        return response.Content.ReadAsStringAsync().Result;
    }
}
A new HttpClient each time will lead to port exhaustion, even within a using statement.
Create it once, reuse for the lifetime of the service.
Also you're in an async method, using .Result will block.

UserService (MVC Front End)
C#:
public async Task<User> GetUserByEmail(string email)
{
    var res = await _httpClient.GetAsync(new Uri(_jwtSettings.WebServiceURL + ApiRoutes.Users.FindByEmail.Replace("{email}", email)));
   if(res.IsSuccessStatusCode){
    var json = await res.Content.ReadAsStringAsync()
    if(!string.IsNullOrEmpty){
    return JsonConvert.DeserializeObject<User>(json);
   }
   }

return null;
}
Just use Refit lol
 
Neither return type matches the method, this shouldn't be able to compile.

It compiles fine. The ActionResult is as you know to return either a type of IAction or an object. Well at least the way I read it here. Also I have used mapper to map the IdentityUser to a User so as not to return everything in the IdentityUser which is not safe.

A new HttpClient each time will lead to port exhaustion, even within a using statement.
Create it once, reuse for the lifetime of the service.
Also you're in an async method, using .Result will block.

Just use Refit lol

I will look into those thanks Lion.
 
really difficult to follow what is going on.

what you should be doing, to keep yourself sane, is to use Postman for you API testing.

If it works as expected in Postman, then worry about the MVC side of things (because the MVC part is irrelevant. This could be a curl request, an AJAX request, a postman request, etc)

as it stands the only issue that it can be is in:

C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);

    return result == null ? NotFound() : _mapper.Map<User>(result);
}

and that _userManager.FindByEmailAsync never returns null

This might make it a little clearer sorry!

C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);

    if(result == null)
    {
        return NotFound();
    }

    return _mapper.Map<User>(result);
}

I have tested this as if a user is not found it does indeed return a null when a non existing email is searched.

ttest.jpg

As I said earlier on DA, I have mapped it from IdentityUser to my own User object so as to limit what is shown.
 
DA you made some good points there too. I may be making a mountain out of a mole hill here. On the API side I have stuck to Microsoft's documentation as much as possible so I'm quite happy with that. It's more on the MVC side I'm basically just "bitching" really.

But I will leave this for now the project is working fine. I am at the point now where I'm starting to niggle with small things you know, when you keep going back and going back :giggle:
 
This might make it a little clearer sorry!

C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);

    if(result == null)
    {
        return NotFound();
    }

    return _mapper.Map<User>(result);
}

I have tested this as if a user is not found it does indeed return a null when a non existing email is searched.

View attachment 1121164

As I said earlier on DA, I have mapped it from IdentityUser to my own User object so as to limit what is shown.
Cool. Then in the case of that screenshot, the API will definitely return 404 there
 
Cool. Then in the case of that screenshot, the API will definitely return 404 there

Cool great. What I'm just going to do for now is on the front end check if it is a 404 in the HttpResponseMessage. If so then I'm going to tweak the service client a little to consider that that record as null. There may be a better way but I've been sitting on this project for pretty much the whole year now and need to just put these niggly things aside for now.

In the case of any other status codes I'm just going to let the error handling middleware catch it and display a 500 page.

C#:
public async Task<T> GetAsync<T>(Uri requestUri)
{
    using (var client = new System.Net.Http.HttpClient())
    {
        var response = await client.GetAsync(requestUri);
        var jsonData = await response.Content.ReadAsStringAsync();

        if (response.StatusCode == HttpStatusCode.NotFound)
        {
            jsonData = string.Empty;
        }

        T obj = JsonConvert.DeserializeObject<T>(jsonData);

        return obj;
    }
}

C#:
public async Task<T> GetProtectedAsync<T>(Uri requestUri, AuthenticationHeaderValue authenticationHeader)
{
    using (var client = new System.Net.Http.HttpClient())
    {
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        if (authenticationHeader != null)
        {
            client.DefaultRequestHeaders.Authorization = authenticationHeader;
        }

        var response = await client.GetAsync(requestUri);
        var jsonData = await response.Content.ReadAsStringAsync();

        if (response.StatusCode == HttpStatusCode.NotFound)
        {
            jsonData = string.Empty;
        }

        T obj = JsonConvert.DeserializeObject<T>(jsonData);

        return obj;
    }
}
 
It compiles fine. The ActionResult is as you know to return either a type of IAction or an object. Well at least the way I read it here. Also I have used mapper to map the IdentityUser to a User so as not to return everything in the IdentityUser which is not safe.
Correct, my mistake. I wasn't sure what SDK or language version you're on, normally using the ternary conditional requires both types have to be the same.
Normally not possible without implicit conversion.
C#:
var foo = bar==null ? 1 : "1";
 
With regards to my earlier post at #106 I think I was just approaching this whole thing all wrong with regards to status codes.

The main issue is a lack of understanding of the response codes and types. A 404 NotFound kept giving my exception middleware on the MVC side a heart attack. So after doing a little reading I realized I likely needed to return something which still registers as a IsSuccessStatusCode = true and that is where the NotContent response comes in!

C#:
public async Task<ActionResult<User>> FindByEmail(string email)
{
    var result = await _userManager.FindByEmailAsync(email);

    if(result == null)
    {
        return NoContent();
    }

    return _mapper.Map<User>(result);
}

It all boiled down to my lack or failure to actually grasp entirely how the Status Codes work and how they are interpreted by the API consumer.


Any thoughts or feedback welcome!!
 
Last edited:
A new HttpClient each time will lead to port exhaustion, even within a using statement.
Create it once, reuse for the lifetime of the service.
Also you're in an async method, using .Result will block.

Thanks for this. I have followed this article and modified my Client accordingly!

 
Top
Sign up to the MyBroadband newsletter
X