WebAPI Mehrere Put / Post-Parameter

153

Ich versuche, mehrere Parameter auf einem WebAPI-Controller zu veröffentlichen. Ein Parameter stammt aus der URL und der andere aus dem Text. Hier ist die URL: /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

Hier ist mein Controller-Code:

public HttpResponseMessage Put(Guid offerId, OfferPriceParameters offerPriceParameters)
{
    //What!?
    var ser = new DataContractJsonSerializer(typeof(OfferPriceParameters));
    HttpContext.Current.Request.InputStream.Position = 0;
    var what = ser.ReadObject(HttpContext.Current.Request.InputStream);

    return new HttpResponseMessage(HttpStatusCode.Created);
}

Der Inhalt des Körpers ist in JSON:

{
    "Associations":
    {
        "list": [
        {
            "FromEntityId":"276774bb-9bd9-4bbd-a7e7-6ed3d69f196f",
            "ToEntityId":"ed0d2616-f707-446b-9e40-b77b94fb7d2b",
            "Types":
            {
                "list":[
                {
                    "BillingCommitment":5,
                    "BillingCycle":5,
                    "Prices":
                    {
                        "list":[
                        {
                            "CurrencyId":"274d24c9-7d0b-40ea-a936-e800d74ead53",
                            "RecurringFee":4,
                            "SetupFee":5
                        }]
                    }
                }]
            }
        }]
    }
}

Irgendeine Idee, warum die Standardbindung nicht an das offerPriceParametersArgument meines Controllers binden kann? Es wird immer auf null gesetzt. Aber ich bin in der Lage, die Daten aus dem Körper mit dem wiederherzustellen DataContractJsonSerializer.

Ich versuche auch, das FromBodyAttribut des Arguments zu verwenden, aber es funktioniert auch nicht.

Normand Bedard
quelle

Antworten:

78
[HttpPost]
public string MyMethod([FromBody]JObject data)
{
    Customer customer = data["customerData"].ToObject<Customer>();
    Product product = data["productData"].ToObject<Product>();
    Employee employee = data["employeeData"].ToObject<Employee>();
    //... other class....
}

unter Verwendung von Referenz

using Newtonsoft.Json.Linq;

Verwenden Sie Request for JQuery Ajax

var customer = {
    "Name": "jhon",
    "Id": 1,
};
var product = {
    "Name": "table",
    "CategoryId": 5,
    "Count": 100
};
var employee = {
    "Name": "Fatih",
    "Id": 4,
};

var myData = {};
myData.customerData = customer;
myData.productData = product;
myData.employeeData = employee;

$.ajax({
    type: 'POST',
    async: true,
    dataType: "json",
    url: "Your Url",
    data: myData,
    success: function (data) {
        console.log("Response Data ↓");
        console.log(data);
    },
    error: function (err) {
        console.log(err);
    }
});
Fatih GÜRDAL
quelle
3
Tolle Lösung. Wenn es anderen noch nicht klar ist, können Sie auch .ToObject <int> (), .ToObject <decimal> (), .ToString () usw. verwenden, wenn Sie einfache, mehrere Parameter aus Ihrem Ajax-Aufruf übergeben.
secretwep
Vielen Dank, ich habe Ihre Lösung ausprobiert, indem ich meine eigene API erstellt und über Postman getestet habe. Sie funktioniert einwandfrei. Ich habe jedoch einen vierten Parameter wie var test = {"Name": "test"} hinzugefügt und ihn dem myData-Objekt hinzugefügt und es wurde erfolgreich gesendet; Gibt es überhaupt eine Möglichkeit, dies zu vermeiden und nur Originalobjekte einzuschränken?
Mlle116
@ H.Al Nein, Newtonsoft.Json kann jede Art von JSON-Daten haben, die die Bibliothek über die Übersetzung kennt. Sie können das Senden von Daten nicht verhindern. Es hängt von Ihnen ab, die eingehenden Daten zu verwenden
Fatih GÜRDAL
63

Nativ unterstützt WebAPI das Binden mehrerer POST-Parameter nicht. Wie Colin betont, gibt es eine Reihe von Einschränkungen, die in meinem Blog-Beitrag beschrieben werden er verweist.

