5

I have two forms, one for logging in and one for registering. These are both on the same view and using the same model. I'm handling the form submissions with a controller.

I'm getting the following error when visiting the login page

InvalidOperationException: Incorrect Content-Type:

Full error log: https://pastebin.com/JjfDtr6q

I've looked online and cannot find anything related directly to my problem and am not experienced enough in C#/.NET Core to understand what is causing the problem.

My View

@model PastPapers.Models.LoginModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Login</title>
</head>

<body>
    <!-- Register form inside modal -->
    <div id="registerModal" class="">
        <div class="">
            <header class="">
                <span onclick="document.getElementById('registerModal').style.display = 'none'" class="">&times;</span>
                <h2>Register</h2>
            </header>
            <div class="">
                <form asp-controller="Login" asp-action="AttemptRegister" method="post">
                    <input asp-for="newUsername" type="text" placeholder="Username" />
                    <input asp-for="newEmail" type="email" placeholder="Email" />
                    <input asp-for="newPassword" type="password" placeholder="Password" />
                    <input type="submit" value="Register" />
                </form>
            </div>
        </div>
    </div>

    <!-- Login form -->
    <form asp-controller="Login" asp-action="AttemptLogin" method="post">
        <input asp-for="username" type="text" placeholder="Username" />
        <input asp-for="password" type="password" placeholder="Password" />
        <input type="submit" value="Login" />
    </form>

    <button onclick="document.getElementById('registerModal').style.display='block'" class="">Register</button>
</body>
</html>

My Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PastPapers.Models;

namespace PastPapers.Controllers
{
    public class LoginController : Controller
    {
        public IActionResult Index(LoginModel loginModel = null)
        {
            if (loginModel == null)
            {
                return View();
            } else
            {
                return View(loginModel);
            }            
        }

        [HttpPost]
        public IActionResult AttemptLogin(LoginModel loginModel = null)
        {
            if (loginModel != null)
            {
                loginModel.AttemptLogin();

                if (loginModel.loginSuccess)
                {
                    return RedirectToAction("Index", "Home", null);
                } else
                {
                    return RedirectToAction("Index", loginModel);
                }

            } else
            {
                return RedirectToAction("Index");
            }
        }


        [HttpPost]
        public IActionResult AttemptRegister(LoginModel loginModel = null)
        {
            if (loginModel != null)
            {
                loginModel.AttemptRegister();

                if (loginModel.registerSuccess)
                {
                    return RedirectToAction("Index");
                } else
                {
                    return RedirectToAction("Index", loginModel);
                }
            } else
            {
                return RedirectToAction("Index");
            }
        }
    }
}

My model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using PastPapers.Helpers;
using Microsoft.AspNetCore.Http;

namespace PastPapers.Models
{
    public class LoginModel : HttpContextAccessor
    {
        public string username { get; set; }
        public string password { get; set; }
        public bool loginSuccess { get; set; }
        public string loginErrorMessage { get; set; }

        public string newUsername { get; set; }
        public string newPassword { get; set; }
        public string newEmail { get; set; }
        public bool registerSuccess { get; set; }
        public string registerErrorMessage { get; set; }

        [JsonIgnore]
        public AppDb Db { get; set; }

        public LoginModel(AppDb db = null)
        {
            Db = db;
            loginSuccess = false;
            registerSuccess = false;
        }

        public LoginModel()
        {

        }

