ASP.NET Core API-Controller geben normalerweise explizite Typen zurück (und dies standardmäßig, wenn Sie ein neues Projekt erstellen).
[Route("api/[controller]")]
public class ThingsController : Controller
{
// GET api/things
[HttpGet]
public async Task<IEnumerable<Thing>> GetAsync()
{
//...
}
// GET api/things/5
[HttpGet("{id}")]
public async Task<Thing> GetAsync(int id)
{
Thing thingFromDB = await GetThingFromDBAsync();
if(thingFromDB == null)
return null; // This returns HTTP 204
// Process thingFromDB, blah blah blah
return thing;
}
// POST api/things
[HttpPost]
public void Post([FromBody]Thing thing)
{
//..
}
//... and so on...
}
Das Problem ist, dass return null;
- es ein HTTP zurückgibt 204
: Erfolg, kein Inhalt.
Dies wird dann von vielen clientseitigen Javascript-Komponenten als Erfolg angesehen, daher gibt es Code wie:
const response = await fetch('.../api/things/5', {method: 'GET' ...});
if(response.ok)
return await response.json(); // Error, no content!
Eine Online-Suche (wie diese Frage und diese Antwort ) weist auf hilfreiche return NotFound();
Erweiterungsmethoden für den Controller hin, aber alle diese Rückgaben IActionResult
sind nicht mit meinem Task<Thing>
Rückgabetyp kompatibel . Das Designmuster sieht folgendermaßen aus:
// GET api/things/5
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id)
{
var thingFromDB = await GetThingFromDBAsync();
if (thingFromDB == null)
return NotFound();
// Process thingFromDB, blah blah blah
return Ok(thing);
}
Das funktioniert, aber um es zu verwenden, muss der Rückgabetyp von GetAsync
geändert werden Task<IActionResult>
- die explizite Typisierung geht verloren, und entweder müssen alle Rückgabetypen auf dem Controller geändert werden (dh es wird überhaupt keine explizite Typisierung verwendet), oder es wird eine Mischung geben, bei der Einige Aktionen befassen sich mit expliziten Typen, während andere. Außerdem müssen Unit-Tests jetzt Annahmen über die Serialisierung treffen und den Inhalt des Where explizit deserialisieren, IActionResult
bevor sie einen konkreten Typ hatten.
Es gibt viele Möglichkeiten, dies zu umgehen, aber es scheint ein verwirrender Mischmasch zu sein, der leicht herausgearbeitet werden kann. Die eigentliche Frage lautet also: Was ist der richtige Weg, den die ASP.NET Core-Designer beabsichtigen?
Es scheint, dass die möglichen Optionen sind:
- Haben Sie eine seltsame (chaotisch zu testende) Mischung aus expliziten Typen und
IActionResult
abhängig vom erwarteten Typ. - Vergessen Sie explizite Typen, sind sie nicht wirklich von Core MVC unterstützt, immer Gebrauch
IActionResult
(in diesem Fall , warum sind sie überhaupt vorstellen?) - Schreiben Sie eine Implementierung
HttpResponseException
und verwenden Sie es wieArgumentOutOfRangeException
(siehe diese Antwort für eine Implementierung). Dies erfordert jedoch die Verwendung von Ausnahmen für den Programmablauf, was im Allgemeinen eine schlechte Idee ist und auch vom MVC Core-Team abgelehnt wird . - Schreiben Sie eine Implementierung
HttpNoContentOutputFormatter
dieser Rückgabe404
für GET-Anforderungen. - Fehlt mir noch etwas, wie Core MVC funktionieren soll?
- Oder gibt es einen Grund, warum eine fehlgeschlagene GET-Anforderung
204
richtig und404
falsch ist?
All dies beinhaltet Kompromisse und Refactoring, die etwas verlieren oder eine scheinbar unnötige Komplexität hinzufügen, die im Widerspruch zum Design von MVC Core steht. Welcher Kompromiss ist der richtige und warum?
StatusCode(500)
und es funktioniert nur mit Aktionen, die zurückkehrenIActionResult
, auf die ich dann näher eingehen werde.