【问题标题】:How to convert model into url properly in asp.net MVC?如何在 asp.net MVC 中将模型正确转换为 url?
【发布时间】:2010-06-17 12:01:42
【问题描述】:

从 SEO 的角度来看,很高兴看到 url 的格式可以解释页面上的内容 让我们看看这种情况(这只是一个例子) 我们需要显示有关某些产品的页面,并决定为该页面设置这样的 url 模板: /product/{ProductId}/{ProductCategory}/{ProductUrlName}。 并为此创建这样的模型

public class ProductUrlInfo{
   public int ProductId{get;set;}
   public string ProductCountry{get;set;}
   public string ProductUrlName{get;set;}
}

我想创建控制器方法,我传递 ProductUrlInfo 对象但不是所有必填字段。上面显示的 url 模板的经典控制器方法如下

public ActionResult Index(int ProductId, string ProductCategory, string ProductUrlName){
    return View();
}

我们需要这样称呼它 Html.ActionLink<UserController>(x=>Index(user.ProductId, user.ProductCategory, user.ProductUrlName), "See user page")

我想创建这样的控制器方法

public ActionResult Index(ProductUrlInfo productInfo){
    return View();
}

然后这样称呼它:Html.ActionLink<ProductController>(x=>Index(product), "See product page")

实际上,当我们再添加一条路由并将其指向同一个控制器方法时,我会工作,因此路由将是: /product/{productInfo} /product/{ProductId}/{ProductCategory}/{ProductUrlName} 在这种情况下,路由引擎获取我们模型的字符串方法(需要覆盖它)并且它几乎总是工作。但有时它会失败并显示像 /page/?productInfo=/Cars/Porsche911

所以我的解决方法并不总是能正常工作。 有人知道如何以这种方式使用网址吗?

编辑也许不清楚,抱歉... ProductUrlInfo 不是视图模型。那是一个仅在 url 中显示的对象。此类产品的所有对象示例

public class ProductList: List<Product>{}

public class Product{
   public int ProductId{get;set;}
   public string ProductCountry{get;set;}
   public string ProductName{get;set;}
   public string Height{get;set;}
   public float Price{get;set;}

   //data which must be rendered in the url string
   public ProductUrlInfo Key(){
      return new ProductUrlInfo(){
        ProductId = this.ProductId
        ,ProductCountry = MyConverter.EncodeForUrl(this.ProductCountry)
        ,ProductUrlName= MyConverter.EncodeForUrl(this.ProductName)
      }
   }

}

视图控件上的某处:

foreach( var pr in Model) 
  Html.RenderActionLink<ProductController>(x=>Index(pr.Key), "See product page")

控制器方法应该是这样的:

//accept our complex object as a key
public ActionResult Index(ProductUrlInfo key){
    //retrive data from database or any other stuff
    Product pr = Repository.GetProductByKey(key);
    return View(pr);
}

//accept our complex object as a key
public ActionResult Edit(ProductUrlInfo key){
    //retrive data from database or any other stuff
    Product pr = Repository.GetProductByKey(key);
    return View(pr);
}

[HttpPost]
public ActionResult Edit(Product product){
    //do update here
}