Es gibt eine Problemumgehung, indem Sie einen benutzerdefinierten Parameterordner erstellen. Der Code dafür ist hässlich und verworren, aber ich habe Code zusammen mit einer detaillierten Erklärung in meinem Blog veröffentlicht, der hier in ein Projekt eingefügt werden kann:

Übergeben mehrerer einfacher POST-Werte an die ASP.NET-Web-API

Rick Strahl
quelle
1
Der ganze Kredit geht an Sie :) Ich habe gerade Ihre Serie auf WebAPI gelesen, als ich meine eigene Implementierung startete, als diese Frage auftauchte.
Colin Young
Danke dir! Sehr hilfreich.
Normand Bedard
2
Ab 2019 ist dies jetzt der Fall.
Max
@ John - Gibt es eine Basisversion, von der diese Funktionalität unterstützt wird? Heute keinen Erfolg.
Neil Moss
25

Wenn Attribut-Routing verwendet wird, können Sie die Attribute [FromUri] und [FromBody] verwenden.

Beispiel:

[HttpPost()]
[Route("api/products/{id:int}")]
public HttpResponseMessage AddProduct([FromUri()] int id,  [FromBody()] Product product)
{
  // Add product
}
Bryan Rayner
quelle
1
Ich habe genau die gleiche Methode angewendet. Ich muss zwei Modelle an die Aktion übergeben. Ich habe eine mit den weniger Eigenschaften über die Abfragezeichenfolge und eine andere mit dem Text übergeben. Außerdem müssen Sie das Attribut [FromBody] nicht explizit angeben
Sergey G.
1
Ich kann das nicht zum Laufen bringen. Haben Sie ein vollständigeres Beispiel?
Der Eine
Ich denke nicht, dass dies der richtige Weg ist, um Daten per POST-Methode zu senden, aber ich sehe keine andere Lösung, wenn Sie 2 Modelle per Post senden müssen.
Alexandr
Diese Antwort ist der Jam!
Leonardo Wildt
1
Ich benutze Aspnetcore und Sie müssen [FromRoute]anstelle von[FromUri]
DanielV
19

Wir haben das Json-Objekt mit der HttpPost-Methode übergeben und es in ein dynamisches Objekt analysiert. es funktioniert gut. Dies ist ein Beispielcode:

Webapi:

[HttpPost]
public string DoJson2(dynamic data)
{
   //whole:
   var c = JsonConvert.DeserializeObject<YourObjectTypeHere>(data.ToString()); 

   //or 
   var c1 = JsonConvert.DeserializeObject< ComplexObject1 >(data.c1.ToString());

   var c2 = JsonConvert.DeserializeObject< ComplexObject2 >(data.c2.ToString());

   string appName = data.AppName;
   int appInstanceID = data.AppInstanceID;
   string processGUID = data.ProcessGUID;
   int userID = data.UserID;
   string userName = data.UserName;
   var performer = JsonConvert.DeserializeObject< NextActivityPerformers >(data.NextActivityPerformers.ToString());

   ...
}

Der komplexe Objekttyp kann Objekt, Array und Wörterbuch sein.

ajaxPost:
...
Content-Type: application/json,
data: {"AppName":"SamplePrice",
       "AppInstanceID":"100",
       "ProcessGUID":"072af8c3-482a-4b1c‌​-890b-685ce2fcc75d",
       "UserID":"20",
       "UserName":"Jack",
       "NextActivityPerformers":{
           "39‌​c71004-d822-4c15-9ff2-94ca1068d745":[{
                 "UserID":10,
                 "UserName":"Smith"
           }]
       }}
