0

I am working on a MVC Web App which is calling a Web API. In my Create (POST) method, a user will enter email addresses of some users in the database. I have a check to enter the email only if the email does not already exist in the database or not. If it already exists, I want to be able to show an error message to the user "Email already exists".

I thought HttpStatusCode.NotFound, "Email exists already." would work, but I think it will only show in PostMan. How can I show it on the MVC side with like ViewBag.ErrorMessage?

API

public IHttpActionResult PostApprovedUsers(ApprovedUsers approvedUsers)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

if (!db.ApprovedUsers.Any(u => u.Email == approvedUsers.Email))
{
    db.ApprovedUsers.Add(approvedUsers);
    db.SaveChanges();
}
else
{
    return Content(HttpStatusCode.NotFound, "Email exists already.");
}
return CreatedAtRoute("DefaultApi", new { id = approvedUsers.Email }, approvedUsers);

MVC Create

public ActionResult Create([Bind(Include = "Email,FirstName,LastName")] ApprovedUsers approvedUsers)
{
    using (WebClient client = new WebClient())
    {
        token = Session.GetDataFromSession<string>("access_token");
            client.Headers.Add("authorization", "Bearer " + token);
            client.UploadValues(apiUrl, "POST", new NameValueCollection()
            {                      
                { "Email", approvedUsers.Email },
                { "FirstName",approvedUsers.FirstName },
                { "LastName",approvedUsers.LastName }
            });                
    }
    return RedirectToAction("Index");
}
10
  • 1
    You are not handling the response of API in Create action method. You need to check the status code coming in the response and set in the viewbag before returning the view. Commented May 31, 2017 at 12:55
  • @Luke - He is making the call in his Create Controller using Webclient. OP: Is there a reason you have segregated your logic this way? If you are writing both the api and the controller, there is not reason to have one controller call the other one. You should just use a common service here and call that from the create controller. Commented May 31, 2017 at 13:10
  • @AndrewHunter I took over this project which someone else created in my internship. Somehow I am supposed to finish it by next week and it is a mess. Especially I am new to ASP.Net. Commented May 31, 2017 at 14:04
  • Is this a project that has a public repo or are you allowed to make it public? There seems to be some interesting design decisions made here. Commented May 31, 2017 at 14:06
  • No, no repositories were created. I am sorry, I don't know what you mean by "Public" repo? My manager just told me to fix the errors and complete the remaining features for this API and then work on the UI of the MVC side. Commented May 31, 2017 at 14:08

2 Answers 2

1

The main problem with what you were seeing is that you were not in fact doing anything with the return value of webclient. That is ok though, since based on our discussion everything is in the same project and that would not be how you would want to do this using ASP.NET MVC and Web API.

Since these are all in the same project, we do want to combine common functionality but not within a controller. The DB service should be abstracted away into a seperate project - or class - and then injected into both controllers using some form of IoC Container. There are a couple of options available and I personally usually use Ninject for my .NET projects.

Assuming you have that functionality your MVC Controller and API controller should then both program against that abstraction. Interface based programming is how .NET works best.

With that in mind your new API controller would look something like this:

public IHttpActionResult PostApprovedUsers([Bind(Include="Email, FirstName, LastName")]ApprovedUser approvedUser)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        if (_db.UserExists(approvedUser))
        {
            return Content(HttpStatusCode.NotFound, "Email exists Already");
        }

        _db.AddUser(approvedUser);
        return CreatedAtRoute("DefaultApi", new {id = approvedUser.Email}, approvedUser);
    }

Where _db is the service that gets injected into the controller. Please not how common functionality such as checking to see if a user exists and adding a user are wrapped behind the abstraction:

public interface IMyFakeDbService
{
    IQueryable<ApprovedUser> ApprovedUsers { get; }

    int RemoveUser(ApprovedUser user);

    int RemoveUser(string email);

    bool UserExists(ApprovedUser user);

    void AddUser(ApprovedUser user);
}

This then brings us to your MVC Controller. The same principle applies; since all the common functionality is in the db service just inject that into the controller instead of calling the API controller. You will see that you can use ModelState.AddModelError here to pass information back into the view.

public ActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Create([Bind(Include = "Email, FirstName, LastName")]ApprovedUser user)
    {
        if (ModelState.IsValid)
        {
            if (!_db.UserExists(user))
            {
                _db.AddUser(user);
                return RedirectToAction("Index");
            }
            ModelState.AddModelError("","A user with that email already exists");
        }
        return View();
    }

Finally

  1. I made a public git repo with a complete project here
  2. The DB calls should be async. You are wasting server resources if not
  3. If you want to learn MVC real quick I would suggest Pro ASP.NET MVC 5 and Pro ASP.NET Web API2
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you so very much for all the help. Really appreciate it. Just wanted to ask how can I add the token code?
So you were adding the token code because the API was sitting behind some form of authorized only attribute. You would handle security for the MVC controller in that controller. The sample code you posted did not have that controller being restricted, is it suppose to be?
It looks like you already have the session security cookie set in the controller since you access it from session state 'Session.GetDataFromSession<string>("access_token");'. That means the MVC controller should already be protected and you can just call the service directly.
The controller is not really restricted, but I have to access the token for every user or something.
Well I would look at the requirements for that. For the use case you posted, it is not required. If the answer works for you would you mind marking the answer as accepted? ;)
0

I always use Ok<T>, where T is a model I defined which have 3 variables, one will identify the status (true, false) and one will be a string message, and one will have data type of List<object> which I need to receive at end point, then you can modify the error message as you want in your case instead of this

return BadRequest(ModelState);

I will replace it with something like this

return Ok<MyResponse>(new MyResponse{ Message = "Error Message Here", Status = false});

then once you get your content you can desirialize it into your object

MyResponse response = new MyResponse();
HttpResponseMessage response = await client.GetAsync(path);
// ok will return success status code, but you will depend on your status to know if it was success or error happened 
if (response.IsSuccessStatusCode)
{
    // Desirialize your result 
    response = await response.Content.ReadAsAsync<MyResponse>();
}
// then get the message, 
if(response.Status)
{
 //it was fine, return something ..
 }
else {
// Error happened, get your error message and disaply it, if you want as viewbag
ViewBag.ErrorMessage = response.Message;
// do something
}

2 Comments

@Muzner Do you have an example I can follow for reference please? I am trying to understand your example code, but I am not being able to understand fully. Where would you call return Ok<MyResponse>(new MyResponse{ Message = "Error Message Here", Status = false}); ? Do I return that instead of return RedirectToAction("Index"); ?
This is your api return, not your mvc controller, api return this when there is an error, example email not found, or model is not valid, so instead of BadRequest return the one i used, this way you can modify error message as you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.