让我解释一下它是如何传递给Index(ProductUrlInfo key) 控制器方法的。 我认为我正在使用一些副作用。但首先,如果您有几个项目要传递给控制器​​,实际上只有一个项目是对象主键(即对象 ID),其他元素只是解释用户他打开了哪个页面(一些附加信息)(即对象类别和名称)。因此,将来您可能希望更改此信息(添加新字段/删除旧字段等)但是如果您有来自整个项目的该页面的链接,那么完全替换新格式的链接可能会非常痛苦。那么为什么不将一些模型传递给控制器​​,然后将其呈现为 url 呢? 我调查了是否可以执行以下步骤将自定义对象传递给控制器 1. 创建一个类,其中包含要在 url 中显示的所有必填字段(此处为 ProductUrlInfo 类)

  1. 用你实际传递的对象注册路由(在我的特殊情况下是 "/{key}" )

  2. 这里有一些魔法。在您想要的 url 格式路由(“/{id}/{Category}/{ProductUrlName}”和例如“/{id}/{ProductUrlName}”之后注册) 如果由于某种原因不知道其类别,您可能只想呈现名称

  3. 覆盖ProductUrlInfo 类中的ToString 方法,它必须呈现您想要的url 格式。即

    公共覆盖字符串 ToS​​tring()
    {
    返回 (!String.IsNullOrEmpty(Category))
    ? string.Format( "{0}/{1}/{2}", Id, Category, ProductUrlName )
    : string.Format("{0}/{1}", Id, ProductUrlName); }

    正如我所理解的那样:当路由引擎获得传递对象时,它会寻找适当的路由并找到 {key} 字符串。然后它发现传递的对象是一个复杂类型,并调用它的 ToString 方法。但它不是盲目地设置结果而不是 {key} 参数,而是将其与路由进行比较。 现在,如果您将 ProductUrlInfo 作为参数传递给 ActionLink,您将被转移到 Index(ProductUrlInfo key) 控制器方法。 当然,它把我看作是一个黑客,所以我想知道是否有人将对象传递给 GET 控制器方法,但以其他(更好)的方式?

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-2 url-routing


    【解决方案1】:

    只是一个建议。

    在我看来,在 URL 字符串中包含 userID、userCountry 和 userLogin 是不好的做法。这些似乎是用户信息而不是应用程序信息,可能看起来差别不大,但我相信有差别而且很重要。

    如果我有一个经过身份验证的用户(与登录用户不同,如果网站允许匿名用户,经过身份验证的用户可以是匿名的)然后我会在会话中保留他们的 ID、国家、登录信息在 URL 中显示该数据可能不安全,并且还因为它与 SEO 无关,除非您希望淡化网站中每个页面的重要性。虽然该站点可能会向每个用户显示相同的页面,但 SE 会看到数百个(与您的用户一样多)指向它的 URL。

    我认为您将这些信息存储在会话状态中并保持您的 URL 更加清晰并且完全专注于识别您的用户所需的操作会更简单。

    【讨论】:

    • Lazarus,谢谢你的建议,但这是一个示例模型——我首先想到的是。实际上我不以这种方式与用户合作。将其视为产品。是的,我将更改标题中的示例。
    • 您应该通过一个键来识别产品,其他任何东西都是过滤器,应该单独处理。
    • 是的,ProductId 是唯一键,所有其他字段只是用户信息。 IE。查看当前页面的地址栏。您会看到 /questions/3061430/how-to-convert-model-into-url-properly-in-asp-net-mvc 其中 3061430 是 pageId 和“how-to-convert-model-into-url-properly-in -asp-net-mvc" - 用户信息。我想做同样的事情,但将此信息包装到单独的对象中-以防万一将来我想更改 url 格式...
    【解决方案2】:

    您需要对字符串进行 urlencode。 尝试将 ProductUrlInfo 类更改为 smth。像这样:

    public class ProductUrlInfo{
    
      public int ProductId{ get; set;}
      public string ProductCountry{ get; set; }
    
      private string productUrlName;
      public string ProductUrlName
      {
          get { return HttpUtility.UrlEncode(productUrlName); }
          set { productUrlName = HttpUtility.UrlDecode(value); }
      }
    }
    

    但是,以这种方式在 ASP.NET MVC 中处理对象并不是一个好的做法。 相反,您可以使用表单

    <% using (Html.BeginForm("Index", "User", FormMethod.Post))
    { %>
    
       <%= Html.HiddenFor(model => model.ProductId) %>
       <%= Html.HiddenFor(model => model.ProductCategory) %>
       <%= Html.HiddenFor(model => model.ProductUrlName) %>
    
       <input type="submit" value="See product page" />
    
    <% } %>
    

    (提交按钮是为了简单起见,您可以使用通过js提交表单的链接代替)

    您还可以使用 HttpPost 属性创建单独的 Index 操作,以区分第一次进入索引页面和通过链接/按钮进入(如果需要)的情况:

    [HttpPost]
    public ActionResult Index(ProductUrlInfo productInfo){
      return View();
    }
    

    在这种情况下,路由不需要包含 ProductUrlInfo。

    编辑: 需要明确的是,Lazarus 基本上是正确的,对于你的情况,你不应该做你正在做的事情。一个对象必须通过一个键来识别,否则如果有人弄乱了您的 URL(在地址栏中手动),您可能会遇到不一致。例如。将类别更改为产品不属于的类别。

    【讨论】:

    • ProductUrlInfo 对象创建只是为了在 url 中显示它。它不是视图模型。实际上 page 有另一个模型。
    • 很抱歉,如果不清楚。我已经更新了试图澄清“模型”事物的原始帖子
    猜你喜欢
    • 2011-03-22
    • 2012-06-23
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    相关资源
    最近更新 更多