webentwicklung-frage-antwort-db.com.de

Wie konfiguriere ich Web Api 2 so, dass in einem separaten Projekt nach Controllern gesucht wird? (genau wie früher in Web Api)

Ich habe meine Controller in ein separates Klassenbibliotheksprojekt in Mvc Web Api gestellt. Ich habe die folgende Zeile in die global.asax meines Web-API-Projekts eingefügt, um im separaten Projekt nach Controllern zu suchen:

ControllerBuilder.Current.DefaultNamespaces.Add("MyClassLibraryProject.Controllers");

Ich musste nie eine andere Konfiguration vornehmen, außer die obige Zeile hinzuzufügen. Das hat bei mir immer gut funktioniert.

Ich kann die oben beschriebene Methode jedoch nicht verwenden, um dasselbe in WebApi2 zu tun. Es funktioniert einfach nicht. Das WebApi2-Projekt versucht weiterhin, die Controller im Controller-Ordner des eigenen Projekts zu finden.

- Nach 2 Monaten gibt es ein kleines zusammenfassendes Update (als ich anfing Kopfgeld dafür zu geben):

Ich habe eine WebApiOne-Lösung erstellt, die zwei Projekte umfasst, das erste ist ein WebApi-Projekt und das zweite ist eine Klassenbibliothek für Controller. Wenn ich den Verweis auf das Controller-Klassenbibliotheksprojekt zum WebApi-Projekt hinzufüge, funktioniert alles wie erwartet. wenn ich zu http://mydevdomain.com/api/values ​​ gehe, kann ich die korrekte Ausgabe sehen.

Ich habe jetzt ein zweites Projekt mit dem Namen WebApiTwo erstellt. Es hat 2 Projekte, das erste ist ein WebApi2-Projekt und das zweite ist eine Klassenbibliothek für Controller. Wenn ich den Verweis auf das Controller-Klassenbibliotheksprojekt zum WebApi2-Projekt hinzufüge, funktioniert dies NICHT wie erwartet. dh wenn ich zu http://mydevdomain.com/api/values ​​ gehe, erhalte ich "Es wurde kein Typ gefunden, der mit dem Controller namens" values ​​"übereinstimmt. "

für das erste Projekt mache ich überhaupt keine benutzerdefinierten Einstellungen. Ich habe NICHT:

ControllerBuilder.Current.DefaultNamespaces.Add("MyClassLibraryProject.Controllers");

in meinem global.asax, und ich habe keine von StrathWeb vorgeschlagenen kundenspezifischen Lösungen in zwei seiner Blog-Posts implementiert, da ich denke, dass es nicht mehr anwendbar ist; weil alles funktioniert, indem nur die Referenz des Controller-Projekts zum WebApi-Projekt hinzugefügt wird.

Also würde ich erwarten, dass alle für WebApi2 gleich funktionieren ... aber es ist nicht so. Hat jemand wirklich versucht, dies in WebAPi2 zu tun?

49
M. Ali Iftikhar

Ich habe gerade bestätigt, dass dies gut funktioniert. Dinge zu überprüfen:

Referenzen: Verweist Ihr Haupt-Web-API-Projekt auf die externe Klassenbibliothek?

Routing: Haben Sie Routen eingerichtet, die die externen Controller stören könnten?

Schutzstufe: Sind die Controller in der externen Bibliothek public?

Inheritance: Erben die Controller in der externen Bibliothek von ApiController?

Versionierung: Verwenden sowohl Ihr Web-API-Projekt als auch Ihre Klassenbibliothek dieselbe Version der Web-API-Bibliotheken?

Wenn es hilft, kann ich meine Testlösung zusammenstellen und Ihnen zur Verfügung stellen. Beachten Sie außerdem, dass Sie Web API nicht anweisen müssen, die Controller mit der Zeile zu finden, die Sie zu Global.asax Hinzugefügt haben. Das System findet die Controller automatisch, sofern Sie sie referenziert haben.

29
DavidG