...
Bes Ley
quelle
1
Wir können mehrere Parameter als ein json-Objekt formatieren, das veröffentlicht werden soll, und wir werden es später auf der Serverseite auf mehrere Objekte analysieren. Dies könnte eine andere Art zu denken sein.
Bes Ley
@EkoosticMartin, es funktioniert gut, Sie müssen den dynamischen Typ analysieren, indem Sie Folgendes verwenden: JsonConvert.DeserializeObject <YourObjectTypeHere> (data.ToString ()); Hier finden Sie ein komplexes Beispiel für Dateninhalte, das ein Array und ein Wörterbuchobjekt enthält. {"AppName": "SamplePrice", "AppInstanceID": "100", "ProcessGUID": "072af8c3-482a-4b1c-890b-685ce2fcc75d", "UserID": "20", "UserName": "Jack", " NextActivityPerformers ": {" 39c71004-d822-4c15-9ff2-94ca1068d745 ": [{" UserID ": 10," UserName ":" Smith "}]}}
Bes Ley
1
Okay, sicher, dann benutze einfach einen einzelnen String-Parameter, es gibt keinen Unterschied.
EkoostikMartin
Single bedeutet nicht einfach, json string kann mit vielen verschiedenen Objekttypen kombiniert werden. Dies ist der entscheidende Punkt und ein weiterer Weg, um Fragen zu lösen.
Bes Ley
1
Hervorragende Lösung! Danke :)
Carl R
9

Eine einfache Parameterklasse kann verwendet werden, um mehrere Parameter in einem Beitrag zu übergeben:

public class AddCustomerArgs
{
    public string First { get; set; }
    public string Last { get; set; }
}

[HttpPost]
public IHttpActionResult AddCustomer(AddCustomerArgs args)
{
    //use args...
    return Ok();
}
Greg Gum
quelle
9

Sie können mehrere POST-Parameter zulassen, indem Sie die MultiPostParameterBinding-Klasse von verwenden https://github.com/keith5000/MultiPostParameterBinding

Um es zu benutzen:

1) Laden Sie den Code in den Quellordner herunter und fügen Sie ihn Ihrem Web-API-Projekt oder einem anderen Projekt in der Lösung hinzu.

2) Verwenden Sie das Attribut [MultiPostParameters] für die Aktionsmethoden, die mehrere POST-Parameter unterstützen müssen.

[MultiPostParameters]
public string DoSomething(CustomType param1, CustomType param2, string param3) { ... }

3) Fügen Sie diese Zeile in Global.asax.cs an einer beliebigen Stelle vor dem Aufruf von GlobalConfiguration.Configure (WebApiConfig.Register) zur Application_Start-Methode hinzu :

GlobalConfiguration.Configuration.ParameterBindingRules.Insert(0, MultiPostParameterBinding.CreateBindingForMarkedParameters);

4) Lassen Sie Ihre Clients die Parameter als Eigenschaften eines Objekts übergeben. Ein Beispiel für ein JSON-Objekt für die DoSomething(param1, param2, param3)Methode ist:

{ param1:{ Text:"" }, param2:{ Text:"" }, param3:"" }

Beispiel JQuery:

$.ajax({
    data: JSON.stringify({ param1:{ Text:"" }, param2:{ Text:"" }, param3:"" }),
    url: '/MyService/DoSomething',
    contentType: "application/json", method: "POST", processData: false
})
.success(function (result) { ... });

Besuchen Sie den Link für weitere Details.

Haftungsausschluss: Ich bin direkt mit der verknüpften Ressource verbunden.

Keith
quelle
7

Schöne Frage und Kommentare - habe viel aus den Antworten hier gelernt :)

Beachten Sie als zusätzliches Beispiel, dass Sie auch Körper und Routen mischen können, z

[RoutePrefix("api/test")]
public class MyProtectedController 
{
    [Authorize]
    [Route("id/{id}")]
    public IEnumerable<object> Post(String id, [FromBody] JObject data)
    {
        /*
          id                                      = "123"
          data.GetValue("username").ToString()    = "user1"
          data.GetValue("password").ToString()    = "pass1"
         */
    }
}

So anrufen:

POST /api/test/id/123 HTTP/1.1
Host: localhost
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer x.y.z
Cache-Control: no-cache

username=user1&password=pass1


enter code here
Anthony De Souza
quelle
Ich möchte 2 komplexe Typparameter senden. Wie als [HttpPost] öffentliche Zeichenfolge UploadFile (UploadMediaFile mediaFile, Byte [] Daten), wie es geht.
Başar Kaya
2

