1

I wrote an asp.net core 3.0 and Reactjs web application. I have an issue when I POST to my api. It hits the method but the argument is always null.

I have debugged andI saw data in react app.

When I call the DELETE method I don't have any problem.

I am using CQRS + MediatR pattern but to simplify I removed part of Cqrs + MediatR and I added my code in ActivityConroller. Note that I used the Formik library for handling forms and also I have used Ant Design for creating a template of a web app.

Backend:

     [Route("api/[controller]")]
     public class ActivitiesController : ControllerBase
     {        
         private readonly DataContext _context;

         public Handler(DataContext context)
         {
             _context = context;
         }

         [HttpPost]
         public async Task<ActionResult> Create(Activity model)
         {
             var activity = new Activity
             {
                 Id = model.Id,
                 Title = model.Title,
                 Description = model.Description,
                 Category = model.Category,
                 Date = model.Date,
                 City = model.City,
                 Venue = model.Venue
             };

             _context.Activities.Add(activity);

             var success = await _context.SaveChangesAsync() > 0;

             if (success) return Ok();

             throw new Exception("Problem saving changes");
         }        
    }

----------
ActivityForm.tsx

import React, { useState, FormEvent } from "react";
import { Formik, FormikProps, Field } from "formik";
import { Button, Input, Form, Card, DatePicker } from "antd";
import FormItem from "antd/lib/form/FormItem";
import { IActivity } from "../../../app/models/activity";
import moment, { Moment } from "moment";
import "moment/locale/en-au";
import { v4 as uuid } from "uuid";
const { TextArea } = Input;

interface IProps {
  setEditMode: (arg: boolean) => void;
  activity: IActivity;
  createActivity: (activity: IActivity) => void;
  editActivity: (activity: IActivity) => void;
}

export const ActivityForm: React.FC<IProps> = (prop: IProps) => {
  const initialValues = () => {
    if (prop.activity) {
      return prop.activity;
    } else {
      return {
        id: "",
        title: "",
        category: "",
        description: "",
        city: "",
        date: "",
        venue: ""
      };
    }
  };
  const [activity, setActivity] = useState<IActivity>(initialValues);
  const [chooseDate, setChooseDate] = useState();
  // const defaultValue = moment().format("YYYY/MM/DD HH:mm:ss");
  const onChange = (date: Moment | null, dateString: string) => {
    let dateSelected = date;
    setChooseDate(dateSelected);
  };
  const handleInputChange = (
    event: FormEvent<HTMLInputElement | HTMLTextAreaElement>
  ) => {
    const { name, value } = event.currentTarget;
    setActivity({ ...activity, [name]: value });
  };
  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    if (activity.id.length === 0) {
      let newActivity = { ...activity, id: uuid() };
      newActivity.date = moment(chooseDate).format("YYYY-MM-DD HH:mm:ss");
      console.log(newActivity);
      prop.createActivity(newActivity);
    } else {
      prop.editActivity(activity);
    }
  };
  return (
    <div>
      <Formik
        initialValues={initialValues}
        onSubmit={(values, actions) => {

        }}
        render={() => (
          <Form style={{ marginTop: 100 }} onSubmit={handleSubmit}>
            <Field
              render={(props: IProps & FormikProps<IProps>) => (
                <Card style={{ width: 400 }}>
                  <FormItem>
                    <Input
                      value={activity.title}
                      placeholder="Title"
                      onChange={handleInputChange}
                      name="title"
                      type="text"
                    />

                    <TextArea
                      // {...props.description}
                      value={activity.description}
                      placeholder="Description"
                      onChange={handleInputChange}
                      rows={4}
                      name="description"
                    />

                    <Input
                      value={activity.category}
                      placeholder="Category"
                      onChange={handleInputChange}
                      name="category"
                      type="text"
                    />

                    <DatePicker
                      showTime={{
                        defaultValue: moment("00:00:00", "HH:mm:ss")
                      }}

                      style={{ width: 350 }}
                      onChange={() => onChange}
                      name="date"
                      format="YYYY-MM-DD HH:mm:ss"
                    />

                    <Input
                      value={activity.city}
                      placeholder="City"
                      onChange={handleInputChange}
                      name="city"
                      type="text"
                    />

                    <Input
                      value={activity.venue}
                      placeholder="Venue"
                      onChange={handleInputChange}
                      name="venue"
                      type="text"
                    />
                  </FormItem>

                  <div>
                    <Button htmlType="submit" type="primary" size="large">
                      Submit
                    </Button>
                    <Button
                      onClick={() => prop.setEditMode(false)}
                      type="ghost"
                      size="large"
                      style={{ marginLeft: 10 }}
                    >
                      Cancel
                    </Button>
                  </div>
                </Card>
              )}
            />
          </Form>
        )}
      />
    </div>
  );
};