Es sollte so funktionieren wie es ist. Checkliste

  • ApiController erben
  • Beenden Sie den Controllernamen mit Controller . Z.B. ValuesController
  • Stellen Sie sicher, dass das WebApi-Projekt und das Klassenbibliotheksprojekt auf dieselben WebApi-Assemblys verweisen
  • Versuchen Sie, Routen mithilfe des Attributroutings zu erzwingen
  • Clean die Lösung, entfernen Sie bin Ordner manuell und erstellen Sie neu
  • Löschen Sie die Ordner Temporary ASP.NET Files. Ergebnis der Suche nach WebApi- und MVC-Cache-Controllern
  • Rufen Sie `config.MapHttpAttributeRoutes () auf; um sicherzustellen, dass das Framework Attributrouten berücksichtigt
  • Stellen Sie sicher, dass die von Ihnen aufgerufene Methode für die Behandlung des richtigen HTTP - Verbs ausgelegt ist (wenn es sich um eine GET - Webmethode handelt, können Sie sie über die Browser - URL aufrufen, wenn es sich um POST handelt) Webanfrage)

Dieser Controller:

[RoutePrefix("MyValues")]
public class AbcController : ApiController
{
    [HttpGet]
    [Route("Get")]
    public string Get()
    {
        return "Ok!";
    }
}

entspricht dieser URL:

http://localhost/MyValues/Get (Beachten Sie, dass die Route keinen /api/ Enthält, da dieser nicht in RoutePrefix angegeben wurde.


Controller-Lookup-Caching: Dies ist der Standard-Controller-Resolver . Sie werden im Quellcode sehen, dass es das Nachschlageergebnis zwischenspeichert.

/// <summary>
/// Returns a list of controllers available for the application.
/// </summary>
/// <returns>An <see cref="ICollection{Type}" /> of controllers.</returns>
public override ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver)
{
    HttpControllerTypeCacheSerializer serializer = new HttpControllerTypeCacheSerializer();

    // First, try reading from the cache on disk
    List<Type> matchingTypes = ReadTypesFromCache(TypeCacheName, IsControllerTypePredicate, serializer);
    if (matchingTypes != null)
    {
        return matchingTypes;
    }
...
}
16

Bin auf dasselbe Szenario gestoßen und @justmara hat mich auf den richtigen Weg gebracht. Hier ist , wie die Kraftbelastung der abhängigen Baugruppen aus @ justmaras Antwort erreicht werden kann:

1) Überschreiben Sie die DefaultAssembliesResolver-Klasse

public class MyNewAssembliesResolver : DefaultAssembliesResolver
{
    public override ICollection<Assembly> GetAssemblies()
    {

        ICollection<Assembly> baseAssemblies = base.GetAssemblies();
        List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
        var controllersAssembly = Assembly.LoadFrom(@"Path_to_Controller_DLL");
        baseAssemblies.Add(controllersAssembly);
        return baseAssemblies;

    }
}

2) Ersetzen Sie im Konfigurationsabschnitt den Standard durch die neue Implementierung

config.Services.Replace(typeof(IAssembliesResolver), new MyNewAssembliesResolver());

Ich habe diese Syntax mithilfe von Zeigern aus diesem Blog zusammengefügt:

http://www.strathweb.com/2013/08/customizing-controller-discovery-in-asp-net-web-api/

Wie bereits erwähnt, wissen Sie, ob dieses Problem auftritt, wenn Sie das Laden des Controllers durch direkten Verweis erzwingen. Eine andere Möglichkeit besteht darin, die Ergebnisse von CurrentDomain.GetAssemblies() als Beispiel zu verwenden und festzustellen, ob Ihre Assembly in der Liste enthalten ist.

Außerdem: Wenn Sie sich mit OWIN-Komponenten selbst hosten, werden Sie darauf stoßen. Beachten Sie beim Testen, dass der DefaultAssembliesResolver NICHT aktiviert wird, bis die erste WebAPI-Anforderung gesendet wurde (ich habe eine Weile gebraucht, um dies zu realisieren).

9
user1821052

Sind Sie sicher, dass die Assembly, auf die verwiesen wird, geladen wurde, BEVOR IAssembliesResolver aufgerufen wurde? Versuchen Sie, einen Dummy-Code in Ihre Anwendung einzufügen

var a = new MyClassLibraryProject.Controllers.MyClass();

in der Konfigurationsmethode (aber vergessen Sie nicht, dass der Compiler diesen Code "optimieren" und vollständig entfernen kann, wenn "a" nie verwendet wird). Ich hatte ein ähnliches Problem mit der Ladereihenfolge der Baugruppe. Beim Hochfahren wurde das Laden abhängiger Baugruppen erzwungen.

7
justmara

Sie müssen webapi/mvc anweisen, Ihre referenzierte Assembly zu laden. Sie tun dies mit dem Abschnitt compilation/assemblies in Ihrer web.config.

<compilation debug="true" targetFramework="4.5.2">
  <assemblies>
    <add Assembly="XYZ.SomeAssembly" />
  </assemblies>
</compilation>

So einfach ist das. Sie können dies mit dem von @ user1821052 vorgeschlagenen Code tun, aber diese web.config-Version hat den gleichen Effekt.

5
Ryan Mann

Abgesehen von dem, was bereits gesagt wurde:

Stellen Sie sicher, dass Sie nicht zwei Controller mit demselben Namen in unterschiedlichen Namespaces haben.

Hatte gerade den Fall, dass ein Controller (foo.UserApiController) teilweise in einen neuen Namespace (bar.UserApiController) und URI migriert werden sollte. Der alte Controller wurde gemäß Konvention auf/userapi abgebildet, der neue über RoutePrefix["api/users"]. Der neue Controller hat erst funktioniert, als ich ihn in bar.UserFooApiController umbenannt habe.

2
Igor Lankin

Bei der Verwendung von AttributeRouting ist es leicht zu vergessen, Ihre Methoden mit dem Route -Attribut zu dekorieren, insbesondere wenn Sie das RoutePrefix -Attribut für Ihre Controller-Klasse verwenden. Es scheint, dass Ihre Controller-Assembly damals nicht von der Web-API-Pipeline erfasst wurde.

1
ngu

Wenn Ihre Klassenbibliothek mit EF erstellt wurde, stellen Sie sicher, dass Sie die in App.config Für das Klassenbibliotheksprojekt angegebene Verbindungszeichenfolge haben: [ ~ # ~] und [~ # ~] im Web.config für Ihr Web-API-Projekt MVC.

0
CodeCabbie