简体   繁体   English

路由控制器asp.net mvc

[英]Routing Controllers asp.net mvc

In our application users can create objects and give them a name. 在我们的应用程序中,用户可以创建对象并为其命名。 This data is stored in the database with a typeId. 此数据使用typeId存储在数据库中。 I have an api controller that can currently be called, with a name like 我有一个目前可以调用的api控制器,其名称类似于

api/data

This controller has the basic Get, Put, Post and Delete methods. 该控制器具有基本的Get,Put,Post和Delete方法。

Now i would like users to be able to call this controller by object type. 现在,我希望用户能够按对象类型调用此控制器。 So if they setup 3 different objects and call them company, contact and project, i would like them to be able to call the api/data controller, using these names, similar to this 因此,如果他们设置3个不同的对象并将其称为公司,联系人和项目,我希望他们能够使用这些名称来调用api /数据控制器,类似于此

api/company
api/contact
api/project

I do not know these object types unitl runtime, so i cannot code these manually. 我不知道这些对象类型是unitl运行时,所以我无法手动编码。

Now i also have other controllers that i do not want effected by this, so if i have a normal controller called page, then i still want to be able to call it by 现在,我还有其他我不希望受此影响的控制器,因此,如果我有一个称为page的普通控制器,那么我仍然希望能够通过以下方式调用它

api/page

Is there a way i can do this? 有办法可以做到吗? Almost intercept a call to a controller, see if the name is equal to a name i have in the database, and if so pass it to the data controller, else let it process as normal. 几乎拦截了对控制器的调用,看看该名称是否等于我在数据库中拥有的名称,如果是,则将该名称传递给数据控制器,否则让它按常规进行处理。

I think you should 我想你应该

HomeController: 家庭控制器:

ActionResult Index(string name)
{
// check if name exists in DB
}

I didn't see other way to do what you want even by editing routes... or maybe if you do routes with DB but well... 我什至没有看到通过编辑路由来做您想要的事情的其他方法...或者也许您用DB做路由却很好...

As far as you are using api controller, you may play with RoutePrefix and Route annotations. 就使用api控制器而言,您可以使用RoutePrefix和Route批注。 For example: 例如:

[RoutePrefix("api")]
public class DataController : ApiController
{
    [Route("company")]
    public IEnumerable<CompanyViewModel> GetCompanies() 
    {
    ....
    }
}

But this may conflict with default route definition. 但这可能与默认路由定义冲突。 If so, try to specifically define your routes for this controller: 如果是这样,请尝试专门为此控制器定义路由:

 config.Routes.MapHttpRoute(
            name: "CompanyApi",
            routeTemplate: "api/company/{id}",
            defaults: new {controller = "DataController" , id = RouteParameter.Optional}
        );

You would need to add a route for each object in your db, or rather each object not from the db, and have all others route to a default path. 您将需要为数据库中的每个对象添加一条路由,或者为数据库中的每个对象添加一条路由,并将所有其他路由都路由到默认路径。 You may want to look at using regex with your routing, if you are able to prefix the object names at all. 如果您完全可以为对象名称加上前缀,则可能要考虑在路由中使用正则表达式。

Web API routing uses fall-through logic, so start off by declaring your known routes first, then have a generic handler to catch all other requests and handle them accordingly. Web API路由使用穿透逻辑,因此首先要声明您的已知路由,然后使用通用处理程序来捕获所有其他请求并进行相应处理。 For example, use the following code when setting up your HttpConfiguration : 例如,在设置HttpConfiguration时使用以下代码:

// Define all known routes using attributes.
config.MapHttpAttributeRoutes();

// Generic route to handle all other requests
config.Routes.MapHttpRoute(
    name: "DynamicRoute",
    routeTemplate: "api/{dataType}",
    defaults: new { controller = "data" }
);

Then in your DataController you can have actions that take the dataType as a parameter: 然后,在您的DataController您可以执行将dataType作为参数的操作:

public async Task<IHttpActionResult> Get(string dataType, ...)
{  ...  }

I have managed to do what i want by using RoutePrefix attribute on controller with an extra MapRoute, with a constraint applied to it. 我已经通过在控制器上使用RoutePrefix属性和一个额外的MapRoute来完成我想要的事情,并对其施加了约束。

Here is the code, if someone else is trying to do something similar 这是代码,如果有人尝试做类似的事情

First add a routePrefix to the controller that you want to call 首先将routePrefix添加到要调用的控制器

[RoutePrefix("api/{myCustomName}")]
public MyController : ApiController { 

}

Now in my WebApiConfig file, i needed to add the following lines 现在在我的WebApiConfig文件中,我需要添加以下行

// Web API routes
config.MapHttpAttributeRoutes();

// handle calling MyController using custom name
config.Routes.MapHttpRoute(
    name: "ModuleDataApi",
    routeTemplate: "api/{myCustomName}/{id}",
    defaults: new { controller = "MyController" },
    constraints: new { moduleType = new MyCustomRouteConstraint() }
);

Now create the constraint that checks if there is a valid match. 现在创建检查是否存在有效匹配的约束。 In my code, i have populated a text file with the details if the file doesnt exist, and i will delete/refresh this when a new custom name is added. 在我的代码中,如果文本文件不存在,我将在其中填充详细信息,并且在添加新的自定义名称时,我将删除/刷新此文件。 I believe this would perform better then a database query everytime, since i would probably have about 20 name/value pairs in here at the very most. 我相信,这样做每次都会比数据库查询更好,因为我最多可能在这里大约有20个名称/值对。

So create your constraint like so 所以像这样创建约束

public class MyCustomRouteConstraint : IHttpRouteConstraint
{
    private DbContext _context;
    private static object fileLockObject = new object();

    public MyCustomRouteConstraint ()
    {
        _context = new DbContext();
    }

    public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        var appDataPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/CustomNameRouteCache.txt");

        // lock variable so no 2 or more threads can try to create file at once
        lock (fileLockObject)
        {
            var list = new List<MyRouteModel>();

            // check if our cache file exists, if not, lets create it
            if (!File.Exists(appDataPath))
            {
                using (var writer = new System.IO.StreamWriter(appDataPath))
                {
                    // get all modules
                    var names = _context.CustomNames.ToList();

                    foreach (var item in names)
                    {
                        list.Add(Mapper.Map<MyRouteModel>(item));
                    }

                    // serialize the list to the file in json format
                    var json = JsonConvert.SerializeObject(list);

                    // write to file
                    writer.Write(json);
                };
            }
            else
            {
                // open file and get json contents
                using (var reader = new System.IO.StreamReader(appDataPath))
                {
                    var json = reader.ReadToEnd();

                    list = JsonConvert.DeserializeObject<List<MyRouteModel>>(json);
                }
            }

            // lets search our list and see if this value is in our modules
            foreach (var item in list)
            {
                if (item.Name.Equals(values[parameterName].ToString()))
                {
                    return true;
                }
            }
        }

        return false;
    }
}

Now this seems to work perfectly, i can call MyController using the following paths, or any others that are created by the user at runtime 现在,这似乎可以正常工作,我可以使用以下路径或用户在运行时创建的任何其他路径来调用MyController

api/company/
api/contact/
api/project/
api/whateveryouwant/

Hope this help someone else trying to do something similar 希望这可以帮助其他尝试做类似事情的人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM