diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
index 502d678f0d..54fcf5afaf 100644
--- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
+++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs
@@ -125,6 +125,16 @@ public class JsonApiOptions
         /// </remarks>
         public bool EnableOperations { get; set; }
 
+        /// <summary>
+        /// Whether or not to validate model state.
+        /// </summary>
+        /// <example>
+        /// <code>
+        /// options.ValidateModelState = true;
+        /// </code>
+        /// </example>
+        public bool ValidateModelState { get; set; }
+
         [Obsolete("JsonContract resolver can now be set on SerializerSettings.")]
         public IContractResolver JsonContractResolver
         {
diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
index 760f8f8d56..f4041e97d6 100644
--- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
+++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Threading.Tasks;
+using JsonApiDotNetCore.Extensions;
 using JsonApiDotNetCore.Internal;
 using JsonApiDotNetCore.Models;
 using JsonApiDotNetCore.Services;
@@ -152,6 +153,8 @@ public virtual async Task<IActionResult> PostAsync([FromBody] T entity)
 
             if (!_jsonApiContext.Options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId))
                 return Forbidden();
+            if (_jsonApiContext.Options.ValidateModelState && !ModelState.IsValid)
+                return BadRequest(ModelState.ConvertToErrorCollection());
 
             entity = await _create.CreateAsync(entity);
 
@@ -164,6 +167,8 @@ public virtual async Task<IActionResult> PatchAsync(TId id, [FromBody] T entity)
 
             if (entity == null)
                 return UnprocessableEntity();
+            if (_jsonApiContext.Options.ValidateModelState && !ModelState.IsValid)
+                return BadRequest(ModelState.ConvertToErrorCollection());
 
             var updatedEntity = await _update.UpdateAsync(id, entity);
 
diff --git a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs
new file mode 100644
index 0000000000..8eb0fc95f7
--- /dev/null
+++ b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs
@@ -0,0 +1,24 @@
+using JsonApiDotNetCore.Internal;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.EntityFrameworkCore.Internal;
+
+namespace JsonApiDotNetCore.Extensions
+{
+    public static class ModelStateExtensions
+    {
+        public static ErrorCollection ConvertToErrorCollection(this ModelStateDictionary modelState)
+        {
+            ErrorCollection errors = new ErrorCollection();
+            foreach (var entry in modelState)
+            {
+                if (!entry.Value.Errors.Any())
+                    continue;
+                foreach (var modelError in entry.Value.Errors)
+                {
+                    errors.Errors.Add(new Error(400, entry.Key, modelError.ErrorMessage, modelError.Exception != null ? ErrorMeta.FromException(modelError.Exception) : null));
+                }
+            }
+            return errors;
+        }
+    }
+}
diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
index 75fc955402..cc337c28dc 100755
--- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
+++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <VersionPrefix>2.3.2</VersionPrefix>
+    <VersionPrefix>2.3.3</VersionPrefix>
     <TargetFrameworks>$(NetStandardVersion)</TargetFrameworks>
     <AssemblyName>JsonApiDotNetCore</AssemblyName>
     <PackageId>JsonApiDotNetCore</PackageId>
diff --git a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs
index 9c59372846..d2d3ac8319 100644
--- a/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs
+++ b/test/UnitTests/Controllers/BaseJsonApiController_Tests.cs
@@ -5,7 +5,10 @@
 using Moq;
 using Xunit;
 using System.Threading.Tasks;
+using JsonApiDotNetCore.Configuration;
 using JsonApiDotNetCore.Internal;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
 
 namespace UnitTests
 {
@@ -143,6 +146,8 @@ public async Task PatchAsync_Calls_Service()
             const int id = 0;
             var resource = new Resource();
             var serviceMock = new Mock<IUpdateService<Resource>>();
+            _jsonApiContextMock.Setup(a => a.ApplyContext<Resource>(It.IsAny<BaseJsonApiController<Resource>>())).Returns(_jsonApiContextMock.Object);
+            _jsonApiContextMock.SetupGet(a => a.Options).Returns(new JsonApiOptions());
             var controller = new BaseJsonApiController<Resource>(_jsonApiContextMock.Object, update: serviceMock.Object);
 
             // act
@@ -153,6 +158,47 @@ public async Task PatchAsync_Calls_Service()
             VerifyApplyContext();
         }
 