        public void AttemptRegister()
        {
            try
            {
                Db.Connection.Open();

                string sql = "INSERT INTO users (id, username, password, email) VALUES (DEFAULT, @username, @password, @email)";
                MySqlCommand cmd = new MySqlCommand(sql, Db.Connection);
                cmd.Prepare();
                cmd.Parameters.AddWithValue("@username", newUsername);
                cmd.Parameters.AddWithValue("@password", SecurePasswordHasher.Hash(newPassword));
                cmd.Parameters.AddWithValue("@email", newEmail);

                if (cmd.ExecuteNonQuery() == 0)
                {
                    System.Diagnostics.Debug.WriteLine("Account failed to create!");
                    registerSuccess = false;
                } else
                {
                    registerSuccess = true;
                }

            } catch (Exception ex)
            {
                registerErrorMessage = "Error connecting to database";
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }

        public void AttemptLogin()
        {
            try
            {
                Db.Connection.Open();

                string sql = "SELECT password FROM users WHERE username=@username";
                MySqlCommand cmd = new MySqlCommand(sql, Db.Connection);
                cmd.Prepare();
                cmd.Parameters.AddWithValue("@username", username);
                MySqlDataReader reader = cmd.ExecuteReader();

                if (reader.Read())
                {
                    string dbPassword = reader.GetString(0);

                    if (SecurePasswordHasher.Verify(password, dbPassword))
                    {
                        loginSuccess = true;
                        HttpContext.Session.SetString("username", username);

                    } else
                    {
                        loginSuccess = false;
                        loginErrorMessage = "Incorrect password";
                    }
                } else
                {
                    loginErrorMessage = "Unknown username";
                }

            } catch (Exception ex)
            {
                loginErrorMessage = "Error connecting to database";
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

        }
    }
}
2
  • Why is your LoginModel inheriting from HttpContextAccessor? I see where you use it, but it’s almost certainly going to be the cause of your issues. Commented Feb 7, 2018 at 19:53
  • So that I can access the HTTP variable to set username etc. What is the correct way of accessing the session variables in a model? Commented Feb 7, 2018 at 19:55

2 Answers 2

5

I'd forego the whole inheritance of HttpContextAccessor (or rather IHttpContextAccessor) as you will be losing out on the functionality provided by the concrete HttpContextAccessor. Instead, inject HttpContextAccessor into the controller, and then use the result of the manipulation on the Login model to then set the session values. Like this:

In your Startup.ConfigureServices add this line:

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

This enables the HttpContext to be injected into controller.

Then in your controller, add a constructor that accepts IHttpContextAccessor:

public LoginController(IHttpContextAccessor contextAccessor)
{
    // save to a field, like _httpContext = contextAccessor.HttpContext;
}

Then pass that HttpContext into LoginModel.AttemptLogin as a parameter. That way you still get to fulfill your requirement to access the HTTP variable, and the code is more maintainable and predictable.

There is one caveat - there is a small performance hit by adding the HttpContextAccessor to the services. But overall its better.

Sign up to request clarification or add additional context in comments.

1 Comment

According to MS documentation, NEVER save HttpContext into a field, ONLY accessor itself. learn.microsoft.com/en-us/aspnet/core/performance/…
0

Replaced inheritance of HttpContextAccessor with IHttpContextAccessor and added a public HttpContext attribute. New model below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using PastPapers.Helpers;
using Microsoft.AspNetCore.Http;

namespace PastPapers.Models
{
    public class LoginModel : IHttpContextAccessor
    {
        public string username { get; set; }
        public string password { get; set; }
        public bool loginSuccess { get; set; }
        public string loginErrorMessage { get; set; }

        public string newUsername { get; set; }
        public string newPassword { get; set; }
        public string newEmail { get; set; }
        public bool registerSuccess { get; set; }
        public string registerErrorMessage { get; set; }

        [JsonIgnore]
        public AppDb Db { get; set; }
        public HttpContext HttpContext { get; set; }

        public LoginModel(AppDb db = null)
        {
            Db = db;
            loginSuccess = false;
            registerSuccess = false;
        }

        public LoginModel()
        {

        }

        public void AttemptRegister()
        {
            try
            {
                Db.Connection.Open();

                string sql = "INSERT INTO users (id, username, password, email) VALUES (DEFAULT, @username, @password, @email)";
                MySqlCommand cmd = new MySqlCommand(sql, Db.Connection);
                cmd.Prepare();
                cmd.Parameters.AddWithValue("@username", newUsername);
                cmd.Parameters.AddWithValue("@password", SecurePasswordHasher.Hash(newPassword));
                cmd.Parameters.AddWithValue("@email", newEmail);

                if (cmd.ExecuteNonQuery() == 0)
                {
                    System.Diagnostics.Debug.WriteLine("Account failed to create!");
                    registerSuccess = false;
                } else
                {
                    registerSuccess = true;
                }

            } catch (Exception ex)
            {
                registerErrorMessage = "Error connecting to database";
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }

        public void AttemptLogin()
        {
            try
            {
                Db.Connection.Open();

                string sql = "SELECT password FROM users WHERE username=@username";
                MySqlCommand cmd = new MySqlCommand(sql, Db.Connection);
                cmd.Prepare();
                cmd.Parameters.AddWithValue("@username", username);
                MySqlDataReader reader = cmd.ExecuteReader();

                if (reader.Read())
                {
                    string dbPassword = reader.GetString(0);

                    if (SecurePasswordHasher.Verify(password, dbPassword))
                    {
                        loginSuccess = true;
                        HttpContext.Session.SetString("username", username);

                    } else
                    {
                        loginSuccess = false;
                        loginErrorMessage = "Incorrect password";
                    }
                } else
                {
                    loginErrorMessage = "Unknown username";
                }

            } catch (Exception ex)
            {
                loginErrorMessage = "Error connecting to database";
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

        }
    }
}

Comments

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.