Как правильно конвертировать модель в URL в asp.net MVC? - PullRequest
1 голос
/ 17 июня 2010

С точки зрения 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} В этой ситуации механизм маршрутизации получает строковый метод нашей модели (необходимо переопределить его), и он работает ПОЧТИ всегда. Но иногда это терпит неудачу и показывает как URL / Главная /? ProductInfo = / Автомобили / Porsche 911

Так что мой обходной путь не всегда работает должным образом. Кто-нибудь знает, как работать с URL-адресами таким образом?

Редактировать Возможно, было неясно, извините ... 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)
      }
   }

}

Где-то в элементе управления View:

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) метод контроллера. Я думаю, что я использую какой-то побочный эффект. Но сначала, если у вас есть несколько элементов, которые вы хотите передать контроллеру, и фактически единственным элементом является первичный ключ объекта (т. Е. Идентификатор объекта), а другие элементы просто объясняют пользователю, какую страницу он открыл (некоторую дополнительную информацию) (т. Е. Категорию и имя объекта). ). Поэтому в будущем вы можете захотеть изменить эту информацию (добавить новые поля / удалить старые и т. Д.). Но если у вас есть ссылки на эту страницу со всего проекта, то может быть весьма болезненно полностью заменить ссылки на новый формат. Так почему бы не передать контроллеру какую-то модель, которая затем будет отображена в URL? Я исследовал, что если вы можете сделать следующие шаги для передачи пользовательского объекта в контроллер 1. создать класс, который содержит все обязательные поля, которые будут отображаться в URL (класс ProductUrlInfo здесь)

  1. зарегистрировать маршрут с фактически переданным объектом (в моем конкретном случае это " / {ключ} ")

  2. здесь немного магии. Зарегистрируйте ПОСЛЕ того, что маршруты желаемого формата URL (" / {id} / {Category} / {ProductUrlName} " и, например, " / {id} / {ProductUrlName} ") Возможно, вы захотите отобразить только имя, если по какой-то причине неизвестно его категорию

  3. переопределяет ToString метод в ProductUrlInfo классе, и он должен отображать желаемый формат URL. т.е. * +1055 *

    открытая строка переопределения ToString ()
    {
    return (! String.IsNullOrEmpty (Category))
    ? string.Format ("{0} / {1} / {2}", Id, Category, ProductUrlName)
    : string.Format ("{0} / {1}", Id, ProductUrlName); }

    Как я понимаю, этот эффект: когда механизм маршрутизации получает переданный объект, он ищет соответствующий маршрут и находит строку {key}. Затем он обнаружил, что переданный объект является сложным типом, и вызывает его метод ToString. Но это не слепо установленный результат вместо параметра {key}, но также сравнивает его с маршрутами. Теперь, если вы передадите ProductUrlInfo в ActionLink в качестве параметра, вы будете переведены в метод контроллера Index (ProductUrlInfo). Конечно, это выглядит как хак, поэтому я хотел бы знать, есть ли кто-нибудь, кто передает объекты в метод контроллера GET, но каким-то другим (лучшим) способом?

Ответы [ 2 ]

0 голосов
/ 17 июня 2010

Вам необходимо urlencode ваших строк.Попробуйте изменить класс ProductUrlInfo на что-л.как это:

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, позволяющее различать случаи, когда вы впервые заходите на страницу индекса и когда вы переходите по ссылке / кнопке (при необходимости):

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

А маршрут нев этом случае необходимо включить ProductUrlInfo.

РЕДАКТИРОВАТЬ: Просто чтобы прояснить ситуацию, Lazarus в основном подходит, и для вашего случая вы не должны делать то, что делаете.Объект должен быть идентифицирован одним ключом, в противном случае вы можете столкнуться с несогласованностью, если кто-то испортит ваш URL (вручную в адресной строке).Например, изменит категорию на то, к чему товар не относится.

0 голосов
/ 17 июня 2010

Просто предложение.

На мой взгляд, было бы плохой практикой иметь userID, userCountry и userLogin в строке URL. Похоже, что это пользовательская информация, а не информация о приложении. Может показаться, что разница невелика, но я считаю, что есть разница, и она важна.

Если у меня есть аутентифицированный пользователь (в отличие от вошедшего в систему пользователя, аутентифицированный пользователь может быть анонимным при условии, что сайт разрешает анонимных пользователей), тогда я бы держал их идентификатор, страну, регистрационную информацию в сеансе, как это было бы потенциально небезопасно раскрывать эти данные в URL-адресе, а также потому, что это не будет иметь отношения к SEO, если вы не пытаетесь ослабить важность каждой страницы сайта. Хотя сайт может представлять одну и ту же страницу каждому пользователю, SE будет видеть сотни (столько же, сколько у вас пользователей) URL-адресов, указывающих на него.

Я думаю, что вам будет гораздо проще хранить эту информацию в состоянии сеанса и сохранять ваши URL-адреса даже четкими и полностью сфокусированными на определении желаемых действий ваших пользователей.

...