Wie sieht Ihre routeTemplate für diesen Fall aus?

Du hast diese URL gepostet:

/offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

Damit dies funktioniert, würde ich ein Routing wie dieses in Ihrem erwarten WebApiConfig:

routeTemplate: {controller}/{offerId}/prices/

Andere Annahmen sind: - Ihr Controller wird aufgerufen OffersController. - Das JSON-Objekt, das Sie im Anforderungshauptteil übergeben, ist vom Typ OfferPriceParameters(kein abgeleiteter Typ). - Sie haben keine anderen Methoden auf dem Controller, die diese stören könnten (wenn Sie dies tun, versuchen Sie, sie auskommentieren und sehen Sie, was das passiert)

Und wie Filip erwähnte, würde es Ihren Fragen helfen, wenn Sie anfangen, einige Antworten zu akzeptieren, da "Akzeptanzrate von 0%" die Leute glauben lassen könnte, dass sie ihre Zeit verschwenden

Joanna Derks
quelle
Meine Route lautet "Angebote / {OfferId} / Preise". Dies ist die einzige Methode in meinem Controller.
Normand Bedard
2

Wenn Sie ModelBinding nicht verwenden möchten, können Sie DTOs verwenden, um dies für Sie zu tun. Erstellen Sie beispielsweise eine POST-Aktion in DataLayer, die einen komplexen Typ akzeptiert, und senden Sie Daten aus dem BusinessLayer. Sie können dies bei einem UI-> API-Aufruf tun.

Hier sind Beispiel-DTO. Weisen Sie einem Schüler einen Lehrer zu und weisen Sie dem Schüler mehrere Papiere / Themen zu.

public class StudentCurriculumDTO
 {
     public StudentTeacherMapping StudentTeacherMapping { get; set; }
     public List<Paper> Paper { get; set; }
 }    
public class StudentTeacherMapping
 {
     public Guid StudentID { get; set; }
     public Guid TeacherId { get; set; }
 }

public class Paper
 {
     public Guid PaperID { get; set; }
     public string Status { get; set; }
 }

Anschließend kann die Aktion im DataLayer wie folgt erstellt werden:

[HttpPost]
[ActionName("MyActionName")]
public async Task<IHttpActionResult> InternalName(StudentCurriculumDTO studentData)
  {
     //Do whatever.... insert the data if nothing else!
  }

So rufen Sie es vom BusinessLayer aus auf:

using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", dataof_StudentCurriculumDTO)
  {
     //Do whatever.... get response if nothing else!
  }

Dies funktioniert jetzt immer noch, wenn ich Daten von mehreren Schülern gleichzeitig senden möchte. Ändern Sie das MyActionFolgende unten. WebAPI2 muss nicht [FromBody] schreiben, standardmäßig wird der komplexe Typ [FromBody] verwendet.

public async Task<IHttpActionResult> InternalName(List<StudentCurriculumDTO> studentData)

und dann beim Aufrufen eine List<StudentCurriculumDTO>der Daten übergeben.

using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", List<dataof_StudentCurriculumDTO>)
Sandiejat
quelle
0

Parameter wie anfordern

Geben Sie hier die Bildbeschreibung ein

Web-API-Code wie sein

public class OrderItemDetailsViewModel
{
    public Order order { get; set; }
    public ItemDetails[] itemDetails { get; set; }
}

public IHttpActionResult Post(OrderItemDetailsViewModel orderInfo)
{
    Order ord = orderInfo.order;
    var ordDetails = orderInfo.itemDetails;
    return Ok();
}
Pradip Rupareliya
quelle
0

Sie können die Formulardaten als Zeichenfolge abrufen:

    protected NameValueCollection GetFormData()
    {
        string root = HttpContext.Current.Server.MapPath("~/App_Data");
        var provider = new MultipartFormDataStreamProvider(root);

        Request.Content.ReadAsMultipartAsync(provider);

        return provider.FormData;
    }

    [HttpPost]
    public void test() 
    {
        var formData = GetFormData();
        var userId = formData["userId"];

        // todo json stuff
    }

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2

Martien de Jong
quelle