ASP.NET 核心 MVC - Ajax Post 请求 400 错误

ASP.NET Core MVC - Ajax Post Request 400 Error

在 ajax 中,如果我写:data: JSON.stringify(cat) 我得到这个错误:The JSON value could not be converted to System.Collections.Generic.IEnumerable 如果我写:data: cat 我得到这个错误:'i' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.所以,我想我在 json 上做错了什么,但是什么?

以下是类别的相关 类:

Area.Identity.Data.Category:

public class Category
    {
        //----------------------------------------------------
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        //----------------------------------------------------
        public string Name { get; set; }
 
        //----------------------------------------------------
        public ICollection<Post> Posts { get; set; }

        public Category(string name)
        {
            Name = name;
        }
    }

Models.Category:

 public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
 
        public Category(int id, string name)
        {
            Id = id;
            Name = name;
        }

    }

CategoryMapper.cs:

 public class CategoryMapper
    {
        public static Models.Category FromDatabase(Areas.Identity.Data.Category cat)
        {
            return new Models.Category
            (
                cat.Id,
                cat.Name
            );
            
        }
        
        public static Areas.Identity.Data.Category ToDatabase(Models.Category cat)
        {
            return new Areas.Identity.Data.Category
            (
                //id is database generated
                cat.Name
            );
        }
    }

CategoryRepository.cs:

public class CategoryRepository
    {
        private readonly MyContext _context;

        public CategoryRepository(MyContext context)
        {
            _context = context;
        }

        public IEnumerable<Models.Category> GetAll()
        {
            return _context.Category.Select(x => CategoryMapper.FromDatabase(x));
        }

        public void Save(Models.Category category)
        {
            _context.Category.Add(CategoryMapper.ToDatabase(category));
            _context.SaveChanges();
        }
    }

CategoryService.cs:

public class CategoryService
    {
        private CategoryRepository _catRepository;
        public CategoryService(CategoryRepository catRepository)
        {
            _catRepository = catRepository;
        }

        public IEnumerable<Models.Category> GetAll()
        {
            return _catRepository.GetAll();
        }

        public void Save(Models.Category cat)
        {
            _catRepository.Save(cat);
        }

    }

CategoryController.cs:

 public class CategoryController : Controller
    {
        
        // GET: Category
        public ActionResult Index()
        {
            return View();
        }

        // GET: Category/Details/5
        public ActionResult Details(int id)
        {
            ViewBag.ID = id;
            return View();
        }

        // GET: Category/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Category/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(IFormCollection collection)
        {
            return View();
           
        }
   }

CategoryApiController.cs:

    [Route("api/category")]
    [ApiController]
    public class CategoryApiController : ControllerBase
    {
        private CategoryService _catService;

        public CategoryApiController(CategoryService catService)
        {
            _catService = catService;
        }


        [HttpGet]
        public ActionResult<List<Models.Category>> Get()
        {
            return _catService.GetAll().ToList();
        }

        [HttpGet("{id}")] // api/category/0
        public ActionResult<Models.Category> Get(int id)
        {
            return _catService.GetAll().ToList().Find(x => x.Id == id);
        }

        [HttpPost]
        //[Route("/api/category/save")]
        public IActionResult Save([FromBody] JObject json)
        {
            Models.Category c= CategoryDto.FromJson(json);
            _catService.Save(c);
            return Ok();
        }
    }

CategoryDto.cs:

 public static class CategoryDto
    {
        public static Models.Category FromJson(JObject json)
        {
            var id = json["id"].ToObject<int>();
            var name = json["name"].ToObject<string>();

            return new Models.Category(id, name);
        }
    }

Create.cshtml:

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}

<h2>Create New Category </h2>

<table id="table">
    <thead id="table-head"></thead>
    <tbody id="table-body"></tbody>
</table>


<form class="AddCategory">
    <div class="form-group">
        <div class="form-group">
            <input required="" id="cat_input" class="form-control" />
            <span class="text-danger"></span>
        </div><!-- .form-group -->
        <div class="form-group">
            <label>Input category name:</label>
            <input type="button" value="Add new category value" class="btn btn-primary addCategory" />
        </div><!-- .form-group -->
    </div><!-- .form-group -->
</form>


<script type="text/javascript">

    'use strict';

    function createRow(JsonObj) {
        let row = document.createElement('tr');

        let cellId = document.createElement('td');
        cellId.innerHTML = JsonObj.id;

        let cellName = document.createElement('td');
        cellName.innerHTML = JsonObj.name;

        row.appendChild(cellId);
        row.appendChild(cellName);

        return row;
    }

    function createHeader(JsonObj) {
        let keys = Object.keys(JsonObj);
        let row = document.createElement('tr');
        for (let i = 0; i < keys.length; i++) {
            const item = keys[i];
            var cell = document.createElement('th');
            cell.innerHTML = item;
            row.appendChild(cell);
        }


        return row;
    }

    async function getAll() {
        return await axios.get('/api/category');
    }

    function afterGetAll(response) {
        console.log(response.data);
        document.getElementById('table-head').appendChild(createHeader(response.data[0]));
        for (let i = 0; i < response.data.length; i++) {
            const item = response.data[i];
            document.getElementById('table-body').appendChild(createRow(item));
        }

        //Add New Category
        $('.addCategory').on('click', function () {
            var cat_input = $('#cat_input').val();
            var _id = parseInt(response.data.length + 1);
            var cat = { id: _id, name: cat_input };
            console.log(cat);
            $.ajax({
                type: 'POST',
                contentType: 'application/json; charset=utf-8',
                url: '/api/category',
                dataType: 'json',
                data: JSON.stringify(cat),
                /*beforeSend: function (xhr) {
                    xhr.setRequestHeader("XSRF-TOKEN",
                        $('input:hidden[name="__RequestVerificationToken"]').val());
                },*/
                 headers: {
                        "RequestVerificationToken": '@GetAntiXsrfRequestToken()'
                    },
                success: function () {
                        console.log('ok');
                    },
                error: function () {
                        console.log('not ok');
                    }
                });
        });

    }

    $(document).ready(function () {

        getAll().then(response => afterGetAll(response));

    });

</script>

JObject 适用于ASP.NET Core 3 之前的版本,如果你想在3.0 或更高版本中使用它,你必须下载软件包:

Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.6

在 Startup.cs

中启用它
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson();
}