agent.ts

import axios, { AxiosResponse } from "axios";
import { IActivity } from "../models/activity";

axios.defaults.baseURL = "http://localhost:5000/api";

const responseBody = (response: AxiosResponse) => response.data;

const requests = {
  get: (url: string) => axios.get(url).then(responseBody),
  post: (url: string, body: {}) =>
    axios.post(url, body).then(responseBody),
  put: (url: string, body: {}) => axios.put(url, body).then(responseBody),
  del: (url: string) => axios.delete(url).then(responseBody)
};

const Activities = {
  list: (): Promise<IActivity[]> => requests.get("/activities"),
  details: (id: string) => requests.get(`/activities/${id}`),
  create: (activity: IActivity) => requests.post("/activities", activity),
  update: (activity: IActivity) =>
    requests.put(`/activities/${activity.id}`, activity),
  delete: (id: string) => requests.del(`/activities/${id}`)
};

export default { Activities };

App.tsx

import React, { useEffect, useState, Fragment } from "react";
import { IActivity } from "../models/activity";
import { NavBar } from "../../features/nav/NavBar";
import { ActivityDashboard } from "../../features/activities/dashboard/ActivityDashboard";
import "./styles.css";
import agent from "../api/agent";

function App() {
  const [activities, setActivities] = useState<IActivity[]>([]);
  const [selectedActivity, setSelectedActivity] = useState<IActivity | null>(
    null
  );

  const handleSelectActivity = (id: string) => {
    setSelectedActivity(activities.filter(a => a.id === id)[0]);
    setEditMode(false);
  };
  const [submitting, setSubmitting] = useState(false);
  const handleOpenCreateForm = () => {
    setSelectedActivity(null);
    setEditMode(true);
  };


  const handleCreateActivity = (activity: IActivity) => {
    agent.Activities.create(activity)
      .then(() => {
        setActivities([...activities, activity]);
        setSelectedActivity(activity);
        setEditMode(false);
      })
      .catch(error => {
        console.log(error.response);
      });
  };

  const handleEditActivity = (activity: IActivity) => {
    setActivities([...activities.filter(a => a.id !== activity.id), activity]);
    setSelectedActivity(activity);
    setEditMode(false);
  };

  const handleDeleteActivity = (id: string) => {
    agent.Activities.delete(id)
      .then(() => {
        setActivities([...activities.filter(a => a.id !== id)]);
      })
      .catch(error => {
        console.log(error.response);
      });
  };

  const [editMode, setEditMode] = useState(false);

  useEffect(() => {
    async function fetchData() {
      agent.Activities.list().then(response => {
        let activities: IActivity[] = [];
        response.forEach(activity => {
          activity.date = activity.date.split(".")[0];
          activities.push(activity);
        });
        setActivities(activities);
      });
    }
    fetchData();
  }, []);

  return (
    <Fragment>
      <NavBar openCreateForm={handleOpenCreateForm} />

      <ActivityDashboard
        activities={activities}
        selectActivity={handleSelectActivity}
        selectedActivity={selectedActivity}
        editMode={editMode}
        setEditMode={setEditMode}
        setSelectedActivity={setSelectedActivity}
        createActivity={handleCreateActivity}
        editActivity={handleEditActivity}
        deleteActivity={handleDeleteActivity}
      />
    </Fragment>
  );
}

export default App;
6
  • Try changing this Create(Activity model) to this Create([FromBody]Activity model). Commented Dec 3, 2019 at 19:23
  • @R.Richards model is null and I got 500 (Internal Server Error) error. Commented Dec 3, 2019 at 19:28
  • btw your model should be a different type to your domain object - otherwise why not just _context.Activities.Add(model);? Commented Dec 3, 2019 at 19:30
  • Are you sending camelCase to the api? If so it could be a casing thing - have you told your backend to use camel case when deserialising? Commented Dec 3, 2019 at 19:44
  • @R.Richards [FromBody] was the correct answer. But I found my issue and. I define the type of date property string in reactjs, and I specify Date Property DateTime in asp.net core. Right now I removed date property from reactjs everything work very well, and I got rest of properties. But how to solve this issue? Commented Dec 3, 2019 at 19:59

1 Answer 1

2

when using [FromBody] , the JSON-formatted request body is handled by the JsonInputFormatter and System.Text.Json(asp.net core 3.0) , so that just correctly-formatted DateTime value for JSON will work :

enter image description here

Document link : https://learn.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support

So if your string sent from client is format like YYYY-MM-DD HH:mm:ss , that won't work . You can change the format on client side , or you can just send via query string(make sure request is formed without space) or send as part of route value .

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

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.