+        [Fact]
+        public async Task PatchAsync_ModelStateInvalid_ValidateModelStateDisbled()
+        {
+            // arrange
+            const int id = 0;
+            var resource = new Resource();
+            var serviceMock = new Mock<IUpdateService<Resource>>();
+            _jsonApiContextMock.Setup(a => a.ApplyContext<Resource>(It.IsAny<BaseJsonApiController<Resource>>())).Returns(_jsonApiContextMock.Object);
+            _jsonApiContextMock.SetupGet(a => a.Options).Returns(new JsonApiOptions { ValidateModelState = false });
+            var controller = new BaseJsonApiController<Resource>(_jsonApiContextMock.Object, update: serviceMock.Object);
+
+            // act
+            var response = await controller.PatchAsync(id, resource);
+
+            // assert
+            serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny<Resource>()), Times.Once);
+            VerifyApplyContext();
+            Assert.IsNotType<BadRequestObjectResult>(response);
+        }
+
+        [Fact]
+        public async Task PatchAsync_ModelStateInvalid_ValidateModelStateEnabled()
+        {
+            // arrange
+            const int id = 0;
+            var resource = new Resource();
+            var serviceMock = new Mock<IUpdateService<Resource>>();
+            _jsonApiContextMock.Setup(a => a.ApplyContext<Resource>(It.IsAny<BaseJsonApiController<Resource>>())).Returns(_jsonApiContextMock.Object);
+            _jsonApiContextMock.SetupGet(a => a.Options).Returns(new JsonApiOptions{ValidateModelState = true});
+            var controller = new BaseJsonApiController<Resource>(_jsonApiContextMock.Object, update: serviceMock.Object);
+            controller.ModelState.AddModelError("Id", "Failed Validation");
+
+            // act
+            var response = await controller.PatchAsync(id, resource);
+
+            // assert
+            serviceMock.Verify(m => m.UpdateAsync(id, It.IsAny<Resource>()), Times.Never);
+            Assert.IsType<BadRequestObjectResult>(response);
+            Assert.IsType<ErrorCollection>(((BadRequestObjectResult) response).Value);
+        }
+
         [Fact]
         public async Task PatchAsync_Throws_405_If_No_Service()
         {
@@ -168,6 +214,67 @@ public async Task PatchAsync_Throws_405_If_No_Service()
             Assert.Equal(405, exception.GetStatusCode());
         }
 
+        [Fact]
+        public async Task PostAsync_Calls_Service()
+        {
+            // arrange
+            var resource = new Resource();
+            var serviceMock = new Mock<ICreateService<Resource>>();
+            _jsonApiContextMock.Setup(a => a.ApplyContext<Resource>(It.IsAny<BaseJsonApiController<Resource>>())).Returns(_jsonApiContextMock.Object);
+            _jsonApiContextMock.SetupGet(a => a.Options).Returns(new JsonApiOptions());
+            var controller = new BaseJsonApiController<Resource>(_jsonApiContextMock.Object, create: serviceMock.Object);
+            serviceMock.Setup(m => m.CreateAsync(It.IsAny<Resource>())).ReturnsAsync(resource);
+            controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext {HttpContext = new DefaultHttpContext()};
+
+            // act
+            await controller.PostAsync(resource);
+
+            // assert
+            serviceMock.Verify(m => m.CreateAsync(It.IsAny<Resource>()), Times.Once);
+            VerifyApplyContext();
+        }
+
+        [Fact]
+        public async Task PostAsync_ModelStateInvalid_ValidateModelStateDisabled()
+        {
+            // arrange
+            var resource = new Resource();
+            var serviceMock = new Mock<ICreateService<Resource>>();
+            _jsonApiContextMock.Setup(a => a.ApplyContext<Resource>(It.IsAny<BaseJsonApiController<Resource>>())).Returns(_jsonApiContextMock.Object);
+            _jsonApiContextMock.SetupGet(a => a.Options).Returns(new JsonApiOptions { ValidateModelState = false });
+            var controller = new BaseJsonApiController<Resource>(_jsonApiContextMock.Object, create: serviceMock.Object);
+            serviceMock.Setup(m => m.CreateAsync(It.IsAny<Resource>())).ReturnsAsync(resource);
+            controller.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext { HttpContext = new DefaultHttpContext() };
+
+            // act
+            var response = await controller.PostAsync(resource);
+
+            // assert
+            serviceMock.Verify(m => m.CreateAsync(It.IsAny<Resource>()), Times.Once);
+            VerifyApplyContext();
+            Assert.IsNotType<BadRequestObjectResult>(response);
+        }
+
+        [Fact]
+        public async Task PostAsync_ModelStateInvalid_ValidateModelStateEnabled()
+        {
+            // arrange
+            var resource = new Resource();
+            var serviceMock = new Mock<ICreateService<Resource>>();
+            _jsonApiContextMock.Setup(a => a.ApplyContext<Resource>(It.IsAny<BaseJsonApiController<Resource>>())).Returns(_jsonApiContextMock.Object);
+            _jsonApiContextMock.SetupGet(a => a.Options).Returns(new JsonApiOptions { ValidateModelState = true });
+            var controller = new BaseJsonApiController<Resource>(_jsonApiContextMock.Object, create: serviceMock.Object);
+            controller.ModelState.AddModelError("Id", "Failed Validation");
+
+            // act
+            var response = await controller.PostAsync(resource);
+
+            // assert
+            serviceMock.Verify(m => m.CreateAsync(It.IsAny<Resource>()), Times.Never);
+            Assert.IsType<BadRequestObjectResult>(response);
+            Assert.IsType<ErrorCollection>(((BadRequestObjectResult)response).Value);
+        }
+
         [Fact]
         public async Task PatchRelationshipsAsync_Calls_Service()
         {